<commit_before>extern crate clap;
extern crate walkdir;
extern crate twox_hash;

use std::collections::HashMap;
use std::collections::HashSet;
use std::fs::File;
use std::io::Read;
use std::path::{Path, PathBuf};
use std::os::linux::fs::MetadataExt;

use clap::{App, Arg, ArgMatches};
use walkdir::WalkDir;
use twox_hash::XxHash;
use std::hash::Hasher;

#[derive(Default)]
struct Stats {
    file_count: u64,
    total_size: u64,
    duplicate_count: u64,
}

fn parse_args<'a>() -> ArgMatches<'a> {
    App::new("dupr")
        .version("0.1.0")
        .author("Mike Sampson")
        .about("Duplicate file finder")
        .arg(
            Arg::with_name("DIR")
                .help("Directory to process")
                .required(true),
        )
        .arg(Arg::with_name("summary").short("s").long("summary").help(
            "Print out summary information after duplicates",
        ))
        .get_matches()
}

fn collect_paths(path: &str, stats: &mut Stats) -> HashMap<u64, Vec<PathBuf>> {

    let mut length_paths = HashMap::new();

    for entry in WalkDir::new(path) {
        let entry = match entry {
            Ok(e) => e,
            Err(err) => {
                eprintln!("dupr: {}", err);
                continue;
            }
        };

        if !entry.file_type().is_file() {
            continue;
        }

        let metadata = match entry.metadata() {
            Ok(e) => e,
            Err(err) => {
                eprintln!("dupr: {}", err);
                continue;
            }
        };

        if metadata.len() == 0 {
            continue;
        }

        stats.file_count += 1;
        stats.total_size += metadata.len();

        let path = entry.path().to_path_buf();

        length_paths
            .entry(metadata.len())
            .or_insert(Vec::new())
            .push(path);
    }

    length_paths
}

fn main() {
    let matches = parse_args();

    let mut stats: Stats = Default::default();

    let files = collect_paths(matches.value_of("DIR").unwrap(), &mut stats);

    for f_paths in files.values().filter(|x| x.len() > 1) {

        // Avoid hardlinks to same inode
        let mut hardlinks = Vec::new();
        let mut di = HashSet::new();
        for f_path in f_paths {
            let metadata = match f_path.metadata() {
                Ok(m) => m,
                Err(err) => {
                    eprintln!("dupr: {}", err);
                    continue;
                }
            };
            let dev_inode = (metadata.st_dev(), metadata.st_ino());
            if !di.contains(&dev_inode) {
                hardlinks.push(f_path);
                di.insert(dev_inode);
            }
        }

        let mut hashes = HashMap::new();
        for f_path in hardlinks {
            hashes
                .entry(hash_file(&f_path))
                .or_insert(Vec::new())
                .push(f_path);
        }

        for (hash, paths) in hashes.iter().filter(|&(_, v)| v.len() > 1) {
            stats.duplicate_count += paths.len() as u64;
            for p in paths {
                println!("{:016x} - {}", hash, p.display());
            }
        }
    }

    if matches.is_present("summary") {
        println!(
            "Processed {} files with a total size of {} bytes. {} duplicates found.",
            stats.file_count,
            stats.total_size,
            stats.duplicate_count
        );
    }
}

fn hash_file<P: AsRef<Path>>(path: P) -> u64 {
    let mut file = File::open(path).unwrap();

    let mut hash = XxHash::with_seed(0);

    let mut buffer = [0; 32 * 1024];

    while let Ok(n) = file.read(&mut buffer) {
        if n == 0 {
            break;
        }
        hash.write(&buffer[0..n]);
    }

    hash.finish()
}
<commit_msg>Add spinner.<commit_after>extern crate clap;
extern crate walkdir;
extern crate twox_hash;

use std::collections::HashMap;
use std::collections::HashSet;
use std::fs::File;
use std::io::Read;
use std::path::{Path, PathBuf};
use std::os::linux::fs::MetadataExt;

use clap::{App, Arg, ArgMatches};
use walkdir::WalkDir;
use twox_hash::XxHash;
use std::hash::Hasher;

#[derive(Default)]
struct Stats {
    file_count: u64,
    total_size: u64,
    duplicate_count: u64,
}

fn parse_args<'a>() -> ArgMatches<'a> {
    App::new("dupr")
        .version("0.1.0")
        .author("Mike Sampson")
        .about("Duplicate file finder")
        .arg(
            Arg::with_name("DIR")
                .help("Directory to process")
                .required(true),
        )
        .arg(Arg::with_name("summary").short("s").long("summary").help(
            "Print out summary information after duplicates",
        ))
        .get_matches()
}

fn collect_paths(path: &str, stats: &mut Stats) -> HashMap<u64, Vec<PathBuf>> {

    let mut length_paths = HashMap::new();
    let spinner = ["|", "/", "-", "\\"];

    for (idx, entry) in WalkDir::new(path).into_iter().enumerate() {
        eprint!("\rBuilding file list {}", spinner[idx % spinner.len()]);
        let entry = match entry {
            Ok(e) => e,
            Err(err) => {
                eprintln!("dupr: {}", err);
                continue;
            }
        };

        if !entry.file_type().is_file() {
            continue;
        }

        let metadata = match entry.metadata() {
            Ok(e) => e,
            Err(err) => {
                eprintln!("dupr: {}", err);
                continue;
            }
        };

        if metadata.len() == 0 {
            continue;
        }

        stats.file_count += 1;
        stats.total_size += metadata.len();

        let path = entry.path().to_path_buf();

        length_paths
            .entry(metadata.len())
            .or_insert(Vec::new())
            .push(path);
    }
    eprint!("\r");
    length_paths
}

fn main() {
    let matches = parse_args();

    let mut stats: Stats = Default::default();

    let files = collect_paths(matches.value_of("DIR").unwrap(), &mut stats);

    for f_paths in files.values().filter(|x| x.len() > 1) {

        // Avoid hardlinks to same inode
        let mut hardlinks = Vec::new();
        let mut di = HashSet::new();
        for f_path in f_paths {
            let metadata = match f_path.metadata() {
                Ok(m) => m,
                Err(err) => {
                    eprintln!("dupr: {}", err);
                    continue;
                }
            };
            let dev_inode = (metadata.st_dev(), metadata.st_ino());
            if !di.contains(&dev_inode) {
                hardlinks.push(f_path);
                di.insert(dev_inode);
            }
        }

        let mut hashes = HashMap::new();
        for f_path in hardlinks {
            hashes
                .entry(hash_file(&f_path))
                .or_insert(Vec::new())
                .push(f_path);
        }

        for (hash, paths) in hashes.iter().filter(|&(_, v)| v.len() > 1) {
            stats.duplicate_count += paths.len() as u64;
            for p in paths {
                println!("{:016x} - {}", hash, p.display());
            }
        }
    }

    if matches.is_present("summary") {
        println!(
            "Processed {} files with a total size of {} bytes. {} duplicates found.",
            stats.file_count,
            stats.total_size,
            stats.duplicate_count
        );
    }
}

fn hash_file<P: AsRef<Path>>(path: P) -> u64 {
    let mut file = File::open(path).unwrap();

    let mut hash = XxHash::with_seed(0);

    let mut buffer = [0; 32 * 1024];

    while let Ok(n) = file.read(&mut buffer) {
        if n == 0 {
            break;
        }
        hash.write(&buffer[0..n]);
    }

    hash.finish()
}
<|endoftext|>


<commit_before>#[macro_use]
extern crate serde_derive;
extern crate serde;
extern crate serde_json;
extern crate reqwest;
#[macro_use]
extern crate clap;
extern crate geo;
extern crate num_traits;
extern crate rand;

use std::error::Error;
use std::process;
use std::boxed::Box;
use clap::{Arg, App, SubCommand, ArgMatches, Values};
use std::fmt;
use std::fmt::Display;
use reqwest::header::UserAgent;
use std::io::Write;
use geo::{Point, LineString, Polygon};
use geo::haversine_distance::HaversineDistance;
use rand::distributions::{IndependentSample, Range};
use std::fs::File;

static NOMINATIM_ENDPOINT: &'static str = "http://nominatim.openstreetmap.org";
const VERSION: &'static str = env!("CARGO_PKG_VERSION");
const NAME: &'static str = env!("CARGO_PKG_NAME");
const EARTH_RADIUS_KM: f64 = 6373.;

#[derive(Deserialize, Debug)]
struct Location {
    lat: String,
    lon: String,
}

impl Display for Location {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{},{}", self.lon, self.lat)
    }
}

#[derive(Deserialize, Debug)]
struct Place {
    display_name: String,
}

impl Display for Place {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", self.display_name)
    }
}

fn get_query_string(params: Vec<(&str, &str)>) -> String {
    let pairs: Vec<String> = params
        .into_iter()
        .map(|(k, v)| format!("{}={}", k, v))
        .collect();
    pairs.join("&")
}

fn reverse_geocode(lon: &str, lat: &str) -> Result<Place, Box<Error>> {
    let params = vec![("format", "json"), ("lon", lon), ("lat", lat)];
    let query_string = get_query_string(params);
    let url = format!("{}/reverse?{}", NOMINATIM_ENDPOINT, query_string);
    let client = reqwest::Client::new()?;
    let mut res = client
        .get(&url)
        .header(UserAgent(format!("{} v{}", NAME, VERSION)))
        .send()?;
    let result = res.json::<Place>()?;
    Ok(result)
}

fn search(place_name: &str) -> Result<Location, Box<Error>> {
    let params = vec![("format", "jsonv2"), ("q", place_name), ("limit", "1")];
    let query_string = get_query_string(params);
    let url = format!("{}/search?{}", NOMINATIM_ENDPOINT, query_string);
    let client = reqwest::Client::new()?;
    let mut res = client
        .get(&url)
        .header(UserAgent(format!("{} v{}", NAME, VERSION)))
        .send()?;
    let mut results = res.json::<Vec<Location>>()?;
    results.reverse();
    let first = results.pop().ok_or("No result found")?;
    Ok(first)
}

fn is_float(x: String) -> Result<(), String> {
    match x.parse::<f64>() {
        Ok(_) => Ok(()),
        Err(_) => Err("Value is not a proper float.".to_string()),
    }
}

fn is_positive_float(x: String) -> Result<(), String> {
    match x.parse::<f64>() {
            Ok(f) => Ok(f),
            Err(_) => Err("Value is not a proper float.".to_string()),
        }
        .and_then(|f| match f < 0. {
                      false => Ok(()),
                      true => Err("Value is not positive.".to_string()),
                  })
}

fn get_loc_arg<'a, 'b>(name: &'a str, short: &str) -> Arg<'a, 'b> {
    Arg::with_name(name)
        .required(true)
        .require_delimiter(true)
        .short(short)
        .long(name)
        .value_name("lon,lat")
        .validator(is_float)
        .number_of_values(2)
        .takes_value(true)
}

fn get_cli_app<'a, 'b>() -> App<'a, 'b> {
    App::new("Location utility")
        .version(crate_version!())
        .author(crate_authors!())
        .subcommand(SubCommand::with_name("loc")
                        .about("Retrieve a location via search string")
                        .arg(Arg::with_name("place")
                                 .required(true)
                                 .short("P")
                                 .long("place")
                                 .value_name("place name")
                                 .number_of_values(1)
                                 .takes_value(true)))
        .subcommand(SubCommand::with_name("rev")
                        .about("Reverse geocode a place via location")
                        .arg(get_loc_arg("location", "L")))
        .subcommand(SubCommand::with_name("rnd")
                        .about("Get a random location within a km radius")
                        .arg(get_loc_arg("location", "L"))
                        .arg(Arg::with_name("radius")
                                 .required(true)
                                 .short("R")
                                 .long("radius")
                                 .value_name("radius in km")
                                 .validator(is_positive_float)
                                 .number_of_values(1)
                                 .takes_value(true)))
        .subcommand(SubCommand::with_name("dis")
                        .about("Return the distance between two points in meters")
                        .arg(get_loc_arg("location1", "1"))
                        .arg(get_loc_arg("location2", "2")))
}

fn bail_out(message: &str) {
    writeln!(&mut std::io::stderr(), "{}", message).expect("could not write to stderr");
    process::exit(1);
}

fn handle_rev(matches: &ArgMatches) {
    let mut parts: Vec<&str> = matches.values_of("location").unwrap().collect();
    let lat = parts.pop().unwrap();
    let lon = parts.pop().unwrap();
    match reverse_geocode(lon, lat) {
        Ok(data) => println!("{}", data),
        Err(e) => bail_out(e.description()),
    }
}

fn get_random_point(center: &Point<f64>, radius: f64) -> Point<f64> {
    let mut rng = rand::thread_rng();
    let dist_range = Range::new(0., radius);
    let distance = dist_range.ind_sample(&mut rng);
    let bearing_range = Range::new(0., 360.);
    let bearing = bearing_range.ind_sample(&mut rng);

    direction(center, bearing, distance)
}

#[cfg(test)]
mod get_random_point {
    use super::*;
    use geo::contains::Contains;

    #[derive(Deserialize, Debug)]
    struct TestCoordinates(f64, f64);

    #[test]
    fn stays_within_the_given_radius() {
        let file = File::open("test/circle.json").unwrap();
        let coordinates: Vec<TestCoordinates> = serde_json::from_reader(file).unwrap();
        let points: Vec<Point<f64>> = coordinates
            .into_iter()
            .map(|t| Point::new(t.0, t.1))
            .collect();
        let exterior = LineString(points);
        let polygon = Polygon::new(exterior, vec![]);

        let center_point = Point::new(9.177789688110352, 48.776781529534965);
        for _ in 0..1000 {
            let random_point = get_random_point(&center_point, 9.9);
            let contained = polygon.contains(&random_point);
            assert_eq!(contained, true);
        }
    }
}

fn handle_rnd(matches: &ArgMatches) {
    let center_loc = matches.values_of("location").unwrap();
    let center_point = parse_point(center_loc).unwrap();
    let rad_str = matches.value_of("radius").unwrap();
    let rad = rad_str.parse::<f64>().unwrap();
    let random_point = get_random_point(&center_point, rad);

    println!("{},{}", random_point.x(), random_point.y());
}

fn handle_loc(matches: &ArgMatches) {
    let place = matches.value_of("place").unwrap();
    match search(place) {
        Ok(data) => println!("{}", data),
        Err(e) => bail_out(e.description()),
    }
}

fn parse_point(mut loc: Values) -> Result<Point<f64>, Box<Error>> {
    let lon = loc.next().ok_or("Argument parse error")?;
    let lat = loc.next().ok_or("Argument parse error")?;
    let lon_f = lon.parse::<f64>()?;
    let lat_f = lat.parse::<f64>()?;
    Ok(Point::new(lon_f, lat_f))
}

fn direction(point: &Point<f64>, bearing: f64, distance: f64) -> Point<f64> {
    let pi = std::f64::consts::PI;
    let deg_to_rad = pi / 180.;
    let rad_to_deg = 180. / pi;
    let center_lng = deg_to_rad * point.x();
    let center_lat = deg_to_rad * point.y();
    let bearing_rad = deg_to_rad * bearing;

    let rad = distance / EARTH_RADIUS_KM;

    let lat = {
            center_lat.sin() * rad.cos() + center_lat.cos() * rad.sin() * bearing_rad.cos()
        }
        .asin();
    let lng = {
            bearing_rad.sin() * rad.sin() * center_lat.cos()
        }
        .atan2(rad.cos() - center_lat.sin() * lat.sin()) + center_lng;

    Point::new(lng * rad_to_deg, lat * rad_to_deg)
}

#[cfg(test)]
mod direction {
    use super::*;

    #[test]
    fn returns_a_new_point() {
        let point_1 = Point::new(9.177789688110352, 48.776781529534965);
        let point_2 = direction(&point_1, 45., 10.);
        assert_eq!(point_2, Point::new(9.274379723017008, 48.840312896632746));
    }
}

fn handle_dis(matches: &ArgMatches) {
    let loc_1 = matches.values_of("location1").unwrap();
    let point_1 = parse_point(loc_1).unwrap();
    let loc_2 = matches.values_of("location2").unwrap();
    let point_2 = parse_point(loc_2).unwrap();
    let dist = point_1.haversine_distance(&point_2);
    println!("{:.0}", dist);
}

fn main() {
    let app = get_cli_app();
    let matches = app.get_matches();

    match matches.subcommand() {
        ("loc", Some(sub_m)) => handle_loc(sub_m),
        ("rnd", Some(sub_m)) => handle_rnd(sub_m),
        ("rev", Some(sub_m)) => handle_rev(sub_m),
        ("dis", Some(sub_m)) => handle_dis(sub_m),
        _ => bail_out(matches.usage()),
    }
}
<commit_msg>Fixed unused warnings.<commit_after>#[macro_use]
extern crate serde_derive;
extern crate serde;
extern crate serde_json;
extern crate reqwest;
#[macro_use]
extern crate clap;
extern crate geo;
extern crate num_traits;
extern crate rand;

use std::error::Error;
use std::process;
use std::boxed::Box;
use clap::{Arg, App, SubCommand, ArgMatches, Values};
use std::fmt;
use std::fmt::Display;
use reqwest::header::UserAgent;
use std::io::Write;
use geo::Point;
use geo::haversine_distance::HaversineDistance;
use rand::distributions::{IndependentSample, Range};

static NOMINATIM_ENDPOINT: &'static str = "http://nominatim.openstreetmap.org";
const VERSION: &'static str = env!("CARGO_PKG_VERSION");
const NAME: &'static str = env!("CARGO_PKG_NAME");
const EARTH_RADIUS_KM: f64 = 6373.;

#[derive(Deserialize, Debug)]
struct Location {
    lat: String,
    lon: String,
}

impl Display for Location {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{},{}", self.lon, self.lat)
    }
}

#[derive(Deserialize, Debug)]
struct Place {
    display_name: String,
}

impl Display for Place {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", self.display_name)
    }
}

fn get_query_string(params: Vec<(&str, &str)>) -> String {
    let pairs: Vec<String> = params
        .into_iter()
        .map(|(k, v)| format!("{}={}", k, v))
        .collect();
    pairs.join("&")
}

fn reverse_geocode(lon: &str, lat: &str) -> Result<Place, Box<Error>> {
    let params = vec![("format", "json"), ("lon", lon), ("lat", lat)];
    let query_string = get_query_string(params);
    let url = format!("{}/reverse?{}", NOMINATIM_ENDPOINT, query_string);
    let client = reqwest::Client::new()?;
    let mut res = client
        .get(&url)
        .header(UserAgent(format!("{} v{}", NAME, VERSION)))
        .send()?;
    let result = res.json::<Place>()?;
    Ok(result)
}

fn search(place_name: &str) -> Result<Location, Box<Error>> {
    let params = vec![("format", "jsonv2"), ("q", place_name), ("limit", "1")];
    let query_string = get_query_string(params);
    let url = format!("{}/search?{}", NOMINATIM_ENDPOINT, query_string);
    let client = reqwest::Client::new()?;
    let mut res = client
        .get(&url)
        .header(UserAgent(format!("{} v{}", NAME, VERSION)))
        .send()?;
    let mut results = res.json::<Vec<Location>>()?;
    results.reverse();
    let first = results.pop().ok_or("No result found")?;
    Ok(first)
}

fn is_float(x: String) -> Result<(), String> {
    match x.parse::<f64>() {
        Ok(_) => Ok(()),
        Err(_) => Err("Value is not a proper float.".to_string()),
    }
}

fn is_positive_float(x: String) -> Result<(), String> {
    match x.parse::<f64>() {
            Ok(f) => Ok(f),
            Err(_) => Err("Value is not a proper float.".to_string()),
        }
        .and_then(|f| match f < 0. {
                      false => Ok(()),
                      true => Err("Value is not positive.".to_string()),
                  })
}

fn get_loc_arg<'a, 'b>(name: &'a str, short: &str) -> Arg<'a, 'b> {
    Arg::with_name(name)
        .required(true)
        .require_delimiter(true)
        .short(short)
        .long(name)
        .value_name("lon,lat")
        .validator(is_float)
        .number_of_values(2)
        .takes_value(true)
}

fn get_cli_app<'a, 'b>() -> App<'a, 'b> {
    App::new("Location utility")
        .version(crate_version!())
        .author(crate_authors!())
        .subcommand(SubCommand::with_name("loc")
                        .about("Retrieve a location via search string")
                        .arg(Arg::with_name("place")
                                 .required(true)
                                 .short("P")
                                 .long("place")
                                 .value_name("place name")
                                 .number_of_values(1)
                                 .takes_value(true)))
        .subcommand(SubCommand::with_name("rev")
                        .about("Reverse geocode a place via location")
                        .arg(get_loc_arg("location", "L")))
        .subcommand(SubCommand::with_name("rnd")
                        .about("Get a random location within a km radius")
                        .arg(get_loc_arg("location", "L"))
                        .arg(Arg::with_name("radius")
                                 .required(true)
                                 .short("R")
                                 .long("radius")
                                 .value_name("radius in km")
                                 .validator(is_positive_float)
                                 .number_of_values(1)
                                 .takes_value(true)))
        .subcommand(SubCommand::with_name("dis")
                        .about("Return the distance between two points in meters")
                        .arg(get_loc_arg("location1", "1"))
                        .arg(get_loc_arg("location2", "2")))
}

fn bail_out(message: &str) {
    writeln!(&mut std::io::stderr(), "{}", message).expect("could not write to stderr");
    process::exit(1);
}

fn handle_rev(matches: &ArgMatches) {
    let mut parts: Vec<&str> = matches.values_of("location").unwrap().collect();
    let lat = parts.pop().unwrap();
    let lon = parts.pop().unwrap();
    match reverse_geocode(lon, lat) {
        Ok(data) => println!("{}", data),
        Err(e) => bail_out(e.description()),
    }
}

fn get_random_point(center: &Point<f64>, radius: f64) -> Point<f64> {
    let mut rng = rand::thread_rng();
    let dist_range = Range::new(0., radius);
    let distance = dist_range.ind_sample(&mut rng);
    let bearing_range = Range::new(0., 360.);
    let bearing = bearing_range.ind_sample(&mut rng);

    direction(center, bearing, distance)
}

#[cfg(test)]
mod get_random_point {
    use super::*;
    use geo::contains::Contains;
    use geo::{LineString, Polygon};
    use std::fs::File;

    #[derive(Deserialize, Debug)]
    struct TestCoordinates(f64, f64);

    #[test]
    fn stays_within_the_given_radius() {
        let file = File::open("test/circle.json").unwrap();
        let coordinates: Vec<TestCoordinates> = serde_json::from_reader(file).unwrap();
        let points: Vec<Point<f64>> = coordinates
            .into_iter()
            .map(|t| Point::new(t.0, t.1))
            .collect();
        let exterior = LineString(points);
        let polygon = Polygon::new(exterior, vec![]);

        let center_point = Point::new(9.177789688110352, 48.776781529534965);
        for _ in 0..1000 {
            let random_point = get_random_point(&center_point, 9.9);
            let contained = polygon.contains(&random_point);
            assert_eq!(contained, true);
        }
    }
}

fn handle_rnd(matches: &ArgMatches) {
    let center_loc = matches.values_of("location").unwrap();
    let center_point = parse_point(center_loc).unwrap();
    let rad_str = matches.value_of("radius").unwrap();
    let rad = rad_str.parse::<f64>().unwrap();
    let random_point = get_random_point(&center_point, rad);

    println!("{},{}", random_point.x(), random_point.y());
}

fn handle_loc(matches: &ArgMatches) {
    let place = matches.value_of("place").unwrap();
    match search(place) {
        Ok(data) => println!("{}", data),
        Err(e) => bail_out(e.description()),
    }
}

fn parse_point(mut loc: Values) -> Result<Point<f64>, Box<Error>> {
    let lon = loc.next().ok_or("Argument parse error")?;
    let lat = loc.next().ok_or("Argument parse error")?;
    let lon_f = lon.parse::<f64>()?;
    let lat_f = lat.parse::<f64>()?;
    Ok(Point::new(lon_f, lat_f))
}

fn direction(point: &Point<f64>, bearing: f64, distance: f64) -> Point<f64> {
    let pi = std::f64::consts::PI;
    let deg_to_rad = pi / 180.;
    let rad_to_deg = 180. / pi;
    let center_lng = deg_to_rad * point.x();
    let center_lat = deg_to_rad * point.y();
    let bearing_rad = deg_to_rad * bearing;

    let rad = distance / EARTH_RADIUS_KM;

    let lat = {
            center_lat.sin() * rad.cos() + center_lat.cos() * rad.sin() * bearing_rad.cos()
        }
        .asin();
    let lng = {
            bearing_rad.sin() * rad.sin() * center_lat.cos()
        }
        .atan2(rad.cos() - center_lat.sin() * lat.sin()) + center_lng;

    Point::new(lng * rad_to_deg, lat * rad_to_deg)
}

#[cfg(test)]
mod direction {
    use super::*;

    #[test]
    fn returns_a_new_point() {
        let point_1 = Point::new(9.177789688110352, 48.776781529534965);
        let point_2 = direction(&point_1, 45., 10.);
        assert_eq!(point_2, Point::new(9.274379723017008, 48.840312896632746));
    }
}

fn handle_dis(matches: &ArgMatches) {
    let loc_1 = matches.values_of("location1").unwrap();
    let point_1 = parse_point(loc_1).unwrap();
    let loc_2 = matches.values_of("location2").unwrap();
    let point_2 = parse_point(loc_2).unwrap();
    let dist = point_1.haversine_distance(&point_2);
    println!("{:.0}", dist);
}

fn main() {
    let app = get_cli_app();
    let matches = app.get_matches();

    match matches.subcommand() {
        ("loc", Some(sub_m)) => handle_loc(sub_m),
        ("rnd", Some(sub_m)) => handle_rnd(sub_m),
        ("rev", Some(sub_m)) => handle_rev(sub_m),
        ("dis", Some(sub_m)) => handle_dis(sub_m),
        _ => bail_out(matches.usage()),
    }
}
<|endoftext|>


<commit_before>extern crate chrono;

use std::fmt;
use std::process::Command;
use std::str;
// use chrono::datetime::DateTime;
// use chrono::Local;
// use chrono::offset::TimeZone;
use chrono::*;

struct Branch {
    user: String,
    branch: String,
    date_string: String,
    date: DateTime<Local>,
}

impl fmt::Display for Branch {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f,
               "user: {}\nbranch: {}\ndate string: {}\ndate: {}\n",
               self.user,
               self.branch,
               self.date_string,
               self.date)
    }
}

fn parse_repo(line: String) -> Option<Branch> {
    if line.contains("refs/remotes/origin/") && line != "refs/remotes/origin/HEAD" &&
       line != "refs/remotes/origin/master" {

        let mut user = String::new();
        let mut date = String::new();
        let mut branch = String::new();
        for (i, part) in line.split(",").enumerate() {
            match i {
                0 => user = part.to_string(),
                1 => branch = part.to_string(),
                2 => date = part.to_string(),
                _ => {}
            }
        }
        Some(Branch {
            user: user,
            branch: branch,
            date_string: date,
            // date: Local.ymd(2016, 2, 1),
            date: DateTime<Local>::parse_from_str(date),
        })
    } else {
        None
    }
}

fn main() {

    let v = get_git_data();

    for branch in v {
        println!("[branch]\n{}", branch);
    }
}

fn get_git_data() -> Vec<Branch> {
    let output = Command::new("git")
        .arg("for-each-ref")
        .arg("--format=%(authorname),%(refname),%(committerdate)")
        .output()
        .expect("failed to execute process");
    let git_output = str::from_utf8(&output.stdout).unwrap();

    let mut v: Vec<Branch> = Vec::new();
    for line in git_output.lines() {
        parse_repo(line.to_string()).map(|x| v.push(x));
    }
    v
}
<commit_msg>dates parsing and branches filtering<commit_after>extern crate chrono;

use std::fmt;
use std::process::Command;
use std::str;
// use chrono::datetime::DateTime;
// use chrono::Local;
// use chrono::offset::TimeZone;
use chrono::*;

struct Branch {
    user: String,
    branch: String,
    date: DateTime<FixedOffset>,
}

impl fmt::Display for Branch {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f,
               "user: {}\nbranch: {}\ndate: {}",
               self.user,
               self.branch,
               self.date)
    }
}

fn parse_repo(line: String) -> Option<Branch> {
    if !line.contains("refs/remotes/origin/HEAD") && !line.contains("refs/remotes/origin/master") {

        let mut user = String::new();
        let mut date = String::new();
        let mut branch = String::new();
        for (i, part) in line.split("^").enumerate() {
            match i {
                0 => user = part.to_string(),
                1 => branch = part.to_string(),
                2 => date = part.to_string(),
                _ => {}
            }
        }
        Some(Branch {
            user: user,
            branch: branch,
            date: DateTime::parse_from_rfc2822(&date).unwrap(),
        })
    } else {
        None
    }
}

fn main() {

    let v = get_git_data();

    for branch in v {
        println!("[branch]\n{}", branch);
    }
}

fn get_git_data() -> Vec<Branch> {
    let output = Command::new("git")
        .arg("for-each-ref")
        .arg("--sort=-committerdate")
        .arg("--format=%(authorname)^%(refname)^%(committerdate:rfc2822)")
        .arg("refs/remotes/origin/")
        .output()
        .expect("failed to execute process");
    let git_output = str::from_utf8(&output.stdout).unwrap();

    let mut v: Vec<Branch> = Vec::new();
    for line in git_output.lines() {
        parse_repo(line.to_string()).map(|x| v.push(x));
    }
    v
}
<|endoftext|>


<commit_before>// UDP stuff adapted from http://illegalargumentexception.blogspot.co.uk/2015/05/rust-send-and-receive-on-localhost-with.html
use std::net;
#[macro_use]
extern crate clap;
use clap::{Arg, App};

fn socket(listen_on: net::SocketAddr) -> net::UdpSocket {
    let attempt = net::UdpSocket::bind(listen_on);
    let socket;
    match attempt {
        Ok(sock) => {
            socket = sock;
        }
        Err(err) => panic!("Could not bind: {}", err),
    }
    socket
}

fn send_message(send_addr: net::SocketAddr, target: net::SocketAddr, data: &[u8]) {
    let _ = socket(send_addr).send_to(data, target);
}

fn main() {
    let command_vals = ["white",
                        "red",
                        "orange",
                        "yellow",
                        "green",
                        "cyan",
                        "blue",
                        "purple",
                        "black",
                        "question",
                        "exclamation",
                        "quit"];
    let matches = App::new("rust_anybar")
        .version(&crate_version!()[..])
        .author("Stephan Hügel <urschrei@gmail.com>")
        .about("A Rust command-line client for Anybar")
        .args_from_usage("-p --port=[PORT] 'Set destination UDP port. Input must be 0 – 65535, \
                          and defaults to 17388")
        .arg(Arg::with_name("COMMAND")
            .help("The command you wish to send to Anybar")
            .index(1)
            .possible_values(&command_vals)
            .required(true))
        .get_matches();
    // bind to the correct UDP port
    let numeric_port = value_t!(matches.value_of("PORT"), u16).unwrap_or(1738);
    // it's safe to unwrap here, cos we already checked success
    let to_send = matches.value_of("COMMAND").unwrap();
    let ip = net::Ipv4Addr::new(127, 0, 0, 1);
    let listen_addr = net::SocketAddrV4::new(ip, numeric_port);
    let send_addr = net::SocketAddrV4::new(ip, 0);
    // and send our message
    send_message(net::SocketAddr::V4(send_addr),
                 net::SocketAddr::V4(listen_addr),
                 to_send.as_bytes());
}
<commit_msg>rename command params<commit_after>// UDP stuff adapted from http://illegalargumentexception.blogspot.co.uk/2015/05/rust-send-and-receive-on-localhost-with.html
use std::net;
#[macro_use]
extern crate clap;
use clap::{Arg, App};

fn socket(listen_on: net::SocketAddr) -> net::UdpSocket {
    let attempt = net::UdpSocket::bind(listen_on);
    let socket;
    match attempt {
        Ok(sock) => {
            socket = sock;
        }
        Err(err) => panic!("Could not bind: {}", err),
    }
    socket
}

fn send_message(send_addr: net::SocketAddr, target: net::SocketAddr, data: &[u8]) {
    let _ = socket(send_addr).send_to(data, target);
}

fn main() {
    let command_vals = ["white",
                        "red",
                        "orange",
                        "yellow",
                        "green",
                        "cyan",
                        "blue",
                        "purple",
                        "black",
                        "question",
                        "exclamation",
                        "quit"];
    let command_params = App::new("rust_anybar")
        .version(&crate_version!()[..])
        .author("Stephan Hügel <urschrei@gmail.com>")
        .about("A Rust command-line client for Anybar")
        .args_from_usage("-p --port=[PORT] 'Set destination UDP port. Input must be 0 – 65535, \
                          and defaults to 17388")
        .arg(Arg::with_name("COMMAND")
            .help("The command you wish to send to Anybar")
            .index(1)
            .possible_values(&command_vals)
            .required(true))
        .get_matches();
    // bind to the correct UDP port
    let numeric_port = value_t!(command_params.value_of("PORT"), u16).unwrap_or(1738);
    // it's safe to unwrap here, cos we already checked success
    let to_send = command_params.value_of("COMMAND").unwrap();
    let ip = net::Ipv4Addr::new(127, 0, 0, 1);
    let listen_addr = net::SocketAddrV4::new(ip, numeric_port);
    let send_addr = net::SocketAddrV4::new(ip, 0);
    // and send our message
    send_message(net::SocketAddr::V4(send_addr),
                 net::SocketAddr::V4(listen_addr),
                 to_send.as_bytes());
}
<|endoftext|>


<commit_before>extern crate docopt;
extern crate rustc_serialize;
extern crate random_choice;

use docopt::Docopt;
use std::io::{BufRead,BufReader};
use std::fs::File;
use std::collections::{HashSet, HashMap};
use std::cmp::max;
use std::io::Write;
use self::random_choice::random_choice;

const USAGE: &'static str ="
Gibbs sampling algorithm for a Dirichlet Mixture Model of Yin and Wang 2014.

Usage:
  gsdmm <datafile> <vocabfile> <labelout> <clusterout> [-k <max_clusters>] [-a <alpha>] [-b <beta>] [-m <maxit>]
  gsdmm (-h | --help)
  gsdmm --version

Options:
  -h --help             Show this screen.
  --version             Show version.
  -k=<K>                Upper bound on the number of possible clusters. [default: 8]
  -a --alpha=<alpha>    Alpha controls the probability that a student will join a table that is currently empty
                        When alpha is 0, no one will join an empty table. [default: 0.1]
  -b --beta=<beta>      Beta controls the student's affinity for other students with similar interests. A low beta means
                        that students desire to sit with students of similar interests. A high beta means they are less
                        concerned with affinity and are more influenced by the popularity of a table. [default: 0.1]
  -m --maxit=<m>        Maximum number of iterations. [default: 30]

";

#[derive(Debug, RustcDecodable)]
struct Args {
    //    flag_mode: isize,
    arg_datafile: String,
    arg_vocabfile: String,
    arg_labelout: String,
    arg_clusterout: String,
    flag_k: isize,
    flag_alpha: f64,
    flag_beta: f64,
    flag_maxit: isize
}

fn main() {

    let args: Args = Docopt::new(USAGE)
        .and_then(|d| d.decode())
        .unwrap_or_else(|e| e.exit());

    // get the data and vocabulary
    let vocab:HashSet<String> = lines_from_file(&args.arg_vocabfile).into_iter().collect();
    let docs:Vec<Vec<String>> = lines_from_file(&args.arg_datafile).into_iter().map(|line| {
        line.to_owned().split_whitespace().map(|s| s.to_owned()).filter(|s| (&vocab).contains(s)).collect::<Vec<String>>()
    }).collect::<Vec<Vec<String>>>();

    // setup algorithm containers
    let alpha = args.flag_alpha;
    let beta = args.flag_beta;
    let K = args.flag_k as usize;
    let V = vocab.len() as f64;
    let D = docs.len();
    println!("Fitting with alpha={}, beta={}, K={}, maxit={}.", alpha, beta, K, args.flag_maxit);

    let labels = (0_usize..K).collect::<Vec<usize>>();
    let mut d_z:Vec<usize>  = (0_usize..D).map(|_| 0_usize).collect::<Vec<usize>>(); // doc labels
    let mut m_z:Vec<u32>  = zero_vector(K);  // cluster sizes
    let mut n_z:Vec<u32>  = zero_vector(K);  // cluster word counts
    let mut n_z_w = Vec::<HashMap<String,u32>>::with_capacity(K);  // container for cluster word distributions
    for _ in 0_usize..K {
        let mut m = HashMap::<String,u32>::with_capacity(max(vocab.len()/10, 100));
        &n_z_w.push(m);
    }

    // randomly initialize cluster assignment
    let mut p = (0..K).map(|_| 1_f64/(K as f64)).collect::<Vec<f64>>();
    let choices = random_choice().random_choice_f64(&labels, &p, D);
    for i in 0..D {
        let z = choices[i].clone();
        let ref doc = docs[i];
        d_z[i] = z;
        m_z[z] += 1;
        n_z[z] += doc.len() as u32;
        let ref mut clust_words:HashMap<String,u32> = n_z_w[z];
        for word in doc {
            if !clust_words.contains_key(word) {
                clust_words.insert(word.clone(), 0_u32);
            }
                *clust_words.get_mut(word).unwrap() += 1_u32;
        }
    }

    let mut number_clusters = K;
    for it in 0..args.flag_maxit {
        let mut total_transfers = 0;
        for i in 0..D {
            let ref doc = docs[i];
            let doc_size = doc.len() as u32;

            // remove the doc from its current cluster
            let z_old = d_z[i];
            m_z[z_old] -= 1;
            n_z[z_old] -= doc_size;

            // modify the map: enclose it in a block so we can borrow views again.
            {
                let ref mut old_clust_words: HashMap<String, u32> = n_z_w[z_old];
                for word in doc {
                    *old_clust_words.get_mut(word).unwrap() -= 1_u32;

                    // compact dictionary once a key is exausted.
                    if old_clust_words[word] == 0_u32 {
                        old_clust_words.remove(word);
                    }
                }
            }

            // update the probability vector
            let lD1 = ((D - 1) as f64 + (K as f64) * alpha).ln();
            for label in 0_usize..K {
                let N1 = m_z[label] as f64 + alpha;
                let lN1 = if N1 > 0_f64 { N1.ln() } else { 0_f64 };
                let mut lN2 = 0_f64;
                let mut lD2 = 0_f64;

                let ref cluster: HashMap<String, u32> = n_z_w[label];

                for word in doc {
                    lN2 += *cluster.get(word).unwrap_or(&0_u32) as f64 + beta;
                }
                for j in 0_u32..doc_size {
                    lD2 += (n_z[label] + j) as f64 - 1_f64 + V * beta;
                }
                lN2 = if lN2 > 0_f64 { lN2.ln() } else { 0_f64 };
                lD2 = if lD2 > 0_f64 { lD2.ln() } else { 0_f64 };
                p[label] = (lN1 - lD1 + lN2 - lD2).exp();
            }
            let pnorm: f64 = p.iter().sum();
            for label in 0_usize..K {
                p[label] = p[label] / pnorm;
            }
            // choose the next cluster randomly according to the computed probability
            let z_new: usize = random_choice().random_choice_f64(&labels, &p, 1)[0].clone();

            // transfer document to the new cluster
            if z_new != z_old {
                total_transfers += 1;
            }
            d_z[i] = z_new;
            m_z[z_new] += 1_u32;
            n_z[z_new] += doc_size;

            {
                let ref mut new_clust_words: HashMap<String, u32> = n_z_w[z_new];
                for word in doc {
                    if !new_clust_words.contains_key(word) {
                        new_clust_words.insert(word.clone(), 0_u32);
                    }
                        * new_clust_words.get_mut(word).unwrap() += 1_u32;
                }
            }
        }
        number_clusters = n_z_w.iter().map(|c| if c.len()>0 {1} else {0} ).sum();
        if (it % 100 == 0) {
            println!("Iteration {}: {} docs transferred with {} clusters populated.", it, total_transfers, number_clusters);
        }
    }

    // write the labels
    {
        let error_msg = format!("Could not write file!");
        let mut f = File::create(&args.arg_labelout).expect(&error_msg);
        f.write_all(d_z.iter().map(|label| label.to_string()).collect::<Vec<String>>().join("\n").as_bytes());
    }

    // write the cluster descriptions
    {
        let error_msg = format!("Could not write file!");
        let mut f = File::create(&args.arg_clusterout).expect(&error_msg);
        for k in 0..K {
            let ref word_dist = n_z_w[k];
            let mut line = k.to_string() + " ";
            let mut dist_counts:Vec<String> = word_dist.iter().map(|(a,b)| a.clone() + ":" + &b.clone().to_string() ).collect();
            dist_counts.sort();
            line += &dist_counts.join(" ");
            f.write((line+"\n").as_bytes());
        }
    }

    fn lines_from_file(filename: &str) -> Vec<String>
    {
        let error_msg = format!("Could not read file {}!", filename);
        let file = File::open(filename).expect(&error_msg);
        let buf = BufReader::new(file);
        buf.lines().map(|l| l.expect("Could not parse line!")).collect()
    }

    fn zero_vector(size:usize) -> Vec<u32>
    {
        let mut v = Vec::<u32>::with_capacity(size);
        for _ in 0_usize..size {
            v.push(0_u32)
        }
        v
    }
}
<commit_msg>fixed major computational bug :)<commit_after>extern crate docopt;
extern crate rustc_serialize;
extern crate random_choice;

use docopt::Docopt;
use std::io::{BufRead,BufReader};
use std::fs::File;
use std::collections::{HashSet, HashMap};
use std::cmp::max;
use std::io::Write;
use self::random_choice::random_choice;

const USAGE: &'static str ="
Gibbs sampling algorithm for a Dirichlet Mixture Model of Yin and Wang 2014.

Usage:
  gsdmm <datafile> <vocabfile> <labelout> <clusterout> [-k <max_clusters>] [-a <alpha>] [-b <beta>] [-m <maxit>]
  gsdmm (-h | --help)
  gsdmm --version

Options:
  -h --help             Show this screen.
  --version             Show version.
  -k=<K>                Upper bound on the number of possible clusters. [default: 8]
  -a --alpha=<alpha>    Alpha controls the probability that a student will join a table that is currently empty
                        When alpha is 0, no one will join an empty table. [default: 0.1]
  -b --beta=<beta>      Beta controls the student's affinity for other students with similar interests. A low beta means
                        that students desire to sit with students of similar interests. A high beta means they are less
                        concerned with affinity and are more influenced by the popularity of a table. [default: 0.1]
  -m --maxit=<m>        Maximum number of iterations. [default: 30]

";

#[derive(Debug, RustcDecodable)]
struct Args {
    //    flag_mode: isize,
    arg_datafile: String,
    arg_vocabfile: String,
    arg_labelout: String,
    arg_clusterout: String,
    flag_k: isize,
    flag_alpha: f64,
    flag_beta: f64,
    flag_maxit: isize
}

fn main() {

    let args: Args = Docopt::new(USAGE)
        .and_then(|d| d.decode())
        .unwrap_or_else(|e| e.exit());

    // get the data and vocabulary
    let vocab:HashSet<String> = lines_from_file(&args.arg_vocabfile).into_iter().collect();
    let docs:Vec<Vec<String>> = lines_from_file(&args.arg_datafile).into_iter().map(|line| {
        line.to_owned().split_whitespace().map(|s| s.to_owned()).filter(|s| (&vocab).contains(s)).collect::<Vec<String>>()
    }).collect::<Vec<Vec<String>>>();

    // setup algorithm containers
    let alpha = args.flag_alpha;
    let beta = args.flag_beta;
    let K = args.flag_k as usize;
    let D = docs.len();

    // compute utilized vocabulary size.
    let mut utilized_vocab = HashSet::with_capacity(vocab.len());
    for doc in &docs {
        for word in doc {
            utilized_vocab.insert(word.clone());
        }
    }
    let V = utilized_vocab.len() as f64;
    println!("Fitting with alpha={}, beta={}, K={}, maxit={}, vocab size={}", alpha, beta, K, args.flag_maxit, V as u32);

    let labels = (0_usize..K).collect::<Vec<usize>>();
    let mut d_z:Vec<usize>  = (0_usize..D).map(|_| 0_usize).collect::<Vec<usize>>(); // doc labels
    let mut m_z:Vec<u32>  = zero_vector(K);  // cluster sizes
    let mut n_z:Vec<u32>  = zero_vector(K);  // cluster word counts
    let mut n_z_w = Vec::<HashMap<String,u32>>::with_capacity(K);  // container for cluster word distributions
    for _ in 0_usize..K {
        let mut m = HashMap::<String,u32>::with_capacity(max(vocab.len()/10, 100));
        &n_z_w.push(m);
    }

    // randomly initialize cluster assignment
    let mut p = (0..K).map(|_| 1_f64/(K as f64)).collect::<Vec<f64>>();
    let choices = random_choice().random_choice_f64(&labels, &p, D);
    for i in 0..D {
        let z = choices[i].clone();
        let ref doc = docs[i];
        d_z[i] = z;
        m_z[z] += 1;
        n_z[z] += doc.len() as u32;
        let ref mut clust_words:HashMap<String,u32> = n_z_w[z];
        for word in doc {
            if !clust_words.contains_key(word) {
                clust_words.insert(word.clone(), 0_u32);
            }
                *clust_words.get_mut(word).unwrap() += 1_u32;
        }
    }

    let mut number_clusters = K;
    for it in 0..args.flag_maxit {
        let mut total_transfers = 0;
        for i in 0..D {
            let ref doc = docs[i];
            let doc_size = doc.len() as u32;

            // remove the doc from its current cluster
            let z_old = d_z[i];
            m_z[z_old] -= 1;
            n_z[z_old] -= doc_size;

            // modify the map: enclose it in a block so we can borrow views again.
            {
                let ref mut old_clust_words: HashMap<String, u32> = n_z_w[z_old];
                for word in doc {
                    *old_clust_words.get_mut(word).unwrap() -= 1_u32;

                    // compact dictionary once a key is exausted.
                    if old_clust_words[word] == 0_u32 {
                        old_clust_words.remove(word);
                    }
                }
            }

            // update the probability vector
            let lD1 = ((D - 1) as f64 + (K as f64) * alpha).ln();
            for label in 0_usize..K {
                let N1 = m_z[label] as f64 + alpha;
                let lN1 = if N1 > 0_f64 { N1.ln() } else { 0_f64 };
                let mut lN2 = 0_f64;
                let mut lD2 = 0_f64;

                let ref cluster: HashMap<String, u32> = n_z_w[label];

                for word in doc {
                    lN2 += (*cluster.get(word).unwrap_or(&0_u32) as f64 + beta).ln();
                }
                for j in 1_u32..(doc_size+1) {
                    lD2 += ((n_z[label] + j) as f64 - 1_f64 + V * beta).ln();
                }
                p[label] = (lN1 - lD1 + lN2 - lD2).exp();
            }
            let pnorm: f64 = p.iter().sum();
            for label in 0_usize..K {
                p[label] = p[label] / pnorm;
            }

            // choose the next cluster randomly according to the computed probability
            let z_new: usize = random_choice().random_choice_f64(&labels, &p, 1)[0].clone();

            // transfer document to the new cluster
            if z_new != z_old {
                total_transfers += 1;
            }
            d_z[i] = z_new;
            m_z[z_new] += 1_u32;
            n_z[z_new] += doc_size;

            {
                let ref mut new_clust_words: HashMap<String, u32> = n_z_w[z_new];
                for word in doc {
                    if !new_clust_words.contains_key(word) {
                        new_clust_words.insert(word.clone(), 0_u32);
                    }
                        * new_clust_words.get_mut(word).unwrap() += 1_u32;
                }
            }
        }
        let new_number_clusters = n_z_w.iter().map(|c| if c.len()>0 {1} else {0} ).sum();
        println!("Iteration {}: {} docs transferred with {} clusters populated.", it, total_transfers, new_number_clusters);

        // apply ad-hoc convergence test
        if (total_transfers==0 && new_number_clusters==number_clusters) {
            println!("Converged after {} iterations. Solution has {} clusters.", it, new_number_clusters);
            break
        }
        number_clusters = new_number_clusters;
    }

    // write the labels
    {
        let error_msg = format!("Could not write file!");
        let mut f = File::create(&args.arg_labelout).expect(&error_msg);
        f.write_all(d_z.iter().map(|label| label.to_string()).collect::<Vec<String>>().join("\n").as_bytes());
    }

    // write the cluster descriptions
    {
        let error_msg = format!("Could not write file!");
        let mut f = File::create(&args.arg_clusterout).expect(&error_msg);
        for k in 0..K {
            let ref word_dist = n_z_w[k];
            let mut line = k.to_string() + " ";
            let mut dist_counts:Vec<String> = word_dist.iter().map(|(a,b)| a.clone() + ":" + &b.clone().to_string() ).collect();
            dist_counts.sort();
            line += &dist_counts.join(" ");
            f.write((line+"\n").as_bytes());
        }
    }

    fn lines_from_file(filename: &str) -> Vec<String>
    {
        let error_msg = format!("Could not read file {}!", filename);
        let file = File::open(filename).expect(&error_msg);
        let buf = BufReader::new(file);
        buf.lines().map(|l| l.expect("Could not parse line!")).collect()
    }

    fn zero_vector(size:usize) -> Vec<u32>
    {
        let mut v = Vec::<u32>::with_capacity(size);
        for _ in 0_usize..size {
            v.push(0_u32)
        }
        v
    }
}
<|endoftext|>


<commit_before>extern crate tempdir;
extern crate hyper;
extern crate grnenvlib;
extern crate clap;

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

fn main() {
    default_main();
}

fn cli() -> App<'static, 'static> {
    App::new("grnenv-rs")
        .version("0.1.0")
        .author("Hiroshi Hatake <cosmo0920.wp@gmail.com>")
        .about("A tiny tool for obtain and select multiple Groonga.")
        .subcommand(SubCommand::with_name("init").about("Prepare grnenv-rs."))
        .subcommand(SubCommand::with_name("install")
            .about("Install a given Groonga version and arch")
            .arg(Arg::with_name("VERSION").required(true))
            .arg(Arg::with_name("arch")
                .short("a")
                .long("arch")
                .value_name("ARCH")
                .help("Select architectures. e.g.) x86, x64")
                .takes_value(true)))
        .subcommand(SubCommand::with_name("switch")
            .about("switch Groonga with given version and arch")
            .arg(Arg::with_name("VERSION").required(true))
            .arg(Arg::with_name("arch")
                .short("a")
                .long("arch")
                .value_name("ARCH")
                .help("Select architectures. e.g.) x86, x64")
                .takes_value(true)))
        .subcommand(SubCommand::with_name("versions").about("display installed Groonga versions"))
}

#[cfg(not(windows))]
fn default_main() {
    panic!("Unsupported!");
}

#[cfg(windows)]
fn default_main() {
    let matches = cli().get_matches();
    match matches.subcommand() {
        ("init", _) => command::windows::init(),
        ("install", Some(m)) => command::windows::install(m),
        ("switch", Some(m)) => command::windows::switch(m),
        ("versions", _) => command::windows::versions(),
        (_, _) => unreachable!(),
    }
}
<commit_msg>Capitalize<commit_after>extern crate tempdir;
extern crate hyper;
extern crate grnenvlib;
extern crate clap;

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

fn main() {
    default_main();
}

fn cli() -> App<'static, 'static> {
    App::new("grnenv-rs")
        .version("0.1.0")
        .author("Hiroshi Hatake <cosmo0920.wp@gmail.com>")
        .about("A tiny tool for obtain and select multiple Groonga.")
        .subcommand(SubCommand::with_name("init").about("Prepare grnenv-rs."))
        .subcommand(SubCommand::with_name("install")
            .about("Install a given Groonga version and arch")
            .arg(Arg::with_name("VERSION").required(true))
            .arg(Arg::with_name("arch")
                .short("a")
                .long("arch")
                .value_name("ARCH")
                .help("Select architectures. e.g.) x86, x64")
                .takes_value(true)))
        .subcommand(SubCommand::with_name("switch")
            .about("Switch Groonga with given version and arch")
            .arg(Arg::with_name("VERSION").required(true))
            .arg(Arg::with_name("arch")
                .short("a")
                .long("arch")
                .value_name("ARCH")
                .help("Select architectures. e.g.) x86, x64")
                .takes_value(true)))
        .subcommand(SubCommand::with_name("versions").about("Display installed Groonga versions"))
}

#[cfg(not(windows))]
fn default_main() {
    panic!("Unsupported!");
}

#[cfg(windows)]
fn default_main() {
    let matches = cli().get_matches();
    match matches.subcommand() {
        ("init", _) => command::windows::init(),
        ("install", Some(m)) => command::windows::install(m),
        ("switch", Some(m)) => command::windows::switch(m),
        ("versions", _) => command::windows::versions(),
        (_, _) => unreachable!(),
    }
}
<|endoftext|>


<commit_before>extern crate clap;
extern crate git2;

use git2::Repository;
use clap::{Arg, App};

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);
        },
    };
}
<commit_msg>Sketch out an implementation plan<commit_after>extern crate clap;
extern crate git2;

use git2::Repository;
use clap::{Arg, App};

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
    // 2. Fetch submodule's history
    // 3. Check out submodule's branch under some unique name (UUID?)
    // 4. Rewrite submodule branch's history, moving everything under a single directory named
    //    after the submodule
    // 5. Run through main branch's history and note down commit IDs where submodule was touched,
    //    along with submodule's commit ID
    // 6. Run through old and new submodule's history (in lockstep) and note down new commit IDs of
    //    the commits that were referenced in the main repo
    // 7. Remove submodule's remote
    // 8. Run through master's history, doing two things:
    //      8.1 updating the tree to contain the relevant tree from submodule
    //      8.2 in commits that used to update the submodule, add a parent pointing to appropriate
    //          commit in new submodule history
}
<|endoftext|>


<commit_before>#![feature(core)]
#![feature(path)]

extern crate git2;
extern crate "rustc-serialize" as rustc_serialize;
extern crate docopt;

use git2::{Repository, Branch, BranchType, DiffLine,
    Commit, Diff, DiffFormat, DiffDelta, DiffHunk, Error, Oid};
use docopt::Docopt;
use std::old_io as io;
use std::old_io::BufferedReader;
use std::old_io::File;
use std::str;

// Write the Docopt usage string.
static USAGE: &'static str = "
Usage: transit <repo> [<old> <new>]

If no commits are given, transit will revwalk from latest to oldest.
";

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

fn main() {
    // Parse the args above or die.
    let args: Args = Docopt::new(USAGE)
        .and_then(|d| d.decode())
        .unwrap_or_else(|e| e.exit());
    // Pull up the revwalk.
    let path = Path::new(args.arg_repo);
    let repo = Repository::discover(&path)
        .ok().expect("Unable to find repo.");
    // If we can destructures the two optional args into real things.
    if let (Some(old_string), Some(new_string)) = (args.arg_old, args.arg_new) {
        // Compare a specific commit pair.
        // Fist, get the commits. (Error checked)
        let old = Oid::from_str(&old_string[]).and_then(|oid| repo.find_commit(oid));
        let new = Oid::from_str(&new_string[]).and_then(|oid| repo.find_commit(oid));
        if old.is_ok() && new.is_ok() {
            find_moves(&repo, &old.unwrap(), &new.unwrap()).unwrap();
        } else {
            panic!("Commit ids were not valid.");
        }
    } else {
        // Revwalk.
        let mut revwalk = repo.revwalk()
            .ok().expect("Unable to get revwalk.");
        // Setup some options.
        revwalk.simplify_first_parent(); // TODO: Maybe remove?
        let mut flags = git2::Sort::empty();
        flags.insert(git2::SORT_TIME);
        flags.insert(git2::SORT_TOPOLOGICAL);
        revwalk.set_sorting(flags);
        // Push HEAD to the revwalk.
        revwalk.push_head()
            .ok().expect("Unable to push HEAD.");
        // We sadly must collect here to use `.windows()`
        let history = revwalk.filter_map(|id| repo.find_commit(id).ok())
            .collect::<Vec<Commit>>();
        // Walk through each pair of commits.
        for pair in history.windows(2) {
            let (old, new) = (&pair[1], &pair[0]);
            find_moves(&repo, old.clone(), new.clone()).unwrap();
        }
    }

fn find_moves(repo: &Repository, old: &Commit, new: &Commit) -> Result<Vec<Output>, Error> {
        let old_tree = try!(old.tree());
        let new_tree = try!(new.tree());
        // Build up a diff of the two trees.
        let diff = try!(Diff::tree_to_tree(repo, Some(&old_tree), Some(&new_tree), None));
        // State. TODO: Don't just use a u64.
        let moved = 0;
        let mut current_hunk: Vec<String> = Vec::new();
        // Read about this function in http://alexcrichton.com/git2-rs/git2/struct.Diff.html#method.print
        // It's a bit weird, but I think it will provide the necessary information.
        diff.print(DiffFormat::Patch, |delta, maybe_hunk, line| -> bool {
            // Thinking:
            //  * If is not a hunk, keep going.
            //  * If it's a hunk, do regex magic.
            //  * Stick regex output into a hashmap as a hash.
            //  * Later, we will iterate through and output pased on the values.
            // Filter out all the boring context lines.
            // If we're not interested in this line just return since it will iterate to the next.
            if maybe_hunk.is_none() { return true }; // Return early.
            match line.origin() {
                // Context
                ' ' | '=' => true,
                // Headers
                'F' | 'H' => true,
                // Additions
                '+' | '>' => {
                    println!("+ {}", str::from_utf8(line.content()).unwrap());
                    true
                },
                // Deletions
                '-' | '<' => {
                    println!("- {}", str::from_utf8(line.content()).unwrap());
                    true
                },
                // Other (We don't care about these.)
                _         => true,
            }
                // Look at that match statement, what a hunk. So hunky.
                // match maybe_hunk {
                //     Some(hunk) => unimplemented!(),
                //     None => unimplemented!(),
                // }
        });
        Ok(vec![Output {
            old_commit: old.id(),
            new_commit: new.id(),
            origin_line: 0,
            destination_line: 0,
            num_lines: 0
        }])
    }
}

struct Output {
    old_commit: Oid,
    new_commit: Oid,
    origin_line: u32,
    destination_line: u32,
    num_lines: u32,
}
<commit_msg>added make_output function; fixed find_moves function being declared inside of main();   added derive[Show] for struct Output<commit_after>#![feature(core)]
#![feature(path)]

extern crate git2;
extern crate "rustc-serialize" as rustc_serialize;
extern crate docopt;

use git2::{Repository, Branch, BranchType, DiffLine,
    Commit, Diff, DiffFormat, DiffDelta, DiffHunk, Error, Oid};
use docopt::Docopt;
use std::old_io as io;
use std::old_io::BufferedReader;
use std::old_io::File;
use std::str;

// Write the Docopt usage string.
static USAGE: &'static str = "
Usage: transit <repo> [<old> <new>]

If no commits are given, transit will revwalk from latest to oldest.
";

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

fn main() {
    // Parse the args above or die.
    let args: Args = Docopt::new(USAGE)
        .and_then(|d| d.decode())
        .unwrap_or_else(|e| e.exit());
    // Pull up the revwalk.
    let path = Path::new(args.arg_repo);
    let repo = Repository::discover(&path)
        .ok().expect("Unable to find repo.");
    // If we can destructures the two optional args into real things.
    if let (Some(old_string), Some(new_string)) = (args.arg_old, args.arg_new) {
        // Compare a specific commit pair.
        // Fist, get the commits. (Error checked)
        let old = Oid::from_str(&old_string[]).and_then(|oid| repo.find_commit(oid));
        let new = Oid::from_str(&new_string[]).and_then(|oid| repo.find_commit(oid));
        if old.is_ok() && new.is_ok() {
            find_moves(&repo, &old.unwrap(), &new.unwrap()).unwrap();
        } else {
            panic!("Commit ids were not valid.");
        }
    } else {
        // Revwalk.
        let mut revwalk = repo.revwalk()
            .ok().expect("Unable to get revwalk.");
        // Setup some options.
        revwalk.simplify_first_parent(); // TODO: Maybe remove?
        let mut flags = git2::Sort::empty();
        flags.insert(git2::SORT_TIME);
        flags.insert(git2::SORT_TOPOLOGICAL);
        revwalk.set_sorting(flags);
        // Push HEAD to the revwalk.
        revwalk.push_head()
            .ok().expect("Unable to push HEAD.");
        // We sadly must collect here to use `.windows()`
        let history = revwalk.filter_map(|id| repo.find_commit(id).ok())
            .collect::<Vec<Commit>>();
        // Walk through each pair of commits.
        for pair in history.windows(2) {let (old, new) = (&pair[1], &pair[0]);
			let output = find_moves(&repo, old.clone(), new.clone()).unwrap();
			// mangle the vectors here to make pretty output
			make_output(output);
		}
	}
}

fn make_output(output: Vec<Output>) {
	println!("some json output");

}

fn find_moves(repo: &Repository, old: &Commit, new: &Commit) -> Result<Vec<Output>, Error> {
	let old_tree = try!(old.tree());
	let new_tree = try!(new.tree());
	// Build up a diff of the two trees.
	let diff = try!(Diff::tree_to_tree(repo, Some(&old_tree), Some(&new_tree), None));
	// State. TODO: Don't just use a u64.
	let moved = 0;
	let mut current_hunk: Vec<String> = Vec::new();
	// Read about this function in http://alexcrichton.com/git2-rs/git2/struct.Diff.html#method.print
	// It's a bit weird, but I think it will provide the necessary information.
	diff.print(DiffFormat::Patch, |delta, maybe_hunk, line| -> bool {
		// Thinking:
		//  * If is not a hunk, keep going.
		//  * If it's a hunk, do regex magic.
		//  * Stick regex output into a hashmap as a hash.
		//  * Later, we will iterate through and output pased on the values.
		// Filter out all the boring context lines.
		// If we're not interested in this line just return since it will iterate to the next.
		if maybe_hunk.is_none() { return true }; // Return early.
		match line.origin() {
			// Context
			' ' | '=' => true,
			// Headers
			'F' | 'H' => true,
			// Additions
			'+' | '>' => {
				println!("+ {}", str::from_utf8(line.content()).unwrap());
				true
			},
			// Deletions
			'-' | '<' => {
				println!("- {}", str::from_utf8(line.content()).unwrap());
				true
			},
			// Other (We don't care about these.)
			_         => true,
		}
			// Look at that match statement, what a hunk. So hunky.
			// match maybe_hunk {
			//     Some(hunk) => unimplemented!(),
			//     None => unimplemented!(),
			// }
	});
	Ok(vec![Output {
		old_commit: old.id(),
		new_commit: new.id(),
		origin_line: 0,
		destination_line: 0,
		num_lines: 0
	}])
}

#[derive[Show]]
struct Output {
    old_commit: Oid,
    new_commit: Oid,
    origin_line: u32,
    destination_line: u32,
    num_lines: u32,
}
<|endoftext|>


<commit_before>extern crate rustc_serialize;
extern crate docopt;
#[macro_use]
extern crate aws_abacus;

use docopt::Docopt;
use std::fs;
use std::path;
use std::io;
use std::fs::File;
use std::io::BufReader;
use std::io::BufRead;

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

    let debug = args.flag_debug;

    let log_location = &path::Path::new(&args.arg_log_location);
    debug!(debug, "Running summary on {}.", log_location.to_str().unwrap());

    let mut filenames = Vec::new();
    match file_list(log_location, &mut filenames){
        Ok(_) => {
            debug!(debug, "DEBUG: Found {} files.", filenames.len());
            let mut record_count = 0;
            for filename in filenames {
                match File::open(filename.path()) {
                    Ok(file) => {
                        let buffered_file = BufReader::new(&file);
                        // let lines = buffered_file.lines();
                        // for line in lines {
                        //     // let l = line.unwrap();
                        //     // println!("{}", l);
                        //
                        // }
                        record_count += buffered_file.lines().count();
                    },
                    Err(e) => {
                        println!("{}", e);
                    }
                }
            }
            debug!(debug, "Found {} records.", record_count);
        },
        Err(e) => println!("An error occurred."),
    };
}

fn file_list(dir: &path::Path, filenames: &mut Vec<fs::DirEntry>) -> Result<(), io::Error> {
    if try!(fs::metadata(dir)).is_dir() {
        for entry in try!(fs::read_dir(dir)) {
            let entry = try!(entry);
            if try!(fs::metadata(entry.path())).is_dir() {
                try!(file_list(&entry.path(), filenames));
            } else {
                filenames.push(entry)
            }
        }
    }

    Ok(())
}

const USAGE: &'static str = "
aws-abacus

Usage:
  aws-abacus <log-location>
  aws-abacus (-d | --debug) <log-location>
  aws-abacus (-h | --help)

Options:
  -h --help     Show this screen.
  -d --debug    Turn on debug output
";

#[derive(Debug, RustcDecodable)]
struct Args {
    arg_log_location: String,
    flag_debug: bool,
}
<commit_msg>Removed duplicate log specifier.<commit_after>extern crate rustc_serialize;
extern crate docopt;
#[macro_use]
extern crate aws_abacus;

use docopt::Docopt;
use std::fs;
use std::path;
use std::io;
use std::fs::File;
use std::io::BufReader;
use std::io::BufRead;

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

    let debug = args.flag_debug;

    let log_location = &path::Path::new(&args.arg_log_location);
    debug!(debug, "Running summary on {}.", log_location.to_str().unwrap());

    let mut filenames = Vec::new();
    match file_list(log_location, &mut filenames){
        Ok(_) => {
            debug!(debug, "Found {} files.", filenames.len());
            let mut record_count = 0;
            for filename in filenames {
                match File::open(filename.path()) {
                    Ok(file) => {
                        let buffered_file = BufReader::new(&file);
                        // let lines = buffered_file.lines();
                        // for line in lines {
                        //     // let l = line.unwrap();
                        //     // println!("{}", l);
                        //
                        // }
                        record_count += buffered_file.lines().count();
                    },
                    Err(e) => {
                        println!("{}", e);
                    }
                }
            }
            debug!(debug, "Found {} records.", record_count);
        },
        Err(e) => println!("An error occurred."),
    };
}

fn file_list(dir: &path::Path, filenames: &mut Vec<fs::DirEntry>) -> Result<(), io::Error> {
    if try!(fs::metadata(dir)).is_dir() {
        for entry in try!(fs::read_dir(dir)) {
            let entry = try!(entry);
            if try!(fs::metadata(entry.path())).is_dir() {
                try!(file_list(&entry.path(), filenames));
            } else {
                filenames.push(entry)
            }
        }
    }

    Ok(())
}

const USAGE: &'static str = "
aws-abacus

Usage:
  aws-abacus <log-location>
  aws-abacus (-d | --debug) <log-location>
  aws-abacus (-h | --help)

Options:
  -h --help     Show this screen.
  -d --debug    Turn on debug output
";

#[derive(Debug, RustcDecodable)]
struct Args {
    arg_log_location: String,
    flag_debug: bool,
}
<|endoftext|>


<commit_before>use std::env;
use std::fs::File;
use std::fmt::Display;
use std::io;
use std::io::BufReader;
use std::io::prelude::*;
use std::path::Path;

const STDIN: &'static str = "-";
const BUFFER_SIZE: usize = 4096;

enum Method {
    File,
    Line,
}

fn main() {
    // skip the first element as it is the program name
    let args = env::args();
    if args.len() == 1 {
        cat(io::stdin(), "stdin", Method::Line);
    } else {
        for arg in args.skip(1) {
            if arg.eq(STDIN) {
                cat(io::stdin(), "stdin", Method::Line);
            } else {
                let path = Path::new(&arg);
                let display = path.display();
                let file = File::open(path)
                    .expect(format!("cat: failed to open: {}", display).as_str());
                cat(file, display, Method::File);
            }
        }
    }
}

// Read from `input` and print to stdout.
// `name` is used in error messages.
// `m` selects whether to read line-by-line or to read the entire input.
fn cat<R, N>(input: R, name: N, m: Method) where R: Read, N: Display {
    let mut reader = BufReader::with_capacity(BUFFER_SIZE, input);
    loop {
        let mut s = String::new();
        let n = match m {
            Method::Line => reader.read_line(&mut s),
            Method::File => reader.read_to_string(&mut s),
        }.expect(format!("cat: failed to open {}", name).as_str());
        if n > 0 {
            print!("{}", s);
        } else {
            break;
        }
    }
    io::stdout().flush().unwrap();
}
<commit_msg>simplified<commit_after>use std::env;
use std::fs::File;
use std::fmt::Display;
use std::io;
use std::io::BufReader;
use std::io::prelude::*;

const BUFFER_SIZE: usize = 4096;

fn main() {
    let args = env::args();
    if args.len() == 1 {
        cat(io::stdin(), "stdin");
    } else {
        // skip the first element as it is the program name
        for arg in args.skip(1) {
            if arg == "-" {
                cat(io::stdin(), "stdin");
            } else {
                let file = File::open(&arg)
                    .unwrap_or_else(|e| panic!("cat: failed to open{} : {}", &arg, e));
                cat(file, &arg);
            }
        }
    }
}

/// Read from `input` and print to stdout.
fn cat<R, N>(input: R, name: N)
        where R: Read, N: Display {
    let mut reader = BufReader::with_capacity(BUFFER_SIZE, input);
    let mut s = String::new();
    loop {
        s.clear();
        let n = reader.read_line(&mut s);
        let n = n.unwrap_or_else(|e| panic!("cat: failed to read {}: {}", name, e));
        if n > 0 {
            print!("{}", s);
        } else {
            break;
        }
    }
    io::stdout().flush().unwrap();
}
<|endoftext|>


<commit_before>extern crate hyper;
extern crate rustc_serialize;
extern crate chrono;
extern crate regex;
extern crate env_logger;
#[macro_use] extern crate log;
extern crate docopt;

use std::ascii::AsciiExt;
use std::io::Read;
use std::env;
use std::collections::HashMap;
use std::fs::File;
use std::io::Write;
use std::path::PathBuf;
use std::fmt;
use std::error::Error;
use std::result;

use chrono::naive::datetime::NaiveDateTime;
use chrono::offset::local::Local;
use chrono::offset::fixed::FixedOffset;
use chrono::datetime::DateTime;
use chrono::Datelike;

use rustc_serialize::json;
use rustc_serialize::Decodable;
use hyper::client::Client;

use regex::Regex;

use docopt::Docopt;

static USAGE: &'static str = "
Washing State Ferry Schedules

Usage:
  wsf [options] <from> <to>
  wsf -h

  <from> and <to> are a prefix of the departing terminal and arriving
  terminal, respectively. For example 'wsf sea ba' is equivalent to
  'wsf Seattle \"Bainbridge Island\"'.

Options:
  -h --help     Show this screen.
";

#[derive(Debug, RustcDecodable)]
struct Args {
    arg_from: String,
    arg_to: String,
}

fn run() -> Result<()> {
    try!(env_logger::init());

    let args: Args = Docopt::new(USAGE)
                            .and_then(|d| d.decode())
                            .unwrap_or_else(|e| e.exit());

    let from_in: &str = &args.arg_from.to_ascii_lowercase();
    let to_in: &str   = &args.arg_to.to_ascii_lowercase();

    // pull in api key at *build* time from environment
    let mut s = Session::new(env!("WSDOT_API_KEY"));

    let now = Local::now();
    let mut from: Option<i32> = None;
    let mut to: Option<i32> = None;
    for terminal in s.terminals().unwrap().iter() {
        if terminal.Description.to_ascii_lowercase().starts_with(&from_in) {
            from = Some(terminal.TerminalID);
        }
        if terminal.Description.to_ascii_lowercase().starts_with(&to_in) {
            to = Some(terminal.TerminalID);
        }
    }
    let from = try!(from.ok_or(CliError::BadInput(format!("'{}' is not a known port!", from_in))));
    let to = try!(to.ok_or(CliError::BadInput(format!("'{}' is not a known port!", to_in))));
    let tc = s.schedule(from, to).unwrap();
    for time in tc.Times.iter() {
        if time.depart_time() > now {
            println!("{}\t{}\t{}\t{}",
                     time.depart_time().time(),
                     tc.DepartingTerminalName,
                     tc.ArrivingTerminalName,
                     time.VesselName );
        }

    }
    s.save_cache()
}

fn main() {
    match run() {
        Ok(_) => {},
        Err(e) => {
            println!("{}", e);
            std::process::exit(1);
        }
    }
}

struct Session {
    api_key: String,
    client: Client,
    cache: Cache,
    cacheflushdate: String,
    cache_path: String,
    offline: bool,
}

impl Session {

    fn new(api_key: &str) -> Session {
        let mut cache_path: PathBuf = env::home_dir().unwrap();
        cache_path.push(".wsf.cache");
        let cache_path = format!("{}", cache_path.display());

        let mut s = Session {
            api_key: api_key.to_string(),
            client: Client::new(),
            cache: Cache::load(&cache_path),
            cacheflushdate: String::new(),
            cache_path: cache_path,
            offline: false,
        };

        s.offline = match s.get::<String>("/cacheflushdate".to_owned()) {
            Ok(cfd) => {
                    s.cacheflushdate = cfd;
                    false
                },
            Err(_) => true,
        };
        s
    }

    fn save_cache(&mut self) -> Result<()> {
        self.cache.cache_flush_date = self.cacheflushdate.clone();

        let mut f = try!(File::create(&self.cache_path));
        let encoded = try!(json::encode(&self.cache));
        Ok(try!(f.write_all(encoded.as_bytes())))
    }

    fn get<T: Decodable>(&self, path: String) -> Result<T> {
        let url = &format!("http://www.wsdot.wa.gov/ferries/api/schedule/rest{}?apiaccesscode={}",
                            path,
                            self.api_key);
        let mut res = try!(self.client.get(url).send());
        assert_eq!(res.status, hyper::Ok);

        let mut buf = String::new();
        try!(res.read_to_string(&mut buf));
        Ok(try!(json::decode::<T>(&buf)))
    }

    fn terminals(&mut self) -> Result<Vec<Terminal>> {
        if self.offline || (self.cache.cache_flush_date == self.cacheflushdate) {
            return Ok(self.cache.terminals.clone())
        }
        else {
            let now = Local::today();
            let path = format!("/terminals/{}-{}-{}", now.year(), now.month(), now.day());
            let routes: Vec<Terminal> = try!(self.get(path));
            self.cache.terminals = routes.clone();
            return Ok(routes);
        }
    }

    fn schedule(&mut self, from: i32, to: i32) -> Result<TerminalCombo> {
        let mut cache_is_stale = true;
        let cache_key = format!("{} {}", from, to);

        if self.offline || (self.cache.cache_flush_date == self.cacheflushdate) {
            if self.cache.sailings.contains_key(&cache_key) {
                // cache is up to date and has route!
                // unwrap is correct as we checked for enry first
                return Ok(self.cache.sailings.get(&cache_key).unwrap().clone());
            }
            else {
                // cache is up to date, but we don't have this route in it
                cache_is_stale = false;
            }
        }

        if cache_is_stale {
            self.cache.sailings.clear();
        }

        let now = Local::now();
        let path = format!("/schedule/{}-{}-{}/{}/{}",
                            now.year(), now.month(), now.day(), from, to);

        let schedule: Schedule = try!(self.get(path));

        self.cache.sailings.insert(cache_key, schedule.TerminalCombos[0].clone());
        Ok(schedule.TerminalCombos[0].clone())
    }
}

#[derive(RustcDecodable, RustcEncodable, Debug)]
struct Cache {
    terminals: Vec<Terminal>,
    sailings: HashMap<String, TerminalCombo>,
    cache_flush_date: String,
}

impl Cache {
    fn load(path: &String) -> Cache {
        let r = File::open(path);
        match r {
            Ok(mut f) => {
                let mut s = String::new();
                f.read_to_string(&mut s).unwrap();
                let cache = json::decode(&s).unwrap();
                cache
            },
            Err(_) => {
                Cache {
                    terminals: vec![],
                    sailings: HashMap::new(),
                    cache_flush_date: String::new(),
                }
            }
        }
    }
}

#[allow(non_snake_case)]
#[derive(RustcDecodable, RustcEncodable, Debug, Clone)]
struct Terminal {
    TerminalID: i32,
    Description: String,
}

#[allow(non_snake_case)]
#[derive(RustcDecodable, RustcEncodable, Clone, Debug)]
struct SailingTime {
    DepartingTime: String,
    ArrivingTime: Option<String>,
    VesselName: String,
}

impl SailingTime {
    // parse date strings of form "/Date(1436318400000-0700)/"
    fn depart_time(&self) -> DateTime<Local> {
        let re = Regex::new(r"^/Date\((\d{10})000-(\d{2})(\d{2})\)/$").unwrap();
        let caps = re.captures(&self.DepartingTime).unwrap();

        let epoch: i64 = caps.at(1).unwrap().parse().unwrap();
        let tz_hours: i32 = caps.at(2).unwrap().parse().unwrap();
        let tz_minutes: i32 = caps.at(3).unwrap().parse().unwrap();

        let nd = NaiveDateTime::from_timestamp(epoch, 0);
        let tz = FixedOffset::west((tz_hours * 3600) + (tz_minutes * 60));
        let fotz: DateTime<FixedOffset> = DateTime::from_utc(nd, tz);
        fotz.with_timezone(&Local)
    }
}

#[allow(non_snake_case)]
#[derive(RustcDecodable, RustcEncodable, Clone, Debug)]
struct TerminalCombo {
    Times: Vec<SailingTime>,
    DepartingTerminalName: String,
    ArrivingTerminalName: String,
}

#[allow(non_snake_case)]
#[derive(RustcDecodable, RustcEncodable, Debug)]
struct Schedule {
    TerminalCombos: Vec<TerminalCombo>,
}

type Result<T> = result::Result<T, CliError>;

#[derive(Debug)]
enum CliError {
    Log(log::SetLoggerError),
    Parse(rustc_serialize::json::DecoderError),
    SaveCache(rustc_serialize::json::EncoderError),
    Http(hyper::error::Error),
    Io(std::io::Error),
    BadInput(String),
}

impl fmt::Display for CliError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            CliError::Log(ref err) => format!("Unable to configure logging: {}", err).fmt(f),
            CliError::Parse(ref err) => format!("Unable to parse WSDOT Data: {}", err).fmt(f),
            CliError::SaveCache(ref err) => format!("Unable to save cache: {}", err).fmt(f),
            CliError::Http(ref err) => format!("Unable to communicate with WSDOT: {}", err).fmt(f),
            CliError::Io(ref err) => format!("Unable to read data: {}", err).fmt(f),
            CliError::BadInput(ref desc) => format!("Unable to understand input: {}", desc).fmt(f),
        }
    }
}

impl Error for CliError {
    fn description(&self) -> &str {
        match *self {
            CliError::BadInput(ref err) => err,
            _ => self.description(),
        }
    }
}

impl From<rustc_serialize::json::EncoderError> for CliError {
    fn from(err: rustc_serialize::json::EncoderError) -> CliError {
        CliError::SaveCache(err)
    }
}

impl From<log::SetLoggerError> for CliError {
    fn from(err: log::SetLoggerError) -> CliError {
        CliError::Log(err)
    }
}
impl From<hyper::error::Error> for CliError {
    fn from(err: hyper::error::Error) -> CliError {
        CliError::Http(err)
    }
}

impl From<std::io::Error> for CliError {
    fn from(err: std::io::Error) -> CliError {
        CliError::Io(err)
    }
}

impl From<rustc_serialize::json::DecoderError> for CliError {
    fn from(err: rustc_serialize::json::DecoderError) -> CliError {
        CliError::Parse(err)
    }
}
<commit_msg>more error handling<commit_after>extern crate hyper;
extern crate rustc_serialize;
extern crate chrono;
extern crate regex;
extern crate env_logger;
#[macro_use] extern crate log;
extern crate docopt;

use std::ascii::AsciiExt;
use std::io::Read;
use std::env;
use std::collections::HashMap;
use std::fs::File;
use std::io::Write;
use std::path::PathBuf;
use std::fmt;
use std::error::Error;
use std::result;

use chrono::naive::datetime::NaiveDateTime;
use chrono::offset::local::Local;
use chrono::offset::fixed::FixedOffset;
use chrono::datetime::DateTime;
use chrono::Datelike;

use rustc_serialize::json;
use rustc_serialize::Decodable;
use hyper::client::Client;

use regex::Regex;

use docopt::Docopt;

static USAGE: &'static str = "
Washing State Ferry Schedules

Usage:
  wsf [options] <from> <to>
  wsf -h

  <from> and <to> are a prefix of the departing terminal and arriving
  terminal, respectively. For example 'wsf sea ba' is equivalent to
  'wsf Seattle \"Bainbridge Island\"'.

Options:
  -h --help     Show this screen.
";

#[derive(Debug, RustcDecodable)]
struct Args {
    arg_from: String,
    arg_to: String,
}

fn run() -> Result<()> {
    try!(env_logger::init());

    let args: Args = Docopt::new(USAGE)
                            .and_then(|d| d.decode())
                            .unwrap_or_else(|e| e.exit());

    let from_in: &str = &args.arg_from.to_ascii_lowercase();
    let to_in: &str   = &args.arg_to.to_ascii_lowercase();

    // pull in api key at *build* time from environment
    let mut s = Session::new(env!("WSDOT_API_KEY"));

    let now = Local::now();
    let mut from: Option<i32> = None;
    let mut to: Option<i32> = None;
    for terminal in try!(s.terminals()).iter() {
        if terminal.Description.to_ascii_lowercase().starts_with(&from_in) {
            from = Some(terminal.TerminalID);
        }
        if terminal.Description.to_ascii_lowercase().starts_with(&to_in) {
            to = Some(terminal.TerminalID);
        }
    }
    let from = try!(from.ok_or(CliError::BadInput(format!("'{}' is not a known port!", from_in))));
    let to = try!(to.ok_or(CliError::BadInput(format!("'{}' is not a known port!", to_in))));
    let tc = try!(s.schedule(from, to));
    for time in tc.Times.iter() {
        if time.depart_time() > now {
            println!("{}\t{}\t{}\t{}",
                     time.depart_time().time(),
                     tc.DepartingTerminalName,
                     tc.ArrivingTerminalName,
                     time.VesselName );
        }
    }
    s.save_cache()
}

fn main() {
    match run() {
        Ok(_) => {},
        Err(e) => {
            println!("{}", e);
            std::process::exit(1);
        }
    }
}

struct Session {
    api_key: String,
    client: Client,
    cache: Cache,
    cacheflushdate: String,
    cache_path: String,
    offline: bool,
}

impl Session {

    fn new(api_key: &str) -> Session {
        let mut cache_path: PathBuf = env::home_dir().unwrap();
        cache_path.push(".wsf.cache");
        let cache_path = format!("{}", cache_path.display());

        let mut s = Session {
            api_key: api_key.to_string(),
            client: Client::new(),
            cache: Cache::load(&cache_path),
            cacheflushdate: String::new(),
            cache_path: cache_path,
            offline: false,
        };

        s.offline = match s.get::<String>("/cacheflushdate".to_owned()) {
            Ok(cfd) => {
                    s.cacheflushdate = cfd;
                    false
                },
            Err(_) => true,
        };
        return s;
    }

    fn save_cache(&mut self) -> Result<()> {
        self.cache.cache_flush_date = self.cacheflushdate.clone();

        let mut f = try!(File::create(&self.cache_path));
        let encoded = try!(json::encode(&self.cache));
        Ok(try!(f.write_all(encoded.as_bytes())))
    }

    fn get<T: Decodable>(&self, path: String) -> Result<T> {
        let url = &format!("http://www.wsdot.wa.gov/ferries/api/schedule/rest{}?apiaccesscode={}",
                            path,
                            self.api_key);
        let mut res = try!(self.client.get(url).send());
        assert_eq!(res.status, hyper::Ok);

        let mut buf = String::new();
        try!(res.read_to_string(&mut buf));
        Ok(try!(json::decode::<T>(&buf)))
    }

    fn terminals(&mut self) -> Result<Vec<Terminal>> {
        if self.offline || (self.cache.cache_flush_date == self.cacheflushdate) {
            return Ok(self.cache.terminals.clone())
        }
        else {
            let now = Local::today();
            let path = format!("/terminals/{}-{}-{}", now.year(), now.month(), now.day());
            let routes: Vec<Terminal> = try!(self.get(path));
            self.cache.terminals = routes.clone();
            return Ok(routes);
        }
    }

    fn schedule(&mut self, from: i32, to: i32) -> Result<TerminalCombo> {
        let mut cache_is_stale = true;
        let cache_key = format!("{} {}", from, to);

        if self.offline || (self.cache.cache_flush_date == self.cacheflushdate) {
            if self.cache.sailings.contains_key(&cache_key) {
                // cache is up to date and has route!
                // unwrap is correct as we checked for enry first
                return Ok(self.cache.sailings.get(&cache_key).unwrap().clone());
            }
            else {
                // cache is up to date, but we don't have this route in it
                cache_is_stale = false;
            }
        }

        if cache_is_stale {
            self.cache.sailings.clear();
        }

        let now = Local::now();
        let path = format!("/schedule/{}-{}-{}/{}/{}",
                            now.year(), now.month(), now.day(), from, to);

        let schedule: Schedule = try!(self.get(path));

        self.cache.sailings.insert(cache_key, schedule.TerminalCombos[0].clone());
        Ok(schedule.TerminalCombos[0].clone())
    }
}

#[derive(RustcDecodable, RustcEncodable, Debug)]
struct Cache {
    terminals: Vec<Terminal>,
    sailings: HashMap<String, TerminalCombo>,
    cache_flush_date: String,
}

impl Cache {
    fn load(path: &String) -> Cache {
        let r = File::open(path);
        match r {
            Ok(mut f) => {
                let mut s = String::new();
                f.read_to_string(&mut s).unwrap();
                let cache = json::decode(&s).unwrap();
                cache
            },
            Err(_) => {
                Cache {
                    terminals: vec![],
                    sailings: HashMap::new(),
                    cache_flush_date: String::new(),
                }
            }
        }
    }
}

#[allow(non_snake_case)]
#[derive(RustcDecodable, RustcEncodable, Debug, Clone)]
struct Terminal {
    TerminalID: i32,
    Description: String,
}

#[allow(non_snake_case)]
#[derive(RustcDecodable, RustcEncodable, Clone, Debug)]
struct SailingTime {
    DepartingTime: String,
    ArrivingTime: Option<String>,
    VesselName: String,
}

impl SailingTime {
    // parse date strings of form "/Date(1436318400000-0700)/"
    fn depart_time(&self) -> DateTime<Local> {
        let re = Regex::new(r"^/Date\((\d{10})000-(\d{2})(\d{2})\)/$").unwrap();
        let caps = re.captures(&self.DepartingTime).unwrap();

        let epoch: i64 = caps.at(1).unwrap().parse().unwrap();
        let tz_hours: i32 = caps.at(2).unwrap().parse().unwrap();
        let tz_minutes: i32 = caps.at(3).unwrap().parse().unwrap();

        let nd = NaiveDateTime::from_timestamp(epoch, 0);
        let tz = FixedOffset::west((tz_hours * 3600) + (tz_minutes * 60));
        let fotz: DateTime<FixedOffset> = DateTime::from_utc(nd, tz);
        fotz.with_timezone(&Local)
    }
}

#[allow(non_snake_case)]
#[derive(RustcDecodable, RustcEncodable, Clone, Debug)]
struct TerminalCombo {
    Times: Vec<SailingTime>,
    DepartingTerminalName: String,
    ArrivingTerminalName: String,
}

#[allow(non_snake_case)]
#[derive(RustcDecodable, RustcEncodable, Debug)]
struct Schedule {
    TerminalCombos: Vec<TerminalCombo>,
}

type Result<T> = result::Result<T, CliError>;

#[derive(Debug)]
enum CliError {
    Log(log::SetLoggerError),
    Parse(rustc_serialize::json::DecoderError),
    SaveCache(rustc_serialize::json::EncoderError),
    Http(hyper::error::Error),
    Io(std::io::Error),
    BadInput(String),
}

impl fmt::Display for CliError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            CliError::Log(ref err) => format!("Unable to configure logging: {}", err).fmt(f),
            CliError::Parse(ref err) => format!("Unable to parse WSDOT Data: {}", err).fmt(f),
            CliError::SaveCache(ref err) => format!("Unable to save cache: {}", err).fmt(f),
            CliError::Http(ref err) => format!("Unable to communicate with WSDOT: {}", err).fmt(f),
            CliError::Io(ref err) => format!("Unable to read data: {}", err).fmt(f),
            CliError::BadInput(ref desc) => format!("Unable to understand input: {}", desc).fmt(f),
        }
    }
}

impl Error for CliError {
    fn description(&self) -> &str {
        match *self {
            CliError::BadInput(ref err) => err,
            _ => self.description(),
        }
    }
}

impl From<rustc_serialize::json::EncoderError> for CliError {
    fn from(err: rustc_serialize::json::EncoderError) -> CliError {
        CliError::SaveCache(err)
    }
}

impl From<log::SetLoggerError> for CliError {
    fn from(err: log::SetLoggerError) -> CliError {
        CliError::Log(err)
    }
}
impl From<hyper::error::Error> for CliError {
    fn from(err: hyper::error::Error) -> CliError {
        CliError::Http(err)
    }
}

impl From<std::io::Error> for CliError {
    fn from(err: std::io::Error) -> CliError {
        CliError::Io(err)
    }
}

impl From<rustc_serialize::json::DecoderError> for CliError {
    fn from(err: rustc_serialize::json::DecoderError) -> CliError {
        CliError::Parse(err)
    }
}
<|endoftext|>