<commit_before>extern crate clap;
extern crate git2;

use git2::{Repository, Remote, Error, Index, Commit};
use git2::build::CheckoutBuilder;
use clap::{Arg, App};
use std::path::Path;
use std::collections::{HashMap, HashSet};

const E_NO_GIT_REPO : i32 = 1;

fn main() {
    let options = App::new("git-submerge")
                          .version("0.1")
                          .author("Alexander Batischev <eual.jp@gmail.com>")
                          .about("Merges git submodule into the repo as if it was that way from the start")
                          .arg(Arg::with_name("SUBMODULE_DIR")
                               .help("The submodule to merge")
                               .required(true)
                               .index(1))
                          .get_matches();
    // We can safely use unwrap() here because if the option is empty, Clap would've already shown
    // the error message and aborted.
    let submodule_dir = options.value_of("SUBMODULE_DIR").unwrap();
    println!("Merging {}...", submodule_dir);

    let repo = match Repository::open(".") {
        Ok(repo) => repo,
        Err(e) => {
            eprintln!("Couldn't find Git repo in the current directory: {}", e.message());
            std::process::exit(E_NO_GIT_REPO);
        },
    };

    // 1. Add submodule as a remote
    let mut remote = add_remote(&repo, submodule_dir).expect("Couldn't add a remote");
    // 2. Fetch submodule's history
    remote.fetch(&[], None, None).expect("Couldn't fetch submodule's history");
    // 3. Find out submodule's HEAD commit id
    let submodules = repo.submodules().expect("Couldn't obtain a list of submodules");
    let submodule_path = Path::new(submodule_dir);
    // TODO: use repo.find_submodule() here
    let submodule = submodules.iter().find(|s| s.path() == submodule_path)
        .expect("Couldn't find the submodule with expected path");
    let submodule_head = submodule.head_id()
        .expect("Couldn't obtain submodule's HEAD");
    // 4. Rewrite submodule branch's history, moving everything under a single directory named
    //    after the submodule
    let mut revwalk = repo.revwalk().expect("Couldn't obtain RevWalk object for the repo");
    // TODO: make sure "topological" here means "parents are always visited before their children".
    // We need that in order to be sure that our old-to-new-ids map always contains everything we
    // need it to contain.
    revwalk.set_sorting(git2::SORT_REVERSE | git2::SORT_TOPOLOGICAL);
    // TODO: push all branches and tags, not just HEAD
    revwalk.push(submodule_head).expect("Couldn't add submodule's HEAD to RevWalk list");

    let mut old_id_to_new = HashMap::new();

    for maybe_oid in revwalk {
        match maybe_oid {
            Ok(oid) => {
                // 4.1. Extract the tree
                let commit = repo.find_commit(oid).expect(&format!("Couldn't get a commit with ID {}", oid));
                let tree = commit.tree().expect(&format!("Couldn't obtain the tree of a commit with ID {}", oid));
                let mut old_index = Index::new().expect("Couldn't create an in-memory index for commit");
                let mut new_index = Index::new().expect("Couldn't create an in-memory index");
                old_index.read_tree(&tree).expect(&format!("Couldn't read the commit {} into index", oid));
                // 4.2. Obtain the new tree, where everything from the old one is moved under
                //   a directory named after the submodule
                for entry in old_index.iter() {
                    let mut new_entry = entry;

                    // TODO: what mode, owner, mtime etc. does the newly created dir get?
                    let mut new_path = String::from(submodule_dir);
                    new_path += "/";
                    new_path += &String::from_utf8(new_entry.path).expect("Failed to convert a path to str");

                    new_entry.path = new_path.into_bytes();
                    new_index.add(&new_entry).expect("Couldn't add an entry to the index");
                }
                let tree_id = new_index.write_tree_to(&repo).expect("Couldn't write the index into a tree");
                old_id_to_new.insert(tree.id(), tree_id);
                let tree = repo.find_tree(tree_id).expect("Couldn't retrieve the tree we just created");
                // 4.3. Create new commit with the new tree
                let parents = {
                    let mut p: Vec<Commit> = Vec::new();
                    for parent_id in commit.parent_ids() {
                        let new_parent_id = old_id_to_new[&parent_id];
                        let parent = repo.find_commit(new_parent_id).expect("Couldn't find parent commit by its id");
                        p.push(parent);
                    };
                    p
                };

                let mut parents_refs: Vec<&Commit> = Vec::new();
                for i in 0 .. parents.len() {
                    parents_refs.push(&parents[i]);
                }
                let new_commit_id = repo.commit(
                    None,
                    &commit.author(),
                    &commit.committer(),
                    &commit.message().expect("Couldn't retrieve commit's message"),
                    &tree,
                    &parents_refs[..])
                    .expect("Failed to commit");
                // 4.4. Update the map with the new commit's ID
                old_id_to_new.insert(oid, new_commit_id);
            },
            Err(e) =>
                eprintln!("Error walking the submodule's history: {:?}", e),
        }
    };
    // 7. Remove submodule's remote
    repo.remote_delete(submodule_dir).expect("Couldn't remove submodule's remote");
    // 8. Run through master's history, doing two things:
    revwalk = repo.revwalk().expect("Couldn't obtain RevWalk object for the repo");
    revwalk.set_sorting(git2::SORT_REVERSE | git2::SORT_TOPOLOGICAL);
    let head = repo.head().expect("Couldn't obtain repo's HEAD");
    let head_id = head.target().expect("Couldn't resolve repo's HEAD to a commit ID");
    // TODO: push all branches and tags, not just HEAD
    revwalk.push(head_id).expect("Couldn't add repo's HEAD to RevWalk list");

    for maybe_oid in revwalk {
        match maybe_oid {
            Ok(oid) => {
                // 8.1 updating the tree to contain the relevant tree from submodule
                let commit = repo.find_commit(oid).expect(&format!("Couldn't get a commit with ID {}", oid));
                let tree = commit.tree().expect(&format!("Couldn't obtain the tree of a commit with ID {}", oid));

                let submodule_subdir = match tree.get_path(submodule_path) {
                    Ok(tree) => tree,
                    Err(e) => {
                        if e.code() == git2::ErrorCode::NotFound
                            && e.class() == git2::ErrorClass::Tree
                        {
                            // It's okay. The tree lacks the subtree corresponding to the
                            // submodule. In other words, the commit doesn't include the submodule.
                            // That's totally fine. Let's map it into itself and move on.
                            old_id_to_new.insert(oid, oid);
                            continue;
                        } else {
                            // Unexpected error; let's report it and abort the program
                            // TODO: clean things up before aborting
                            panic!("Error getting submodule's subdir from the tree: {:?}", e);
                        };
                    },
                };

                // **INVARIANT**: if we got this far, current commit contains a submodule and
                // should be rewritten

                let submodule_commit_id = submodule_subdir.id();
                let new_submodule_commit_id = old_id_to_new[&submodule_commit_id];
                let submodule_commit = repo.find_commit(new_submodule_commit_id).expect("Couldn't obtain submodule's commit");
                let subtree_id = submodule_commit.tree()
                    .and_then(|t| t.get_path(submodule_path))
                    .and_then(|te| Ok(te.id()))
                    .expect("Couldn't obtain submodule's subtree ID");

                let mut treebuilder = repo.treebuilder(Some(&tree)).expect("Couldn't create TreeBuilder");
                treebuilder.remove(submodule_path).expect("Couldn't remove submodule path from TreeBuilder");
                treebuilder.insert(submodule_path, subtree_id, 0o040000).expect("Couldn't add submodule as a subdir to TreeBuilder");
                let new_tree_id = treebuilder.write().expect("Couldn't write TreeBuilder into a Tree");
                let new_tree = repo.find_tree(new_tree_id).expect("Couldn't read back the Tree we just wrote");

                // 8.2 in commits that used to update the submodule, add a parent pointing to
                //   appropriate commit in new submodule history
                let mut parent_subtree_ids = HashSet::new();
                for parent in commit.parents() {
                    let parent_tree = parent.tree().expect("Couldn't obtain parent's tree");
                    let parent_subdir_tree_id = parent_tree.get_path(submodule_path).and_then(|x| Ok(x.id()));

                    match parent_subdir_tree_id {
                        Ok(id) => {
                            parent_subtree_ids.insert(id);
                            ()
                        },
                        Err(e) => {
                            if e.code() == git2::ErrorCode::NotFound
                                && e.class() == git2::ErrorClass::Tree
                            {
                                // It's okay; carry on.
                                continue;
                            } else {
                                panic!("Error getting submodule's subdir from the tree: {:?}", e);
                            };
                        },
                    }
                };
                // true if
                //
                // o--o--o--A--
                //             `,-C
                //  o--o--o--B-
                //
                //  or
                //
                // o--o--o--o--A--B
                //
                // false if
                //
                // o--o--o--A--
                //             `,-A
                //  o--o--o--B-
                //
                //  or
                //
                // o--o--o--A--
                //             `,-B
                //  o--o--o--B-
                let submodule_updated: bool = !parent_subtree_ids.contains(&submodule_commit_id);

                // rewrite the parents if the submodule was updated
                let parents = {
                    let mut p: Vec<Commit> = Vec::new();
                    for parent_id in commit.parent_ids() {
                        let actual_parent_id = old_id_to_new[&parent_id];
                        let parent = repo.find_commit(actual_parent_id).expect("Couldn't find parent commit by its id");
                        p.push(parent);
                    };

                    if submodule_updated {
                        p.push(submodule_commit);
                    }

                    p
                };

                let mut parents_refs: Vec<&Commit> = Vec::new();
                for i in 0 .. parents.len() {
                    parents_refs.push(&parents[i]);
                }
                let new_commit_id = repo.commit(
                    None,
                    &commit.author(),
                    &commit.committer(),
                    &commit.message().expect("Couldn't retrieve commit's message"),
                    &new_tree,
                    &parents_refs[..])
                    .expect("Failed to commit");

                old_id_to_new.insert(oid, new_commit_id);
            },
            Err(e) =>
                eprintln!("Error walking the submodule's history: {:?}", e),
        }
    };

    // It's safe to do force-reset because we checked at the beginning and the repo was clean
    let mut checkoutbuilder = CheckoutBuilder::new();
    checkoutbuilder.force();

    let updated_id = old_id_to_new[&head_id];
    let object = repo.find_object(updated_id, None).expect("Couldn't look up an object at which HEAD points");
    repo.reset(&object, git2::ResetType::Hard, Some(&mut checkoutbuilder)).expect("Couldn't run force-reset");
}

fn add_remote<'a>(repo : &'a Repository, submodule_name : &str) -> Result<Remote<'a>, Error> {
    // TODO: randomize remote's name or at least check that it doesn't exist already
    // Maybe use remote_anonymous()
    let url = String::from("./") + submodule_name;
    repo.remote(submodule_name, &url)
}
<commit_msg>Let Drop run even when returning early<commit_after>extern crate clap;
extern crate git2;

use git2::{Repository, Remote, Error, Index, Commit};
use git2::build::CheckoutBuilder;
use clap::{Arg, App};
use std::path::Path;
use std::collections::{HashMap, HashSet};

const E_NO_GIT_REPO : i32 = 1;

fn main() {
    let exit_code = real_main();
    std::process::exit(exit_code);
}

fn real_main() -> i32 {
    let options = App::new("git-submerge")
                          .version("0.1")
                          .author("Alexander Batischev <eual.jp@gmail.com>")
                          .about("Merges git submodule into the repo as if it was that way from the start")
                          .arg(Arg::with_name("SUBMODULE_DIR")
                               .help("The submodule to merge")
                               .required(true)
                               .index(1))
                          .get_matches();
    // We can safely use unwrap() here because if the option is empty, Clap would've already shown
    // the error message and aborted.
    let submodule_dir = options.value_of("SUBMODULE_DIR").unwrap();
    println!("Merging {}...", submodule_dir);

    let repo = match Repository::open(".") {
        Ok(repo) => repo,
        Err(e) => {
            eprintln!("Couldn't find Git repo in the current directory: {}", e.message());
            return E_NO_GIT_REPO;
        },
    };

    // 1. Add submodule as a remote
    let mut remote = add_remote(&repo, submodule_dir).expect("Couldn't add a remote");
    // 2. Fetch submodule's history
    remote.fetch(&[], None, None).expect("Couldn't fetch submodule's history");
    // 3. Find out submodule's HEAD commit id
    let submodules = repo.submodules().expect("Couldn't obtain a list of submodules");
    let submodule_path = Path::new(submodule_dir);
    // TODO: use repo.find_submodule() here
    let submodule = submodules.iter().find(|s| s.path() == submodule_path)
        .expect("Couldn't find the submodule with expected path");
    let submodule_head = submodule.head_id()
        .expect("Couldn't obtain submodule's HEAD");
    // 4. Rewrite submodule branch's history, moving everything under a single directory named
    //    after the submodule
    let mut revwalk = repo.revwalk().expect("Couldn't obtain RevWalk object for the repo");
    // TODO: make sure "topological" here means "parents are always visited before their children".
    // We need that in order to be sure that our old-to-new-ids map always contains everything we
    // need it to contain.
    revwalk.set_sorting(git2::SORT_REVERSE | git2::SORT_TOPOLOGICAL);
    // TODO: push all branches and tags, not just HEAD
    revwalk.push(submodule_head).expect("Couldn't add submodule's HEAD to RevWalk list");

    let mut old_id_to_new = HashMap::new();

    for maybe_oid in revwalk {
        match maybe_oid {
            Ok(oid) => {
                // 4.1. Extract the tree
                let commit = repo.find_commit(oid).expect(&format!("Couldn't get a commit with ID {}", oid));
                let tree = commit.tree().expect(&format!("Couldn't obtain the tree of a commit with ID {}", oid));
                let mut old_index = Index::new().expect("Couldn't create an in-memory index for commit");
                let mut new_index = Index::new().expect("Couldn't create an in-memory index");
                old_index.read_tree(&tree).expect(&format!("Couldn't read the commit {} into index", oid));
                // 4.2. Obtain the new tree, where everything from the old one is moved under
                //   a directory named after the submodule
                for entry in old_index.iter() {
                    let mut new_entry = entry;

                    // TODO: what mode, owner, mtime etc. does the newly created dir get?
                    let mut new_path = String::from(submodule_dir);
                    new_path += "/";
                    new_path += &String::from_utf8(new_entry.path).expect("Failed to convert a path to str");

                    new_entry.path = new_path.into_bytes();
                    new_index.add(&new_entry).expect("Couldn't add an entry to the index");
                }
                let tree_id = new_index.write_tree_to(&repo).expect("Couldn't write the index into a tree");
                old_id_to_new.insert(tree.id(), tree_id);
                let tree = repo.find_tree(tree_id).expect("Couldn't retrieve the tree we just created");
                // 4.3. Create new commit with the new tree
                let parents = {
                    let mut p: Vec<Commit> = Vec::new();
                    for parent_id in commit.parent_ids() {
                        let new_parent_id = old_id_to_new[&parent_id];
                        let parent = repo.find_commit(new_parent_id).expect("Couldn't find parent commit by its id");
                        p.push(parent);
                    };
                    p
                };

                let mut parents_refs: Vec<&Commit> = Vec::new();
                for i in 0 .. parents.len() {
                    parents_refs.push(&parents[i]);
                }
                let new_commit_id = repo.commit(
                    None,
                    &commit.author(),
                    &commit.committer(),
                    &commit.message().expect("Couldn't retrieve commit's message"),
                    &tree,
                    &parents_refs[..])
                    .expect("Failed to commit");
                // 4.4. Update the map with the new commit's ID
                old_id_to_new.insert(oid, new_commit_id);
            },
            Err(e) =>
                eprintln!("Error walking the submodule's history: {:?}", e),
        }
    };
    // 7. Remove submodule's remote
    repo.remote_delete(submodule_dir).expect("Couldn't remove submodule's remote");
    // 8. Run through master's history, doing two things:
    revwalk = repo.revwalk().expect("Couldn't obtain RevWalk object for the repo");
    revwalk.set_sorting(git2::SORT_REVERSE | git2::SORT_TOPOLOGICAL);
    let head = repo.head().expect("Couldn't obtain repo's HEAD");
    let head_id = head.target().expect("Couldn't resolve repo's HEAD to a commit ID");
    // TODO: push all branches and tags, not just HEAD
    revwalk.push(head_id).expect("Couldn't add repo's HEAD to RevWalk list");

    for maybe_oid in revwalk {
        match maybe_oid {
            Ok(oid) => {
                // 8.1 updating the tree to contain the relevant tree from submodule
                let commit = repo.find_commit(oid).expect(&format!("Couldn't get a commit with ID {}", oid));
                let tree = commit.tree().expect(&format!("Couldn't obtain the tree of a commit with ID {}", oid));

                let submodule_subdir = match tree.get_path(submodule_path) {
                    Ok(tree) => tree,
                    Err(e) => {
                        if e.code() == git2::ErrorCode::NotFound
                            && e.class() == git2::ErrorClass::Tree
                        {
                            // It's okay. The tree lacks the subtree corresponding to the
                            // submodule. In other words, the commit doesn't include the submodule.
                            // That's totally fine. Let's map it into itself and move on.
                            old_id_to_new.insert(oid, oid);
                            continue;
                        } else {
                            // Unexpected error; let's report it and abort the program
                            // TODO: clean things up before aborting
                            panic!("Error getting submodule's subdir from the tree: {:?}", e);
                        };
                    },
                };

                // **INVARIANT**: if we got this far, current commit contains a submodule and
                // should be rewritten

                let submodule_commit_id = submodule_subdir.id();
                let new_submodule_commit_id = old_id_to_new[&submodule_commit_id];
                let submodule_commit = repo.find_commit(new_submodule_commit_id).expect("Couldn't obtain submodule's commit");
                let subtree_id = submodule_commit.tree()
                    .and_then(|t| t.get_path(submodule_path))
                    .and_then(|te| Ok(te.id()))
                    .expect("Couldn't obtain submodule's subtree ID");

                let mut treebuilder = repo.treebuilder(Some(&tree)).expect("Couldn't create TreeBuilder");
                treebuilder.remove(submodule_path).expect("Couldn't remove submodule path from TreeBuilder");
                treebuilder.insert(submodule_path, subtree_id, 0o040000).expect("Couldn't add submodule as a subdir to TreeBuilder");
                let new_tree_id = treebuilder.write().expect("Couldn't write TreeBuilder into a Tree");
                let new_tree = repo.find_tree(new_tree_id).expect("Couldn't read back the Tree we just wrote");

                // 8.2 in commits that used to update the submodule, add a parent pointing to
                //   appropriate commit in new submodule history
                let mut parent_subtree_ids = HashSet::new();
                for parent in commit.parents() {
                    let parent_tree = parent.tree().expect("Couldn't obtain parent's tree");
                    let parent_subdir_tree_id = parent_tree.get_path(submodule_path).and_then(|x| Ok(x.id()));

                    match parent_subdir_tree_id {
                        Ok(id) => {
                            parent_subtree_ids.insert(id);
                            ()
                        },
                        Err(e) => {
                            if e.code() == git2::ErrorCode::NotFound
                                && e.class() == git2::ErrorClass::Tree
                            {
                                // It's okay; carry on.
                                continue;
                            } else {
                                panic!("Error getting submodule's subdir from the tree: {:?}", e);
                            };
                        },
                    }
                };
                // true if
                //
                // o--o--o--A--
                //             `,-C
                //  o--o--o--B-
                //
                //  or
                //
                // o--o--o--o--A--B
                //
                // false if
                //
                // o--o--o--A--
                //             `,-A
                //  o--o--o--B-
                //
                //  or
                //
                // o--o--o--A--
                //             `,-B
                //  o--o--o--B-
                let submodule_updated: bool = !parent_subtree_ids.contains(&submodule_commit_id);

                // rewrite the parents if the submodule was updated
                let parents = {
                    let mut p: Vec<Commit> = Vec::new();
                    for parent_id in commit.parent_ids() {
                        let actual_parent_id = old_id_to_new[&parent_id];
                        let parent = repo.find_commit(actual_parent_id).expect("Couldn't find parent commit by its id");
                        p.push(parent);
                    };

                    if submodule_updated {
                        p.push(submodule_commit);
                    }

                    p
                };

                let mut parents_refs: Vec<&Commit> = Vec::new();
                for i in 0 .. parents.len() {
                    parents_refs.push(&parents[i]);
                }
                let new_commit_id = repo.commit(
                    None,
                    &commit.author(),
                    &commit.committer(),
                    &commit.message().expect("Couldn't retrieve commit's message"),
                    &new_tree,
                    &parents_refs[..])
                    .expect("Failed to commit");

                old_id_to_new.insert(oid, new_commit_id);
            },
            Err(e) =>
                eprintln!("Error walking the submodule's history: {:?}", e),
        }
    };

    // It's safe to do force-reset because we checked at the beginning and the repo was clean
    let mut checkoutbuilder = CheckoutBuilder::new();
    checkoutbuilder.force();

    let updated_id = old_id_to_new[&head_id];
    let object = repo.find_object(updated_id, None).expect("Couldn't look up an object at which HEAD points");
    repo.reset(&object, git2::ResetType::Hard, Some(&mut checkoutbuilder)).expect("Couldn't run force-reset");

    0 // An exit code indicating success
}

fn add_remote<'a>(repo : &'a Repository, submodule_name : &str) -> Result<Remote<'a>, Error> {
    // TODO: randomize remote's name or at least check that it doesn't exist already
    // Maybe use remote_anonymous()
    let url = String::from("./") + submodule_name;
    repo.remote(submodule_name, &url)
}
<|endoftext|>


<commit_before>extern crate docopt;
extern crate i2cdev;
#[macro_use]
extern crate serde_derive;
extern crate bme280_rs;

use docopt::Docopt;

use std::{thread, time};

#[cfg(target_os = "linux")]
use i2cdev::linux::*;

use bme280_rs::*;

const USAGE: &'static str = "
Reading BME280 sensor value

Usage:
  bme280 <device> [--address <addr>] [--temperature] [--humidity] [--pressure]
  bme280 (-h | --help)
  bme280 (-v | --version)

Options:
  -h --help    Show this help text.
  --address <addr>     I2C device address [default: 119] (=0x77)
  --temperature    Show temperature in degree Celsius.
  --humidity    Show humidity in %.
  --pressure    Show pressure in hPa.
  -v --version    Show version.
";

const BME280_DEFAULT_ADDRESS: u16 = 0x76;

#[derive(Debug, Deserialize)]
struct Args {
    arg_device: String,
    flag_address: Option<u16>,
    flag_version: bool,
    flag_temperature: bool,
    flag_humidity: bool,
    flag_pressure: bool,
}

#[cfg(not(target_os = "linux"))]
fn main() {
    println!("This program can run only on Linux")
}

#[cfg(target_os = "linux")]
fn main() {
    let args: Args = Docopt::new(USAGE).and_then(|d| d.deserialize()).unwrap_or_else(|e| e.exit());

    if args.flag_version {
        println!("bme280 {}", env!("CARGO_PKG_VERSION"));
        return;
    }

    let address = args.flag_address.unwrap_or(BME280_DEFAULT_ADDRESS);
    let config: Config = Config {
        mode: Mode::Force,
        oversampling_temperature: Oversampling::X1,
        oversampling_pressure: Oversampling::X1,
        oversampling_humidity: Oversampling::X2,
        standby_time: StandbyTime::Ms1,
        filter_coeff: IIRFilterCoeff::OFF,
        spi3w_enabled: false,
    };

    let dev = LinuxI2CDevice::new(args.arg_device, address).unwrap();
    let mut bme280 = BME280::new(dev, config).unwrap();

    // Sleep while measureing finishes.
    let millis = time::Duration::from_millis(100);
    thread::sleep(millis);

    if args.flag_temperature {
        println!("{}", bme280.temperature().unwrap());
    }
    if args.flag_humidity {
        println!("{:.2}", bme280.humidity().unwrap());
    }
    if args.flag_pressure {
        println!("{:.2}", bme280.pressure().unwrap() / 100.0);
    }
}
<commit_msg>Remove unnecessary constant.<commit_after>extern crate docopt;
extern crate i2cdev;
#[macro_use]
extern crate serde_derive;
extern crate bme280_rs;

use docopt::Docopt;

use std::{thread, time};

#[cfg(target_os = "linux")]
use i2cdev::linux::*;

use bme280_rs::*;

const USAGE: &'static str = "
Reading BME280 sensor value

Usage:
  bme280 <device> [--address <addr>] [--temperature] [--humidity] [--pressure]
  bme280 (-h | --help)
  bme280 (-v | --version)

Options:
  -h --help    Show this help text.
  --address <addr>     I2C device address [default: 119] (=0x77)
  --temperature    Show temperature in degree Celsius.
  --humidity    Show humidity in %.
  --pressure    Show pressure in hPa.
  -v --version    Show version.
";

#[derive(Debug, Deserialize)]
struct Args {
    arg_device: String,
    flag_address: u16,
    flag_version: bool,
    flag_temperature: bool,
    flag_humidity: bool,
    flag_pressure: bool,
}

#[cfg(not(target_os = "linux"))]
fn main() {
    println!("This program can run only on Linux")
}

#[cfg(target_os = "linux")]
fn main() {
    let args: Args = Docopt::new(USAGE).and_then(|d| d.deserialize()).unwrap_or_else(|e| e.exit());

    if args.flag_version {
        println!("bme280 {}", env!("CARGO_PKG_VERSION"));
        return;
    }

    let address = args.flag_address;
    let config: Config = Config {
        mode: Mode::Force,
        oversampling_temperature: Oversampling::X1,
        oversampling_pressure: Oversampling::X1,
        oversampling_humidity: Oversampling::X2,
        standby_time: StandbyTime::Ms1,
        filter_coeff: IIRFilterCoeff::OFF,
        spi3w_enabled: false,
    };

    let dev = LinuxI2CDevice::new(args.arg_device, address).unwrap();
    let mut bme280 = BME280::new(dev, config).unwrap();

    // Sleep while measureing finishes.
    let millis = time::Duration::from_millis(100);
    thread::sleep(millis);

    if args.flag_temperature {
        println!("{}", bme280.temperature().unwrap());
    }
    if args.flag_humidity {
        println!("{:.2}", bme280.humidity().unwrap());
    }
    if args.flag_pressure {
        println!("{:.2}", bme280.pressure().unwrap() / 100.0);
    }
}
<|endoftext|>


<commit_before>extern crate num;
extern crate portaudio;
extern crate rtlsdr;

use portaudio as pa;
use rtlsdr::{RTLSDRDevice, RTLSDRError};
use num::complex::Complex;
use std::env;
use std::collections::VecDeque;
use std::f32::consts::PI;
use std::error::Error;

// Make sure that the buffer size is radix-2, otherwise the read_sync function
// will fail with an error code of -8.
const BUF_SIZE: usize = 131072;

// Most other sample rates fail, but this one works for my particular device.
// I will investigate exactly what's happening here and generate a list of
// possible sample rates (if there are others besides this one).
const SAMPLE_RATE: u32 = 1140000;

// Here is the sample rate of the output waveform we'll try to use.
const SAMPLE_RATE_AUDIO: f64 = 44100.0;

const DEVIATION: u32 = 2500;

const BANDWIDTH: u32 = 200000;

const CHANNELS: i32 = 2;

fn main() {
    match run() {
        Ok(()) => (),
        Err(e) => println!("ERROR: {}", e),
    }
}

fn run() -> Result<(), Box<Error>> {
    let sdr_index: i32 = env::args().nth(1)
        .expect("Please specify the SDR index")
        .parse::<i32>()?;
    let fm_freq_mhz: f32 = env::args().nth(2)
        .expect("Please specify a center frequency (MHz).")
        .parse::<f32>()?;

    // Initialize the PortAudio class.
    let audio = try!(pa::PortAudio::new());
    let settings =
        try!(audio.default_output_stream_settings(CHANNELS,
                                                  SAMPLE_RATE_AUDIO,
                                                  BUF_SIZE as u32));
    let mut stream = try!(audio.open_blocking_stream(settings));
    try!(stream.start());

    let fm_freq: u32 = (fm_freq_mhz * 1e6) as u32;
    let mut sdr = init_sdr(sdr_index, fm_freq).unwrap();

    // TODO: does this gain value make sense based on what I'm doing?
    let gain = (2.0 * PI * DEVIATION as f32 / SAMPLE_RATE_AUDIO as f32).recip();

    loop {
        // Read the samples and decimate down to match the sample rate of the
        // audio that'll be going out.
        //
        // TODO: on 88.7 I can make out some instruments, but the audio is still
        // very, very poor. Need to figure out what we need to do here. At a
        // minimum, we need a low-pass filter to filter out the high
        // frequency components that are almost certainly aliasing. In addition,
        // we are underflowing the audio buffer. Not sure if this is a
        // performance issue (release didn't help) or if I need to re-think how
        // I send data to the sound card.
        let bytes = sdr.read_sync(BUF_SIZE).unwrap();
        let mut iq_vec = read_samples(bytes).unwrap();
        let dec_rate = (SAMPLE_RATE as f64/ SAMPLE_RATE_AUDIO) as usize;
        iq_vec = decimate(iq_vec, dec_rate);

        // After decimation, demodulate the signal.
        //
        // TODO: from what I've read there should be some decimation here as
        // well but with narrowband FM, we decimate the signal into
        // nothingness. Need to figure out what we do between wideband and
        // narrowband FM.
        let mut demod_iq = demod_fm(iq_vec, gain);

        // Get the write stream and write our samples to the stream.
        let out_frames = match stream.write_available() {
            Ok(available) => {
                match available {
                    pa::StreamAvailable::Frames(frames) => frames as u32,
                    _ => return Err(Box::new(pa::error::Error::NullCallback)),
                }
            }
            Err(e) => return Err(Box::new(e)),
        };

        let buffer_frames = (demod_iq.len() / CHANNELS as usize) as
                            u32;
        let write_frames = if buffer_frames >= out_frames {
            out_frames
        } else {
            buffer_frames
        };
        let n_write_samples = write_frames as usize * CHANNELS as usize;

        stream.write(write_frames,
                   |output| for ix in 0..n_write_samples as usize {
                       output[ix] = demod_iq.pop_front().unwrap();
                   })?;
    }
}

fn init_sdr(sdr_index: i32, fm_freq: u32) -> Result<RTLSDRDevice, RTLSDRError> {
    let mut sdr = try!(rtlsdr::open(sdr_index));
    try!(sdr.set_center_freq(fm_freq));
    try!(sdr.set_sample_rate(SAMPLE_RATE));
    try!(sdr.set_tuner_bandwidth(BANDWIDTH));
    try!(sdr.set_agc_mode(true));
    try!(sdr.reset_buffer());
    Ok(sdr)
}

fn decimate_queue<T: Copy>(signal: VecDeque<T>, dec_rate: usize) -> VecDeque<T> {
    let mut ix = 0;
    let mut signal_dec = VecDeque::<T>::new();
    while ix < signal.len() {
        signal_dec.push_back(*(signal.get(ix).unwrap()));
        ix += dec_rate;
    }
    signal_dec
}

fn decimate<T: Copy>(signal: Vec<T>, dec_rate: usize) -> Vec<T> {
    let mut ix = 0;
    let mut signal_dec = Vec::<T>::new();
    while ix < signal.len() {
        signal_dec.push(*(signal.get(ix).unwrap()));
        ix += dec_rate;
    }
    signal_dec
}

fn read_samples(bytes: Vec<u8>) -> Option<Vec<Complex<f32>>> {
    // The docs tell us that RTL-SDR sends us data in bytes alternating between
    // I and Q. I'm not normalizing here, but I might have to in the future.

    // First, check that we've been given an even number of bytes. Not sure
    // what to do if we don't get I and Q.
    let bytes_len = bytes.len();
    if bytes_len % 2 != 0 {
        println!("Received an odd number of samples.");
        return None;

    }
    let mut iq_vec: Vec<Complex<f32>> = Vec::with_capacity(bytes_len / 2);

    // Write the values to the complex value and normalize from [0, 255] to
    // [-1, 1].
    for iq in bytes.chunks(2) {
        iq_vec.push(Complex::new((iq[0] as f32 - 127.5) / 127.5,
                                 (iq[1] as f32 - 127.5) / 127.5));
    }

    Some(iq_vec)
}

fn demod_fm(iq: Vec<Complex<f32>>, gain: f32) -> VecDeque<f32> {
    let mut demod_queue: VecDeque<f32> = VecDeque::with_capacity(iq.len());
    let mut prev = iq[0];
    for ix in 0..iq.len() {
        let c: Complex<f32> = prev.conj() * iq[ix];
        demod_queue.push_back(c.re.atan2(c.im) * gain);
        prev = iq[ix];
    }
    demod_queue
}
<commit_msg>Added multithreading support to the application.<commit_after>extern crate num;
extern crate portaudio;
extern crate rtlsdr;

use portaudio as pa;
use rtlsdr::{RTLSDRDevice, RTLSDRError};
use num::complex::Complex;
use std::env;
use std::collections::VecDeque;
use std::f32::consts::PI;
use std::error::Error;

// Threading libraries.
use std::thread;
use std::sync::mpsc::channel;

// Make sure that the buffer size is radix-2, otherwise the read_sync function
// will fail with an error code of -8.
const BUF_SIZE: usize = 131072;

// Most other sample rates fail, but this one works for my particular device.
// I will investigate exactly what's happening here and generate a list of
// possible sample rates (if there are others besides this one).
const SAMPLE_RATE: u32 = 1140000;

// Here is the sample rate of the output waveform we'll try to use.
const SAMPLE_RATE_AUDIO: f64 = 44100.0;

const DEVIATION: u32 = 2500;

const BANDWIDTH: u32 = 200000;

const CHANNELS: i32 = 2;

fn main() {
    match run() {
        Ok(()) => (),
        Err(e) => println!("ERROR: {}", e),
    }
}

struct RTLSDR {
    rtlsdr: RTLSDRDevice,
}

unsafe impl Send for RTLSDR {}

fn run() -> Result<(), Box<Error>> {
    let sdr_index: i32 = env::args().nth(1)
        .expect("Please specify the SDR index")
        .parse::<i32>()?;
    let fm_freq_mhz: f32 = env::args().nth(2)
        .expect("Please specify a center frequency (MHz).")
        .parse::<f32>()?;

    // Initialize the PortAudio class.
    let audio = try!(pa::PortAudio::new());
    let settings =
        try!(audio.default_output_stream_settings(CHANNELS,
                                                  SAMPLE_RATE_AUDIO,
                                                  BUF_SIZE as u32));
    let mut stream = try!(audio.open_blocking_stream(settings));
    try!(stream.start());

    let fm_freq: u32 = (fm_freq_mhz * 1e6) as u32;
    let mut sdr = RTLSDR {
        rtlsdr: init_sdr(sdr_index, fm_freq).unwrap(),
    };

    // TODO: does this gain value make sense based on what I'm doing?
    let gain = (2.0 * PI * DEVIATION as f32 / SAMPLE_RATE_AUDIO as f32).recip();
    let (tx, rx) = channel();

    thread::spawn(move || {
        loop {
            // Read the samples and decimate down to match the sample rate of the
            // audio that'll be going out.
            //
            // TODO: on 88.7 I can make out some instruments, but the audio is still
            // very, very poor. Need to figure out what we need to do here. At a
            // minimum, we need a low-pass filter to filter out the high
            // frequency components that are almost certainly aliasing. In addition,
            // we are underflowing the audio buffer. Not sure if this is a
            // performance issue (release didn't help) or if I need to re-think how
            // I send data to the sound card.
            let tx = tx.clone();
            let bytes = sdr.rtlsdr.read_sync(BUF_SIZE).unwrap();
            let dec_rate = (SAMPLE_RATE as f64/ SAMPLE_RATE_AUDIO) as usize;
            let iq_vec = decimate(read_samples(bytes).unwrap().as_slice(), dec_rate);

            // After decimation, demodulate the signal.
            //
            // TODO: from what I've read there should be some decimation here as
            // well but with narrowband FM, we decimate the signal into
            // nothingness. Need to figure out what we do between wideband and
            // narrowband FM.
            let mut demod_iq = demod_fm(iq_vec, gain);
            tx.send(demod_iq).unwrap();
        }
    });

    loop {
        // Get the write stream and write our samples to the stream.
        let mut demod_iq = rx.recv().unwrap();
        let out_frames = match stream.write_available() {
            Ok(available) => {
                match available {
                    pa::StreamAvailable::Frames(frames) => frames as u32,
                    _ => return Err(Box::new(pa::error::Error::NullCallback)),
                }
            }
            Err(e) => return Err(Box::new(e)),
        };

        let buffer_frames = (demod_iq.len() / CHANNELS as usize) as
                            u32;
        let write_frames = if buffer_frames >= out_frames {
            out_frames
        } else {
            buffer_frames
        };
        let n_write_samples = write_frames as usize * CHANNELS as usize;

        stream.write(write_frames,
                   |output| for ix in 0..n_write_samples as usize {
                       output[ix] = demod_iq.pop_front().unwrap();
                   })?;
    }
}

fn init_sdr(sdr_index: i32, fm_freq: u32) -> Result<RTLSDRDevice, RTLSDRError> {
    let mut sdr = try!(rtlsdr::open(sdr_index));
    try!(sdr.set_center_freq(fm_freq));
    try!(sdr.set_sample_rate(SAMPLE_RATE));
    try!(sdr.set_tuner_bandwidth(BANDWIDTH));
    try!(sdr.set_agc_mode(true));
    try!(sdr.reset_buffer());
    Ok(sdr)
}

fn decimate_queue<T: Copy>(signal: VecDeque<T>, dec_rate: usize) -> VecDeque<T> {
    let mut ix = 0;
    let mut signal_dec = VecDeque::<T>::new();
    while ix < signal.len() {
        signal_dec.push_back(*(signal.get(ix).unwrap()));
        ix += dec_rate;
    }
    signal_dec
}

fn decimate(signal: &[Complex<f32>], dec_rate: usize) -> Vec<Complex<f32>> {
    let mut ix = 0;
    let new_size = (signal.len() / dec_rate + 1) as usize;
    let mut signal_dec = Vec::<Complex<f32>>::with_capacity(new_size);
    while ix < signal.len() {
        signal_dec.push(signal[ix]);
        ix += dec_rate;
    }
    signal_dec
}

fn read_samples(bytes: Vec<u8>) -> Option<Vec<Complex<f32>>> {
    // The docs tell us that RTL-SDR sends us data in bytes alternating between
    // I and Q. I'm not normalizing here, but I might have to in the future.

    // First, check that we've been given an even number of bytes. Not sure
    // what to do if we don't get I and Q.
    let bytes_len = bytes.len();
    if bytes_len % 2 != 0 {
        println!("Received an odd number of samples.");
        return None;

    }
    let mut iq_vec: Vec<Complex<f32>> = Vec::with_capacity(bytes_len / 2);

    // Write the values to the complex value and normalize from [0, 255] to
    // [-1, 1].
    for iq in bytes.chunks(2) {
        iq_vec.push(Complex::new((iq[0] as f32 - 127.5) / 127.5,
                                 (iq[1] as f32 - 127.5) / 127.5));
    }

    Some(iq_vec)
}

fn demod_fm(iq: Vec<Complex<f32>>, gain: f32) -> VecDeque<f32> {
    let mut demod_queue: VecDeque<f32> = VecDeque::with_capacity(iq.len());
    let mut prev = iq[0];
    for ix in 0..iq.len() {
        let c: Complex<f32> = prev.conj() * iq[ix];
        demod_queue.push_back(c.re.atan2(c.im) * gain);
        prev = iq[ix];
    }
    demod_queue
}
<|endoftext|>


<commit_before>extern crate glob;
extern crate http;
extern crate hyper;
extern crate hyper_tls;
#[macro_use]
extern crate lazy_static;
extern crate libc;
#[macro_use]
extern crate serde_derive;
extern crate serde_json;
#[macro_use]
extern crate structopt;
extern crate toml;

mod ghclient;
mod statics;
mod structs;
#[macro_use]
mod syslog;

use statics::CONF_PATH;
use structopt::StructOpt;

#[derive(Debug, StructOpt)]
enum Command {
    /// Gets user public key
    #[structopt(name = "key")]
    Key {
        #[structopt(parse(from_str))]
        user: String,
    },
    /// Executes pam check
    #[structopt(name = "pam")]
    Pam,
    /// Check configuration
    #[structopt(name = "check")]
    Check {
        #[structopt(parse(from_os_str))]
        confpath: std::path::PathBuf,
    },
    /// Cleans caches up
    #[structopt(name = "cleanup")]
    CleanUp,
    /// Get rate limit for github api
    #[structopt(name = "ratelimit")]
    RateLimit,
    /// Displays version details
    #[structopt(name = "version")]
    Version,
}

fn main() {
    let command = Command::from_args();

    use ghclient::GithubClient;
    use std::env;
    use std::process;
    use structs::Config;

    match command {
        Command::Check { confpath } => match Config::new(&confpath) {
            Ok(_) => process::exit(0),
            Err(_) => process::exit(11),
        },
        Command::Key { user } => {
            match Config::new(&CONF_PATH).and_then(|conf| Ok(GithubClient::new(&conf)))
                                         .and_then(|client| client.print_user_public_key(&user))
            {
                Ok(_) => process::exit(0),
                Err(_) => process::exit(21),
            }
        }
        Command::Pam => match env::var("PAM_USER") {
            Ok(user) => match Config::new(&CONF_PATH).and_then(|conf| Ok(GithubClient::new(&conf)))
                                                     .and_then(|client| client.check_pam(&user))
            {
                Ok(true) => process::exit(0),
                Ok(false) => process::exit(1),
                Err(_) => process::exit(31),
            },
            Err(_) => process::exit(41),
        },
        Command::CleanUp => {
            match Config::new(&CONF_PATH).and_then(|conf| Ok(GithubClient::new(&conf)))
                                         .and_then(|client| client.clear_all_caches())
            {
                Ok(_) => process::exit(0),
                Err(_) => process::exit(51),
            }
        }
        Command::RateLimit => {
            match Config::new(&CONF_PATH).and_then(|conf| Ok(GithubClient::new(&conf)))
                                         .and_then(|client| client.print_rate_limit())
            {
                Ok(_) => process::exit(0),
                Err(_) => process::exit(61),
            }
        }
        Command::Version => {
            println!("{}",
                     concat!(env!("CARGO_PKG_VERSION"),
                             include_str!(concat!(env!("OUT_DIR"), "/commit-info.txt"))));
        }
    };
}
<commit_msg>Format<commit_after>extern crate glob;
extern crate http;
extern crate hyper;
extern crate hyper_tls;
#[macro_use]
extern crate lazy_static;
extern crate libc;
#[macro_use]
extern crate serde_derive;
extern crate serde_json;
#[macro_use]
extern crate structopt;
extern crate toml;

mod ghclient;
mod statics;
mod structs;
#[macro_use]
mod syslog;

use statics::CONF_PATH;
use structopt::StructOpt;

#[derive(Debug, StructOpt)]
enum Command {
    /// Gets user public key
    #[structopt(name = "key")]
    Key {
        #[structopt(parse(from_str))]
        user: String,
    },
    /// Executes pam check
    #[structopt(name = "pam")]
    Pam,
    /// Check configuration
    #[structopt(name = "check")]
    Check {
        #[structopt(parse(from_os_str))]
        confpath: std::path::PathBuf,
    },
    /// Cleans caches up
    #[structopt(name = "cleanup")]
    CleanUp,
    /// Get rate limit for github api
    #[structopt(name = "ratelimit")]
    RateLimit,
    /// Displays version details
    #[structopt(name = "version")]
    Version,
}

fn main() {
    let command = Command::from_args();

    use ghclient::GithubClient;
    use std::env;
    use std::process;
    use structs::Config;

    match command {
        Command::Check { confpath } => match Config::new(&confpath) {
            Ok(_) => process::exit(0),
            Err(_) => process::exit(11),
        },
        Command::Key { user } => match Config::new(&CONF_PATH).and_then(|conf| Ok(GithubClient::new(&conf)))
                                                              .and_then(|client| client.print_user_public_key(&user))
        {
            Ok(_) => process::exit(0),
            Err(_) => process::exit(21),
        },
        Command::Pam => match env::var("PAM_USER") {
            Ok(user) => match Config::new(&CONF_PATH).and_then(|conf| Ok(GithubClient::new(&conf)))
                                                     .and_then(|client| client.check_pam(&user))
            {
                Ok(true) => process::exit(0),
                Ok(false) => process::exit(1),
                Err(_) => process::exit(31),
            },
            Err(_) => process::exit(41),
        },
        Command::CleanUp => match Config::new(&CONF_PATH).and_then(|conf| Ok(GithubClient::new(&conf)))
                                                         .and_then(|client| client.clear_all_caches())
        {
            Ok(_) => process::exit(0),
            Err(_) => process::exit(51),
        },
        Command::RateLimit => match Config::new(&CONF_PATH).and_then(|conf| Ok(GithubClient::new(&conf)))
                                                           .and_then(|client| client.print_rate_limit())
        {
            Ok(_) => process::exit(0),
            Err(_) => process::exit(61),
        },
        Command::Version => {
            println!("{}",
                     concat!(env!("CARGO_PKG_VERSION"),
                             include_str!(concat!(env!("OUT_DIR"), "/commit-info.txt"))));
        }
    };
}
<|endoftext|>


<commit_before>extern crate futures;
extern crate tokio_core;
extern crate tokio_proto;
extern crate tokio_service;
extern crate uuid;
extern crate clap;
extern crate es_proto;

use std::io;
use std::net::SocketAddr;
use std::thread;
use std::process;
use uuid::Uuid;

use futures::{Future, IntoFuture, Stream, Sink};
use futures::future::{BoxFuture, Either};
use tokio_core::reactor::{Core, Handle};
use tokio_service::Service;
use futures::sync::{oneshot, mpsc};

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

use es_proto::{EventStoreClient, Package, Message, Builder, ExpectedVersion, StreamVersion, UsernamePassword};

#[derive(Debug)]
enum ReadMode {
    ForwardOnce{ skip: usize, count: usize },
    Backward{ skip: usize, count: usize }
}

impl ReadMode {
    fn into_request(self, stream_id: &str) -> Message {
        use ReadMode::*;
        match self {
            ForwardOnce { skip, count } => {
                if count != 1 {
                    unimplemented!();
                }
                Builder::read_event()
                    .stream_id(stream_id.to_owned())
                    .event_number(StreamVersion::from_opt(skip as u32).expect("Stream version overflow"))
                    .resolve_link_tos(true)
                    .require_master(false)
                    .build_message()
            },
            Backward { .. } => unimplemented!()
        }
    }
}

fn main() {

    let matches = App::new("testclient")
        .version("0.1.0")
        .about("Test client similar to EventStore.TestClient in EventStore binary distribution")
        .arg(Arg::with_name("hostname")
                .short("h")
                .long("host")
                .value_name("HOST")
                .takes_value(true)
                .help("The name of the host to connect to, default: 127.0.0.1"))
        .arg(Arg::with_name("port")
                .short("p")
                .long("port")
                .value_name("PORT")
                .takes_value(true)
                .help("The port to connect to, default: 1113"))
        .arg(Arg::with_name("verbose")
                 .short("v")
                 .long("verbose")
                 .takes_value(false)
                 .help("Output verbose timings"))
        .subcommand(SubCommand::with_name("ping")
                        .about("Send a ping to the host after possibly authenticating depending on the options"))
        .subcommand(SubCommand::with_name("write")
                        .about("Write single event to a given stream")
                        .arg(Arg::with_name("stream_id")
                                .value_name("STREAM-ID")
                                .required(true)
                                .index(1)
                                .help("Stream id to write to"))
                        .arg(Arg::with_name("expected_version")
                                .value_name("EXPECTED_VERSION")
                                .required(true)
                                .index(2)
                                .help("Acceptable values: any|created|n where n >= -2"))
                        .arg(Arg::with_name("type")
                                .value_name("TYPE")
                                .required(true)
                                .index(3)
                                .help("Type name of the event"))
                        .arg(Arg::with_name("data")
                                .value_name("DATA")
                                .required(true)
                                .index(4)
                                .help("Raw bytes of data."))
                        .arg(Arg::with_name("metadata")
                                .value_name("METADATA")
                                .required(false)
                                .index(5)
                                .help("Raw bytes of metadata, optional"))
                        .arg(Arg::with_name("json")
                                .short("j")
                                .long("json")
                                .takes_value(false)
                                .help("Flags the data and metadata (when given) as json values")))
        .subcommand(SubCommand::with_name("read")
                        .about("Read event(s) of a stream")
                        .arg(Arg::with_name("stream_id")
                                .value_name("STREAM-ID")
                                .required(true)
                                .index(1)
                                .help("Stream id to read from, or $all for every stream"))
                        .arg(Arg::with_name("count")
                             .value_name("N")
                             .short("c")
                             .long("count")
                             .takes_value(true)
                             .help("Number of events to read, 'all' or N > 0, defaults to 1"))
                        .arg(Arg::with_name("skip")
                             .value_name("SKIP")
                             .short("s")
                             .long("skip")
                             .takes_value(true)
                             .help("The event number to start from, defaults to 0"))
                        .arg(Arg::with_name("mode")
                             .value_name("MODE")
                             .short("m")
                             .long("mode")
                             .takes_value(true)
                             .possible_values(&["forward-forever", "forward-once", "backward"])
                             .help("'forward-once' reads up until the current latest,
 'forward-forever' stays and awaits for new messages until count has been reached,
 'backward' goes to up to first event")))
        .get_matches();

    let addr = {
        let s = format!("{}:{}", matches.value_of("hostname").unwrap_or("127.0.0.1"), matches.value_of("port").unwrap_or("1113"));
        s.parse::<SocketAddr>().expect("Failed to parse host:port as SocketAddr")
    };

    let verbose = matches.is_present("verbose");

    let res = if let Some(matches) = matches.subcommand_matches("ping") {
        ping(addr, verbose)
    } else if let Some(w) = matches.subcommand_matches("write") {
        let mut builder = Builder::write_events();
        builder.stream_id(w.value_of("stream_id").unwrap().to_owned())
            .expected_version(match w.value_of("expected_version").unwrap() {
                "any" => ExpectedVersion::Any,
                "created" => ExpectedVersion::NewStream,
                n => ExpectedVersion::Exact(StreamVersion::from_opt(n.parse().unwrap()).expect("Stream version out of bounds"))
            })
            .require_master(w.is_present("require_master"));

        {
            let mut event = builder.new_event();

            event = event.data(w.value_of("data").unwrap().as_bytes().iter().cloned().collect::<Vec<_>>())
                .data_content_type(w.is_present("json"))
                .event_type(w.value_of("type").unwrap().to_owned());

            event = if let Some(x) = w.value_of("metadata") {
                event.metadata(x.as_bytes().iter().cloned().collect::<Vec<_>>())
                    .metadata_content_type(w.is_present("json"))
            } else {
                event
            };

            event.done();
        }

        write(addr, verbose, builder.build_package(None, None))
    } else if let Some(r) = matches.subcommand_matches("read") {
        let stream_id = r.value_of("stream_id").unwrap();
        let count: usize = match r.value_of("count").unwrap_or("1") {
            "all" => usize::max_value(),
            s => s.parse().expect("Parsing count failed"),
        };

        let skip: usize = r.value_of("skip").unwrap_or("0").parse().expect("Parsing skip failed");
        let mode = match r.value_of("mode") {
            None | Some("forward-once") => ReadMode::ForwardOnce{skip, count},
            Some("forward-forever") => unimplemented!(),
            Some("backward") => ReadMode::Backward{skip, count},
            _ => unreachable!(),
        };

        read(addr, verbose, stream_id, mode)
    } else {
        println!("Subcommand is required.\n\n{}", matches.usage());
        process::exit(1);
    };

    if let Err(e) = res {
        println!("Failure: {:?}", e);
        process::exit(1);
    }
}

use std::time::Instant;

fn ping(addr: SocketAddr, verbose: bool) -> Result<(), io::Error> {
    let mut core = Core::new().unwrap();
    let handle = core.handle();

    let started = Instant::now();

    let job = EventStoreClient::connect(&addr, &handle)
        .map(|client| (client, Instant::now()))
        .and_then(|(client, connected)| {

            let f = client.call(Builder::ping().build_package(None, None));
            let send_began = Instant::now();

            f.map(move |resp| (resp, connected, send_began))
        }).and_then(|(pong, connected, send_began)| {
            let received = Instant::now();
            match pong.message {
                Message::Pong => Ok((connected, send_began, received)),
                msg => Err(io::Error::new(io::ErrorKind::Other, format!("Unexpected response: {:?}", msg)))
            }
        }).and_then(move |(connected, send_began, received)| {
            if verbose {
                print_elapsed("connected in  ", connected - started);
                print_elapsed("ready to send ", send_began - connected);
                print_elapsed("received in   ", received - send_began);
                print_elapsed("total         ", started.elapsed());
            } else {
                print_elapsed("pong received in", started.elapsed());
            }

            Ok(())
        });

    core.run(job)
}

fn write(addr: SocketAddr, verbose: bool, pkg: Package) -> Result<(), io::Error> {
    let mut core = Core::new().unwrap();
    let handle = core.handle();

    let client = EventStoreClient::connect(&addr, &handle);
    let started = Instant::now();

    let job = client.and_then(|client| {
        client.call(pkg)
    }).and_then(|resp| {
        match resp.message {
            Message::WriteEventsCompleted(Ok(success)) => {
                print_elapsed("Success in", started.elapsed());
                if verbose {
                    println!("{:#?}", success);
                }
                Ok(())
            },
            Message::WriteEventsCompleted(Err(reason)) => {
                Err(io::Error::new(io::ErrorKind::Other, format!("{}", reason)))
            },
            x => {
                Err(io::Error::new(io::ErrorKind::Other, format!("Unexpected response: {:?}", x)))
            }
        }
    });

    core.run(job)
}

fn read(addr: SocketAddr, verbose: bool, stream_id: &str, mode: ReadMode) -> Result<(), io::Error> {
    if stream_id == "$all" {
        unimplemented!();
    }

    let mut core = Core::new().unwrap();
    let handle = core.handle();

    let client = EventStoreClient::connect(&addr, &handle);
    let started = Instant::now();

    let job = client.and_then(|client| {
        client.call(Package {
            authentication: None,
            correlation_id: Uuid::new_v4(),
            message: mode.into_request(stream_id),
        })
    }).and_then(|resp| {
        println!("{:#?}", resp);
        Ok(())
    });

    core.run(job)
}

use std::time::Duration;

fn print_elapsed(subject: &str, d: Duration) {
    println!("{} {}.{:06}ms", subject, d.as_secs() * 1000 + d.subsec_nanos() as u64 / 1_000_000, d.subsec_nanos() % 1_000_000);
}

/*
fn nice(addr: &SocketAddr, handle: &Handle) {
    let client = EventStoreClient::connect(&addr, &handle);
    client.and_then(|client| {

    });
}*/

/*
struct Client {
    tx: mpsc::Sender<(Package, oneshot::Sender<Result<Package, io::Error>>)>,
}

impl Client {
    fn new(addr: &SocketAddr, handle: &Handle) -> Client {
        let (tx, rx) = mpsc::channel::<(Package, oneshot::Sender<Result<Package, io::Error>>)>(4);
        let client = EventStoreClient::connect(addr, handle);
        let task = client.and_then(move |client| {
            rx.for_each(move |(pkg, tx)| {
                client.call(pkg).then(|res| {
                    let ret = match res {
                        Ok(_) => Ok(()),
                        Err(_) => Err(())
                    };
                    tx.complete(res);
                    ret
                })
            }).map_err(|_| io::Error::new(io::ErrorKind::Other, "dunno?"))
        });
        handle.spawn(task.then(|res| match res { Ok(_) => Ok(()), Err(_) => Err(()) }));
        Client { tx: tx }
    }

    fn call(&self, msg: Message) -> MessageFuture {
        let (tx, rx) = oneshot::channel();

        self.tx.send(());
    }
}*/

// connecting
// ready
// heartbeat
// disconnected
<commit_msg>main: sketch out more features<commit_after>extern crate futures;
extern crate tokio_core;
extern crate tokio_proto;
extern crate tokio_service;
extern crate uuid;
extern crate clap;
extern crate es_proto;

use std::io;
use std::net::SocketAddr;
use std::thread;
use std::process;
use uuid::Uuid;

use futures::{Future, IntoFuture, Stream, Sink};
use futures::future::{BoxFuture, Either};
use tokio_core::reactor::{Core, Handle};
use tokio_service::Service;
use futures::sync::{oneshot, mpsc};

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

use es_proto::{EventStoreClient, Package, Message, Builder, ExpectedVersion, StreamVersion, UsernamePassword};

#[derive(Debug)]
enum ReadMode {
    ForwardOnce{ skip: usize, count: usize },
    Backward{ skip: usize, count: usize }
}

impl ReadMode {
    fn into_request(self, stream_id: &str) -> Message {
        use ReadMode::*;
        match self {
            ForwardOnce { skip, count } => {
                if count != 1 {
                    unimplemented!();
                }
                Builder::read_event()
                    .stream_id(stream_id.to_owned())
                    .event_number(StreamVersion::from_opt(skip as u32).expect("Stream version overflow"))
                    .resolve_link_tos(true)
                    .require_master(false)
                    .build_message()
            },
            Backward { .. } => unimplemented!()
        }
    }
}

fn main() {

    let matches = App::new("testclient")
        .version("0.1.0")
        .about("Test client similar to EventStore.TestClient in EventStore binary distribution")
        .arg(Arg::with_name("hostname")
                .short("h")
                .long("host")
                .value_name("HOST")
                .takes_value(true)
                .help("The name of the host to connect to, default: 127.0.0.1"))
        .arg(Arg::with_name("port")
                .short("p")
                .long("port")
                .value_name("PORT")
                .takes_value(true)
                .help("The port to connect to, default: 1113"))
        .arg(Arg::with_name("verbose")
                 .short("v")
                 .long("verbose")
                 .takes_value(false)
                 .help("Output verbose timings"))
        .subcommand(SubCommand::with_name("ping")
                        .about("Send a ping to the host after possibly authenticating depending on the options"))
        .subcommand(SubCommand::with_name("write")
                        .about("Write single event to a given stream")
                        .arg(Arg::with_name("stream_id")
                                .value_name("STREAM-ID")
                                .required(true)
                                .index(1)
                                .help("Stream id to write to"))
                        .arg(Arg::with_name("expected_version")
                                .value_name("EXPECTED_VERSION")
                                .required(true)
                                .index(2)
                                .help("Acceptable values: any|created|n where n >= -2"))
                        .arg(Arg::with_name("type")
                                .value_name("TYPE")
                                .required(true)
                                .index(3)
                                .help("Type name of the event"))
                        .arg(Arg::with_name("data")
                                .value_name("DATA")
                                .required(true)
                                .index(4)
                                .help("Raw bytes of data."))
                        .arg(Arg::with_name("metadata")
                                .value_name("METADATA")
                                .required(false)
                                .index(5)
                                .help("Raw bytes of metadata, optional"))
                        .arg(Arg::with_name("json")
                                .short("j")
                                .long("json")
                                .takes_value(false)
                                .help("Flags the data and metadata (when given) as json values")))
        .subcommand(SubCommand::with_name("read")
                        .about("Read event(s) of a stream")
                        .arg(Arg::with_name("stream_id")
                                .value_name("STREAM-ID")
                                .required(true)
                                .index(1)
                                .help("Stream id to read from, or $all for every stream"))
                        .arg(Arg::with_name("count")
                             .value_name("N")
                             .short("c")
                             .long("count")
                             .takes_value(true)
                             .help("Number of events to read, 'all' or N > 0, defaults to 1"))
                        .arg(Arg::with_name("skip")
                             .value_name("SKIP")
                             .short("s")
                             .long("skip")
                             .takes_value(true)
                             .help("The event number to start from, defaults to 0"))
                        .arg(Arg::with_name("mode")
                             .value_name("MODE")
                             .short("m")
                             .long("mode")
                             .takes_value(true)
                             .possible_values(&["forward-forever", "forward-once", "backward"])
                             .help("'forward-once' reads up until the current latest,
'forward-forever' stays and awaits for new messages until count has been reached,
'backward' goes to up to first event"))
                        .arg(Arg::with_name("output")
                             .value_name("OUTPUT_MODE")
                             .short("o")
                             .long("output")
                             .takes_value(true)
                             .possible_values(&["debug", "utf8_lossy", "json_oneline", "hex"])
                             .help("'debug' will print the structure with {:?} or {:#?} depending on verbose
'utf8_lossy' will attempt to decode data as utf8
'json_oneline' will parse the json and stringify on one line
'hex' will write data as hexadecimals")))
        .get_matches();

    let addr = {
        let s = format!("{}:{}", matches.value_of("hostname").unwrap_or("127.0.0.1"), matches.value_of("port").unwrap_or("1113"));
        s.parse::<SocketAddr>().expect("Failed to parse host:port as SocketAddr")
    };

    let verbose = matches.is_present("verbose");

    let res = if let Some(matches) = matches.subcommand_matches("ping") {
        ping(addr, verbose)
    } else if let Some(w) = matches.subcommand_matches("write") {
        let mut builder = Builder::write_events();
        builder.stream_id(w.value_of("stream_id").unwrap().to_owned())
            .expected_version(match w.value_of("expected_version").unwrap() {
                "any" => ExpectedVersion::Any,
                "created" => ExpectedVersion::NewStream,
                n => ExpectedVersion::Exact(StreamVersion::from_opt(n.parse().unwrap()).expect("Stream version out of bounds"))
            })
            .require_master(w.is_present("require_master"));

        {
            let mut event = builder.new_event();

            event = event.data(w.value_of("data").unwrap().as_bytes().iter().cloned().collect::<Vec<_>>())
                .data_content_type(w.is_present("json"))
                .event_type(w.value_of("type").unwrap().to_owned());

            event = if let Some(x) = w.value_of("metadata") {
                event.metadata(x.as_bytes().iter().cloned().collect::<Vec<_>>())
                    .metadata_content_type(w.is_present("json"))
            } else {
                event
            };

            event.done();
        }

        write(addr, verbose, builder.build_package(None, None))
    } else if let Some(r) = matches.subcommand_matches("read") {
        let stream_id = r.value_of("stream_id").unwrap();
        let count: usize = match r.value_of("count").unwrap_or("1") {
            "all" => usize::max_value(),
            s => s.parse().expect("Parsing count failed"),
        };

        let skip: usize = r.value_of("skip").unwrap_or("0").parse().expect("Parsing skip failed");
        let mode = match r.value_of("mode") {
            None | Some("forward-once") => ReadMode::ForwardOnce{skip, count},
            Some("forward-forever") => unimplemented!(),
            Some("backward") => ReadMode::Backward{skip, count},
            _ => unreachable!(),
        };

        read(addr, verbose, stream_id, mode)
    } else {
        println!("Subcommand is required.\n\n{}", matches.usage());
        process::exit(1);
    };

    if let Err(e) = res {
        println!("Failure: {:?}", e);
        process::exit(1);
    }
}

use std::time::Instant;

fn ping(addr: SocketAddr, verbose: bool) -> Result<(), io::Error> {
    let mut core = Core::new().unwrap();
    let handle = core.handle();

    let started = Instant::now();

    let job = EventStoreClient::connect(&addr, &handle)
        .map(|client| (client, Instant::now()))
        .and_then(|(client, connected)| {

            let f = client.call(Builder::ping().build_package(None, None));
            let send_began = Instant::now();

            f.map(move |resp| (resp, connected, send_began))
        }).and_then(|(pong, connected, send_began)| {
            let received = Instant::now();
            match pong.message {
                Message::Pong => Ok((connected, send_began, received)),
                msg => Err(io::Error::new(io::ErrorKind::Other, format!("Unexpected response: {:?}", msg)))
            }
        }).and_then(move |(connected, send_began, received)| {
            if verbose {
                print_elapsed("connected in  ", connected - started);
                print_elapsed("ready to send ", send_began - connected);
                print_elapsed("received in   ", received - send_began);
                print_elapsed("total         ", started.elapsed());
            } else {
                print_elapsed("pong received in", started.elapsed());
            }

            Ok(())
        });

    core.run(job)
}

fn write(addr: SocketAddr, verbose: bool, pkg: Package) -> Result<(), io::Error> {
    let mut core = Core::new().unwrap();
    let handle = core.handle();

    let client = EventStoreClient::connect(&addr, &handle);
    let started = Instant::now();

    let job = client.and_then(|client| {
        client.call(pkg)
    }).and_then(|resp| {
        match resp.message {
            Message::WriteEventsCompleted(Ok(success)) => {
                print_elapsed("Success in", started.elapsed());
                if verbose {
                    println!("{:#?}", success);
                }
                Ok(())
            },
            Message::WriteEventsCompleted(Err(reason)) => {
                Err(io::Error::new(io::ErrorKind::Other, format!("{}", reason)))
            },
            x => {
                Err(io::Error::new(io::ErrorKind::Other, format!("Unexpected response: {:?}", x)))
            }
        }
    });

    core.run(job)
}

fn read(addr: SocketAddr, verbose: bool, stream_id: &str, mode: ReadMode) -> Result<(), io::Error> {
    if stream_id == "$all" {
        unimplemented!();
    }

    let mut core = Core::new().unwrap();
    let handle = core.handle();

    let client = EventStoreClient::connect(&addr, &handle);
    let started = Instant::now();

    let job = client.and_then(|client| {
        client.call(Package {
            authentication: None,
            correlation_id: Uuid::new_v4(),
            message: mode.into_request(stream_id),
        })
    }).and_then(|resp| {
        println!("{:#?}", resp);
        Ok(())
    });

    core.run(job)
}

use std::time::Duration;

fn print_elapsed(subject: &str, d: Duration) {
    println!("{} {}.{:06}ms", subject, d.as_secs() * 1000 + d.subsec_nanos() as u64 / 1_000_000, d.subsec_nanos() % 1_000_000);
}

/*
fn nice(addr: &SocketAddr, handle: &Handle) {
    let client = EventStoreClient::connect(&addr, &handle);
    client.and_then(|client| {

    });
}*/

/*
struct Client {
    tx: mpsc::Sender<(Package, oneshot::Sender<Result<Package, io::Error>>)>,
}

impl Client {
    fn new(addr: &SocketAddr, handle: &Handle) -> Client {
        let (tx, rx) = mpsc::channel::<(Package, oneshot::Sender<Result<Package, io::Error>>)>(4);
        let client = EventStoreClient::connect(addr, handle);
        let task = client.and_then(move |client| {
            rx.for_each(move |(pkg, tx)| {
                client.call(pkg).then(|res| {
                    let ret = match res {
                        Ok(_) => Ok(()),
                        Err(_) => Err(())
                    };
                    tx.complete(res);
                    ret
                })
            }).map_err(|_| io::Error::new(io::ErrorKind::Other, "dunno?"))
        });
        handle.spawn(task.then(|res| match res { Ok(_) => Ok(()), Err(_) => Err(()) }));
        Client { tx: tx }
    }

    fn call(&self, msg: Message) -> MessageFuture {
        let (tx, rx) = oneshot::channel();

        self.tx.send(());
    }
}*/

// connecting
// ready
// heartbeat
// disconnected
<|endoftext|>


<commit_before>#[macro_use] extern crate clap;
#[macro_use] extern crate gfx;
extern crate gfx_window_glutin;
extern crate glutin;
extern crate image;
#[macro_use] extern crate log;
extern crate env_logger;

mod argvalues;
mod runner;
mod loader;

use argvalues::ArgValues;

fn main() {
    env_logger::init().expect("Unable to initialize logger");

    match ArgValues::from_cli() {
        Ok(av) => {
            if let Err(e) = runner::run(&av) {
                error!("{}", e);
                return;
            }
        },
        Err(e) => {
            error!("{}", e);
            return;
        }
    }
}
<commit_msg>Forgot this suggestion<commit_after>#[macro_use] extern crate clap;
#[macro_use] extern crate gfx;
extern crate gfx_window_glutin;
extern crate glutin;
extern crate image;
#[macro_use] extern crate log;
extern crate env_logger;

mod argvalues;
mod runner;
mod loader;

use argvalues::ArgValues;

fn main() {
    env_logger::init().expect("Unable to initialize logger");

    if let Err(e) = ArgValues::from_cli().and_then(|av| runner::run(&av)) {
        error!("{}", e);
    }
}
<|endoftext|>


<commit_before>extern crate loc;

extern crate clap;
extern crate deque;
extern crate itertools;
extern crate num_cpus;
extern crate walkdir;

use clap::{Arg, App, AppSettings};
use walkdir::WalkDir;

use std::collections::HashMap;
use std::collections::hash_map::Entry;
use std::thread;

use deque::{Stealer, Stolen};

use loc as c;

enum Work {
    File(String),
    Quit,
}

struct Worker {
    chan: Stealer<Work>,
}

impl Worker {
    fn run(self) -> Vec<(c::Lang, String, c::Count)> {
        let mut v: Vec<(c::Lang, String, c::Count)> = vec![];
        loop {
            match self.chan.steal() {
                // What causes these?
                Stolen::Empty | Stolen::Abort => continue,
                Stolen::Data(Work::Quit) => break,
                Stolen::Data(Work::File(path)) => {
                    let lang = c::lang_from_ext(&path);
                    if lang != c::Lang::Unrecognized {
                        let count = c::count(&path);
                        v.push((lang, String::from(path), count));
                    }
                }
            };
        }
        v
    }
}

fn main() {

    let matches = App::new("count")
        .global_settings(&[AppSettings::ColoredHelp])
        .version("0.1")
        .author("Curtis Gagliardi <curtis@curtis.io>")
        .about("counts things quickly hopefully")
        .arg(Arg::with_name("exclude")
            .required(false)
            .long("exclude")
            .value_name("FILES/DIRS")
            .takes_value(true)
            .value_delimiter(",")
            .help("comma separated list of files/directories to ignore"))
        .arg(Arg::with_name("target")
            .required(true)
            .help("The file or directory to count lines in/of"))
        .get_matches();

    let filepaths = matches.values_of("target").unwrap();

    let threads = num_cpus::get();
    let mut workers = vec![];
    let (workq, stealer) = deque::new();
    for _ in 0..threads {
        let worker = Worker { chan: stealer.clone() };
        workers.push(thread::spawn(|| worker.run()));
    }

    for filepath in filepaths {
        for entry in WalkDir::new(filepath) {
            let entry = match entry {
                Ok(e) => e,
                Err(_) => {
                    continue;
                }
            };

            if entry.file_type().is_file() {
                let path = entry.path().to_str().unwrap();
                workq.push(Work::File(String::from(path)));
            }
        }
    }

    for _ in 0..workers.len() {
        workq.push(Work::Quit);
    }

    let mut counts: Vec<(c::Lang, String, c::Count)> = Vec::new();
    for worker in workers {
        counts.extend(worker.join().unwrap().iter().cloned())
    }

    let mut lang_counts_by_file: HashMap<c::Lang, Vec<(String, c::Count)>> = HashMap::new();
    for (lang, filepath, count) in counts {
        match lang_counts_by_file.entry(lang) {
            Entry::Occupied(mut lang_counts_by_file) => {
                lang_counts_by_file.get_mut().push((filepath, count))
            }
            Entry::Vacant(lang_counts_by_file) => {
                lang_counts_by_file.insert(vec![(filepath, count)]);
            }
        };
    }

    let mut lang_totals: HashMap<&c::Lang, c::LangTotal> = HashMap::new();
    for (lang, count_vec) in &lang_counts_by_file {
        // TODO(cgag): use a fold?
        let mut lang_total: c::Count = Default::default();
        for &(_, ref count) in count_vec.iter() {
            lang_total.merge(count);
        }
        lang_totals.insert(lang,
                           c::LangTotal {
                               files: count_vec.len() as u32,
                               count: lang_total,
                           });
    }

    let mut totals_by_lang = lang_totals.iter().collect::<Vec<(&&c::Lang, &c::LangTotal)>>();
    totals_by_lang.sort_by(|&(_, c1), &(_, c2)| c2.count.code.cmp(&c1.count.code));

    let mut totals = c::LangTotal {
        files: 0,
        count: Default::default(),
    };
    for &(_, total) in &totals_by_lang {
        totals.files += total.files;
        totals.count.code += total.count.code;
        totals.count.blank += total.count.blank;
        totals.count.comment += total.count.comment;
        totals.count.lines += total.count.lines;
    }

    let linesep = "---------------------------------------------------------------------------------";

    println!("{}", linesep);
    println!(" {0: <18} {1: >8} {2: >12} {3: >12} {4: >12} {5: >12}",
             "Language",
             "Files",
             "Lines",
             "Blank",
             "Comment",
             "Code");
    println!("{}", linesep);

    for &(lang, total) in &totals_by_lang {
        println!(" {0: <18} {1: >8} {2: >12} {3: >12} {4: >12} {5: >12}",
                 lang,
                 total.files,
                 total.count.lines,
                 total.count.blank,
                 total.count.comment,
                 total.count.code);
    }

    println!("{}", linesep);
    println!(" {0: <18} {1: >8} {2: >12} {3: >12} {4: >12} {5: >12}",
             "Total",
             totals.files,
             totals.count.lines,
             totals.count.blank,
             totals.count.comment,
             totals.count.code);
    println!("{}", linesep);
}
<commit_msg>comment out unused exclude option<commit_after>extern crate loc;

extern crate clap;
extern crate deque;
extern crate itertools;
extern crate num_cpus;
extern crate walkdir;

use clap::{Arg, App, AppSettings};
use walkdir::WalkDir;

use std::collections::HashMap;
use std::collections::hash_map::Entry;
use std::thread;

use deque::{Stealer, Stolen};

use loc as c;

enum Work {
    File(String),
    Quit,
}

struct Worker {
    chan: Stealer<Work>,
}

impl Worker {
    fn run(self) -> Vec<(c::Lang, String, c::Count)> {
        let mut v: Vec<(c::Lang, String, c::Count)> = vec![];
        loop {
            match self.chan.steal() {
                // What causes these?
                Stolen::Empty | Stolen::Abort => continue,
                Stolen::Data(Work::Quit) => break,
                Stolen::Data(Work::File(path)) => {
                    let lang = c::lang_from_ext(&path);
                    if lang != c::Lang::Unrecognized {
                        let count = c::count(&path);
                        v.push((lang, String::from(path), count));
                    }
                }
            };
        }
        v
    }
}

fn main() {

    let matches = App::new("count")
        .global_settings(&[AppSettings::ColoredHelp])
        .version("0.1")
        .author("Curtis Gagliardi <curtis@curtis.io>")
        .about("counts things quickly hopefully")
        // TODO(cgag): actually implement filtering
        // .arg(Arg::with_name("exclude")
        //     .required(false)
        //     .long("exclude")
        //     .value_name("FILES/DIRS")
        //     .takes_value(true)
        //     .value_delimiter(",")
        //     .help("comma separated list of files/directories to ignore"))
        .arg(Arg::with_name("target")
            .required(true)
            .help("The file or directory to count lines in/of"))
        .get_matches();

    let filepaths = matches.values_of("target").unwrap();

    let threads = num_cpus::get();
    let mut workers = vec![];
    let (workq, stealer) = deque::new();
    for _ in 0..threads {
        let worker = Worker { chan: stealer.clone() };
        workers.push(thread::spawn(|| worker.run()));
    }

    for filepath in filepaths {
        for entry in WalkDir::new(filepath) {
            let entry = match entry {
                Ok(e) => e,
                Err(_) => {
                    continue;
                }
            };

            if entry.file_type().is_file() {
                let path = entry.path().to_str().unwrap();
                workq.push(Work::File(String::from(path)));
            }
        }
    }

    for _ in 0..workers.len() {
        workq.push(Work::Quit);
    }

    let mut counts: Vec<(c::Lang, String, c::Count)> = Vec::new();
    for worker in workers {
        counts.extend(worker.join().unwrap().iter().cloned())
    }

    let mut lang_counts_by_file: HashMap<c::Lang, Vec<(String, c::Count)>> = HashMap::new();
    for (lang, filepath, count) in counts {
        match lang_counts_by_file.entry(lang) {
            Entry::Occupied(mut lang_counts_by_file) => {
                lang_counts_by_file.get_mut().push((filepath, count))
            }
            Entry::Vacant(lang_counts_by_file) => {
                lang_counts_by_file.insert(vec![(filepath, count)]);
            }
        };
    }

    let mut lang_totals: HashMap<&c::Lang, c::LangTotal> = HashMap::new();
    for (lang, count_vec) in &lang_counts_by_file {
        // TODO(cgag): use a fold?
        let mut lang_total: c::Count = Default::default();
        for &(_, ref count) in count_vec.iter() {
            lang_total.merge(count);
        }
        lang_totals.insert(lang,
                           c::LangTotal {
                               files: count_vec.len() as u32,
                               count: lang_total,
                           });
    }

    let mut totals_by_lang = lang_totals.iter().collect::<Vec<(&&c::Lang, &c::LangTotal)>>();
    totals_by_lang.sort_by(|&(_, c1), &(_, c2)| c2.count.code.cmp(&c1.count.code));

    let mut totals = c::LangTotal {
        files: 0,
        count: Default::default(),
    };
    for &(_, total) in &totals_by_lang {
        totals.files += total.files;
        totals.count.code += total.count.code;
        totals.count.blank += total.count.blank;
        totals.count.comment += total.count.comment;
        totals.count.lines += total.count.lines;
    }

    let linesep = "---------------------------------------------------------------------------------";

    println!("{}", linesep);
    println!(" {0: <18} {1: >8} {2: >12} {3: >12} {4: >12} {5: >12}",
             "Language",
             "Files",
             "Lines",
             "Blank",
             "Comment",
             "Code");
    println!("{}", linesep);

    for &(lang, total) in &totals_by_lang {
        println!(" {0: <18} {1: >8} {2: >12} {3: >12} {4: >12} {5: >12}",
                 lang,
                 total.files,
                 total.count.lines,
                 total.count.blank,
                 total.count.comment,
                 total.count.code);
    }

    println!("{}", linesep);
    println!(" {0: <18} {1: >8} {2: >12} {3: >12} {4: >12} {5: >12}",
             "Total",
             totals.files,
             totals.count.lines,
             totals.count.blank,
             totals.count.comment,
             totals.count.code);
    println!("{}", linesep);
}
<|endoftext|>