<commit_before>extern crate rusoto_core;
extern crate rusoto_ec2;
extern crate serde_json;
#[macro_use]
extern crate serde_derive;
extern crate chrono;
extern crate regex;
extern crate toml;

pub mod auth {
    use rusoto_core::{ChainProvider, ProfileProvider};
    // From:
    // https://github.comm/InQuicker/kaws/blob/master/src/aws.rs
    #[warn(dead_code)]
    pub fn credentials_provider(path: Option<&str>, profile: Option<&str>) -> ChainProvider {
        let mut profile_provider = ProfileProvider::new().expect(
            "Failed to create AWS credentials provider."
        );

        if let Some(path) = path {
            profile_provider.set_file_path(path);
        }

        if let Some(profile) = profile {
            profile_provider.set_profile(profile);
        }

        ChainProvider::with_profile_provider(profile_provider)
    }
}

pub mod config {

    use std::fs::File;
    use std::io::Read;
    use toml;
    use std::path::Path;
    use std::collections::HashMap;

    #[derive(Debug, Deserialize)]
    pub struct Config {
        pub environments: HashMap<String, EnvironConfig>,
    }
    #[derive(Debug, Deserialize)]
    pub struct EnvironConfig {
        pub account_id: String,
        pub region: String,
        pub ssh_options: Vec<String>,
    }

    fn default_config() -> Config {
        let default_config = r#"
[environments.default]
account_id = "123"
region = "us-east-1"
ssh_options = ["-l", "ubuntu", "-o", "StrictHostKeyChecking=no", "-o", "UserKnownHostsFile=/dev/null"]
"#;
        match toml::from_str(&default_config) {
            Ok(config) => config,
            Err(err) => {
                println!("The default config couldn't be deserialzed by toml because {:?}. This is a problem in the source: \n{}",
                         err,
                         default_config);
                panic!();
            }
        }
    }

    pub fn read_config(config_dir: &String) -> Config {
        let pathname = format!("{}/config.toml", config_dir);
        let mut file_bytes = String::new();
        let mut config_file = match File::open(Path::new(&pathname)) {
            Ok(file) =>  file,
            Err(err) => {
                println!("Couldn't read config file {} because {:?}", pathname, err);
                panic!();
            }
        };

        config_file.read_to_string(&mut file_bytes).expect("Something went wrong");
        match toml::from_str(&file_bytes) {
            Ok(data) => data,
            Err(err) => {
                println!("Couldn't read toml from {} because {:?}", pathname, err);
                println!("Using the default config");
                default_config()
            }
        }
    }
}

// The general idea for saving and restoring paths will be that first the cache will be consulted
// when looking for a resource.  If the resource is found, bingo.
//
// In the case(s) where the resource can't be found, try the API, and if the API call is successful,
// record the updated data.  If it is not so successful, then avoid clobbering the current data.
pub mod ec2_instances {
    use rusoto_core::{Region, default_tls_client};
    use rusoto_ec2::{Ec2, Ec2Client, DescribeInstancesRequest, Instance, Reservation};
    // use std::collections::HashMap;

    use std::fs::{File, rename};
    use std::path::Path;
    use std::io::prelude::*;
    use std::io;
    use std::io::Read;
    use std::collections::{HashMap, HashSet};
    use std::str::FromStr;
    use regex::Regex;

    use chrono::prelude::*;
    use chrono::Duration;

    use serde_json;

    // In the future, this will be a config and a runtime option
    // Also in the future, bless a tuple of environment variable that will
    // distinguish account data (Account, API, region) seems reasonable.
    // "global" seems like an appropriate choice for global APIs like IAM, route53.

    // A flat structure to make searching for an instance faster, with a
    // link back to the instance.

    // TODO: implement a sort on name tags
    #[derive(Clone, Debug, Serialize, Deserialize)]
    pub struct AshufInfo {
        pub instance_id: String,
        pub private_ip_addresses: Vec<String>,
        pub public_ip_addresses: Vec<String>,
        pub state_name: String,
        pub launch_time: String,
        pub availability_zone: String,
        pub image_ami: String,
        pub tags: HashMap<String, String>,
    }

    #[derive(Clone, Debug, Serialize, Deserialize)]
    pub struct CacheData { // How we'll cache our data
        written_time: DateTime<Utc>,
        instance_data: Vec<AshufInfo>,
    }

    pub fn read_without_cache(cache_dir: &String, region_name: &String, aws_account_id: &String) -> Vec<AshufInfo> {
        let creds = ::auth::credentials_provider(None, None);
        let reg = Region::from_str(region_name).unwrap();
        let client = Ec2Client::new(default_tls_client().unwrap(), creds, reg);

        let mut ec2_request_input = DescribeInstancesRequest::default();
        ec2_request_input.instance_ids = None;
        match client.describe_instances(&ec2_request_input) {
            Ok(response) => {
                let instances = ec2_res_to_instances(response.reservations.unwrap());
                let instances_data = ashuf_info_list(instances);
                match write_saved_json(&aws_account_id, &cache_dir, &region_name, &instances_data) {
                    Ok(msg) => println!("{}", msg),
                    Err(what_happened) => println!("{}", what_happened),
                };
                instances_data
            },
            Err(error) => {
                println!("Error when invoking describe_instances: {:?}", error);
                Vec::new()
            }
        }
    }

    pub fn read_via_cache(cache_dir: &String, region_name: &String, aws_account_id: &String, cache_ttl: i64) -> Vec<AshufInfo> {

        // let mut limited_info = Vec::new();
        // println!("cache_dir: {}", cache_dir);
        // println!("account: {}", aws_account_id);
        // println!("region_name: {}", region_name);

        let limited_info = match ec2_cached_data(&cache_dir, &aws_account_id, &region_name, cache_ttl) {
            Ok(instances) => {
                // println!("I'm using cache data");
                instances
            },
            Err(_) => read_without_cache(cache_dir, region_name, aws_account_id)
        };

        // XXx when ready, map over the regions provided and cache those
        // so they can be combined afterwards.  But for now, let's do one
        // region.
        limited_info
    }

    pub fn ip_addresses_of(instance: &Instance) -> (Vec<String>, Vec<String>) {
        // A host can have either an ENI in vpc, or a private IP address from an EIP (classic)
        // This function extracts those addresses, and returns two vectors.  The left
        // vector contains the private addresses of an instance, and the right vector contains the
        // public addresses of an instance.
        let mut private = HashSet::new();
        let mut public = HashSet::new();

        if let Some(ref network_interfaces) = instance.network_interfaces {
            for interface in network_interfaces {
                if let Some(ref addr) = interface.private_ip_address {
                    private.insert(addr.clone());
                }
            }
        }

        instance.private_ip_address.as_ref().map(|addr| private.insert(addr.clone()));
        instance.public_ip_address.as_ref().map(|addr| public.insert(addr.clone()));

        (vec!(private.into_iter().collect()), vec!(public.into_iter().collect()))
    }

    pub fn tags_of(instance: &Instance) -> HashMap<String, String> {
        // Tags are stored as inconvenient pairs of {"Name": "name", "Value": "Value"}
        // turn them into simpler key/value map here
        let mut tags = HashMap::new();
        if let Some(ref instance_tags) = instance.tags {
            for tag in instance_tags {
                if let (&Some(ref key), &Some(ref val)) = (&tag.key, &tag.value) {
                    tags.insert(key.clone(), val.clone());
                }

            }
        }
        tags
    }

    pub fn ashuf_info_list(instances: Vec<Instance>) -> Vec<AshufInfo> {
        // Take just the data we want for the AshufInfo struct from the
        // rusoto::ec2::Instance type, and return a vector of `AshufInfo`
        //
        // All data is copied from the instances provided, they are consumed
        // here.
        let mut limited_instances: Vec<AshufInfo> = Vec::new();
        for inst in instances {
            // println!("This instance is {:?}",  inst);
            let (private_addrs, public_addrs) = ip_addresses_of(&inst);
            let tags = tags_of(&inst);
            // println!("{:?}", addrs);
            let new_asi = AshufInfo {
                instance_id: String::from(inst.instance_id.unwrap()),
                private_ip_addresses: private_addrs,
                public_ip_addresses: public_addrs,
                state_name: String::from(inst.state.unwrap().name.unwrap()),
                launch_time: String::from(inst.launch_time.unwrap()),
                availability_zone: String::from(inst.placement.unwrap().availability_zone.unwrap()),
                image_ami: String::from(inst.image_id.unwrap()),
                tags: tags,
            };
            limited_instances.push(new_asi);
        }
        limited_instances
    }

    // returns OK on the left, and Not OK on the right.
    // Let's define that so that on the left are matched instances,
    // and the right  is unmatched instances.
    pub fn partition_matches(rexpr: &Regex, tag: &String, instances: Vec<AshufInfo>) -> (Vec<AshufInfo>,  Vec<AshufInfo>) {
        let (matched, unmatched) = instances
            .into_iter()
            .partition(|inst| {
                if let Some(tval) = inst.tags.get(tag) {
                    if rexpr.is_match(tval) {  // Match on the value (assuming it can't be None?)
                        // println!("Matched {:?}", tval);
                        true
                    } else {
                        false
                    }
                } else {
                    false
                }
            });
        (matched, unmatched)
    }

    pub fn instances_matching_regex(pattern: String, interesting_tags: Vec<String>, instances: Vec<AshufInfo>) -> Vec<AshufInfo> {
        let rexpr = Regex::new(&pattern).unwrap();
        let mut unmatched_instances = Vec::new();
        let mut matched_instances = Vec::new();
        unmatched_instances.extend_from_slice(instances.as_slice());

        for ref tag in interesting_tags {
            let (m, u) = partition_matches(&rexpr, tag, unmatched_instances);
            // Re-bind unmatched instances for the next loop
            unmatched_instances = u;
            matched_instances.extend_from_slice(m.as_slice());
        }
        matched_instances
    }

    pub fn running_instances(instances: Vec<AshufInfo>) -> Vec<AshufInfo> {
        instances.into_iter()
            .filter(|i| i.state_name == "running".to_string())
            .collect::<Vec<AshufInfo>>()
    }

    pub fn ec2_res_to_instances(reservations: Vec<Reservation>) -> Vec<Instance> {
        // The ec2 `describe-instances` call returns a structure that describe
        // reservations, and the reservations contain instances.
        // Since I pretty much never, ever, ever need to know about reservations,
        // and always care about instances, I am removing the reservations info
        // and creating a vector of instances.

        let mut instances = Vec::new();
        for res in reservations {
            for res_instances in res.instances {
                for inst in res_instances {
                    instances.push(inst);
                }
            }
        }
        instances
    }

    // I think I want to change how this works.  I think it'd be better if
    // the default  case is that  if an instance is found in the cache, then
    // don't refresh, just use it.  Only refresh if an instance isn't found
    // and the cache has aged out.  Or if the user requests it.
    pub fn ec2_cached_data(cache_dir: &String, account: &String, region_name: &String, cache_ttl: i64) -> Result<Vec<AshufInfo>, String> {
        // Look at the file at the provided path, and if the age of the
        // file is less than the specified age, get ec2 instance info
        // from it instead of from the api.
        // Otherwise go through the API and return that data
        // println!("cache_dir: {}", cache_dir);
        // println!("account: {}", account);
        // println!("region_name: {}", region_name);
        let data = match read_saved_json(&cache_dir, &account, &region_name) {
            Ok(saved_data) => saved_data,
            Err(error) => return Err(format!("{} while opening {}", error, "cache file"))
        };
        let difference = Utc::now().signed_duration_since(data.written_time); // Note that the order matters here.
        println!("Difference in time between cache record and now is {}", difference);

        if difference < Duration::seconds(cache_ttl) {
            // println!("Got data, and the time is valid");
            Ok(data.instance_data)
        } else {
            // println!("Got data, and the time expired");
            Err("Expired".to_string())
        }
    }

    /// This function is for saving the data from a call to the API. It's for
    /// this side-effect only
    // XXX: add support for (API, region)
    pub fn write_saved_json(account: &String, cache_dir: &String, region_name: &String, data: &Vec<AshufInfo>) -> io::Result<String> {
        // Interesting: in rust you can concat a &str onto a String.
        // Deref coercecions may be an interesting topic?
        let pathname = format!("{}/{}_{}_ec2_instances.json", cache_dir, account, region_name);

        let tmp_pathname = pathname.to_owned() + ".tmp";

        println!("starting, writing to {}", tmp_pathname);

        let mut cache_file_new = File::create(Path::new(&tmp_pathname))?;
        let cache_data = CacheData {
            written_time: Utc::now(),
            instance_data: data.to_owned(),
        };
        let json_bytes = match serde_json::to_string(&cache_data) {
            Err(_) => "{}".to_string(),
            Ok(output) => output
        };
        cache_file_new.write_all(json_bytes.as_bytes())?;
        // cache_file_new.write_all(serde_json::to_string(&data).unwrap().as_bytes());

        println!("Wrote the tmp cache file");

        rename(tmp_pathname, pathname)?;

        println!("Re-named the local cache file after getting new data");

        Ok("Cache written out".to_string())
    }

    pub fn read_saved_json(cache_dir: &String, account: &String, region_name: &String) -> io::Result<CacheData> {
        let pathname = format!("{}/{}_{}_ec2_instances.json", cache_dir, account, region_name);
        let mut file_bytes = String::new();
        let mut cache_file = match File::open(Path::new(&pathname)) {
            Ok(file) =>  file,
            Err(err) => {
                println!("Couldn't read cache file {} because {:?}", pathname, err);
                panic!();
            }
        };
        // let mut cache_file = File::open(Path::new(&pathname))?;

        // println!("starting read_saved_json; path to the instances file is known, and file is opened");

        cache_file.read_to_string(&mut file_bytes).expect("Something went wrong");
        let instance_data: CacheData = serde_json::from_str(&file_bytes)?;
        Ok(instance_data)
    }
}
<commit_msg>Fix a problem when the cache file doesn't exist yet<commit_after>extern crate rusoto_core;
extern crate rusoto_ec2;
extern crate serde_json;
#[macro_use]
extern crate serde_derive;
extern crate chrono;
extern crate regex;
extern crate toml;

pub mod auth {
    use rusoto_core::{ChainProvider, ProfileProvider};
    // From:
    // https://github.comm/InQuicker/kaws/blob/master/src/aws.rs
    #[warn(dead_code)]
    pub fn credentials_provider(path: Option<&str>, profile: Option<&str>) -> ChainProvider {
        let mut profile_provider = ProfileProvider::new().expect(
            "Failed to create AWS credentials provider."
        );

        if let Some(path) = path {
            profile_provider.set_file_path(path);
        }

        if let Some(profile) = profile {
            profile_provider.set_profile(profile);
        }

        ChainProvider::with_profile_provider(profile_provider)
    }
}

pub mod config {

    use std::fs::File;
    use std::io::Read;
    use toml;
    use std::path::Path;
    use std::collections::HashMap;

    #[derive(Debug, Deserialize)]
    pub struct Config {
        pub environments: HashMap<String, EnvironConfig>,
    }
    #[derive(Debug, Deserialize)]
    pub struct EnvironConfig {
        pub account_id: String,
        pub region: String,
        pub ssh_options: Vec<String>,
    }

    fn default_config() -> Config {
        let default_config = r#"
[environments.default]
account_id = "123"
region = "us-east-1"
ssh_options = ["-l", "ubuntu", "-o", "StrictHostKeyChecking=no", "-o", "UserKnownHostsFile=/dev/null"]
"#;
        match toml::from_str(&default_config) {
            Ok(config) => config,
            Err(err) => {
                println!("The default config couldn't be deserialzed by toml because {:?}. This is a problem in the source: \n{}",
                         err,
                         default_config);
                panic!();
            }
        }
    }

    pub fn read_config(config_dir: &String) -> Config {
        let pathname = format!("{}/config.toml", config_dir);
        let mut file_bytes = String::new();
        let mut config_file = match File::open(Path::new(&pathname)) {
            Ok(file) =>  file,
            Err(err) => {
                println!("Couldn't read config file {} because {:?}", pathname, err);
                panic!();
            }
        };

        config_file.read_to_string(&mut file_bytes).expect("Something went wrong");
        match toml::from_str(&file_bytes) {
            Ok(data) => data,
            Err(err) => {
                println!("Couldn't read toml from {} because {:?}", pathname, err);
                println!("Using the default config");
                default_config()
            }
        }
    }
}

// The general idea for saving and restoring paths will be that first the cache will be consulted
// when looking for a resource.  If the resource is found, bingo.
//
// In the case(s) where the resource can't be found, try the API, and if the API call is successful,
// record the updated data.  If it is not so successful, then avoid clobbering the current data.
pub mod ec2_instances {
    use rusoto_core::{Region, default_tls_client};
    use rusoto_ec2::{Ec2, Ec2Client, DescribeInstancesRequest, Instance, Reservation};
    // use std::collections::HashMap;

    use std::fs::{File, rename};
    use std::path::Path;
    use std::io::prelude::*;
    use std::io;
    use std::io::Read;
    use std::collections::{HashMap, HashSet};
    use std::str::FromStr;
    use regex::Regex;

    use chrono::prelude::*;
    use chrono::Duration;

    use serde_json;

    // In the future, this will be a config and a runtime option
    // Also in the future, bless a tuple of environment variable that will
    // distinguish account data (Account, API, region) seems reasonable.
    // "global" seems like an appropriate choice for global APIs like IAM, route53.

    // A flat structure to make searching for an instance faster, with a
    // link back to the instance.

    // TODO: implement a sort on name tags
    #[derive(Clone, Debug, Serialize, Deserialize)]
    pub struct AshufInfo {
        pub instance_id: String,
        pub private_ip_addresses: Vec<String>,
        pub public_ip_addresses: Vec<String>,
        pub state_name: String,
        pub launch_time: String,
        pub availability_zone: String,
        pub image_ami: String,
        pub tags: HashMap<String, String>,
    }

    #[derive(Clone, Debug, Serialize, Deserialize)]
    pub struct CacheData { // How we'll cache our data
        written_time: DateTime<Utc>,
        instance_data: Vec<AshufInfo>,
    }

    pub fn read_without_cache(cache_dir: &String, region_name: &String, aws_account_id: &String) -> Vec<AshufInfo> {
        let creds = ::auth::credentials_provider(None, None);
        let reg = Region::from_str(region_name).unwrap();
        let client = Ec2Client::new(default_tls_client().unwrap(), creds, reg);

        let mut ec2_request_input = DescribeInstancesRequest::default();
        ec2_request_input.instance_ids = None;
        match client.describe_instances(&ec2_request_input) {
            Ok(response) => {
                let instances = ec2_res_to_instances(response.reservations.unwrap());
                let instances_data = ashuf_info_list(instances);
                match write_saved_json(&aws_account_id, &cache_dir, &region_name, &instances_data) {
                    Ok(msg) => println!("{}", msg),
                    Err(what_happened) => println!("{}", what_happened),
                };
                instances_data
            },
            Err(error) => {
                println!("Error when invoking describe_instances: {:?}", error);
                Vec::new()
            }
        }
    }

    pub fn read_via_cache(cache_dir: &String, region_name: &String, aws_account_id: &String, cache_ttl: i64) -> Vec<AshufInfo> {

        // let mut limited_info = Vec::new();
        // println!("cache_dir: {}", cache_dir);
        // println!("account: {}", aws_account_id);
        // println!("region_name: {}", region_name);

        let limited_info = match ec2_cached_data(&cache_dir, &aws_account_id, &region_name, cache_ttl) {
            Ok(instances) => {
                // println!("I'm using cache data");
                instances
            },
            Err(_) => read_without_cache(cache_dir, region_name, aws_account_id)
        };

        // XXx when ready, map over the regions provided and cache those
        // so they can be combined afterwards.  But for now, let's do one
        // region.
        limited_info
    }

    pub fn ip_addresses_of(instance: &Instance) -> (Vec<String>, Vec<String>) {
        // A host can have either an ENI in vpc, or a private IP address from an EIP (classic)
        // This function extracts those addresses, and returns two vectors.  The left
        // vector contains the private addresses of an instance, and the right vector contains the
        // public addresses of an instance.
        let mut private = HashSet::new();
        let mut public = HashSet::new();

        if let Some(ref network_interfaces) = instance.network_interfaces {
            for interface in network_interfaces {
                if let Some(ref addr) = interface.private_ip_address {
                    private.insert(addr.clone());
                }
            }
        }

        instance.private_ip_address.as_ref().map(|addr| private.insert(addr.clone()));
        instance.public_ip_address.as_ref().map(|addr| public.insert(addr.clone()));

        (vec!(private.into_iter().collect()), vec!(public.into_iter().collect()))
    }

    pub fn tags_of(instance: &Instance) -> HashMap<String, String> {
        // Tags are stored as inconvenient pairs of {"Name": "name", "Value": "Value"}
        // turn them into simpler key/value map here
        let mut tags = HashMap::new();
        if let Some(ref instance_tags) = instance.tags {
            for tag in instance_tags {
                if let (&Some(ref key), &Some(ref val)) = (&tag.key, &tag.value) {
                    tags.insert(key.clone(), val.clone());
                }

            }
        }
        tags
    }

    pub fn ashuf_info_list(instances: Vec<Instance>) -> Vec<AshufInfo> {
        // Take just the data we want for the AshufInfo struct from the
        // rusoto::ec2::Instance type, and return a vector of `AshufInfo`
        //
        // All data is copied from the instances provided, they are consumed
        // here.
        let mut limited_instances: Vec<AshufInfo> = Vec::new();
        for inst in instances {
            // println!("This instance is {:?}",  inst);
            let (private_addrs, public_addrs) = ip_addresses_of(&inst);
            let tags = tags_of(&inst);
            // println!("{:?}", addrs);
            let new_asi = AshufInfo {
                instance_id: String::from(inst.instance_id.unwrap()),
                private_ip_addresses: private_addrs,
                public_ip_addresses: public_addrs,
                state_name: String::from(inst.state.unwrap().name.unwrap()),
                launch_time: String::from(inst.launch_time.unwrap()),
                availability_zone: String::from(inst.placement.unwrap().availability_zone.unwrap()),
                image_ami: String::from(inst.image_id.unwrap()),
                tags: tags,
            };
            limited_instances.push(new_asi);
        }
        limited_instances
    }

    // returns OK on the left, and Not OK on the right.
    // Let's define that so that on the left are matched instances,
    // and the right  is unmatched instances.
    pub fn partition_matches(rexpr: &Regex, tag: &String, instances: Vec<AshufInfo>) -> (Vec<AshufInfo>,  Vec<AshufInfo>) {
        let (matched, unmatched) = instances
            .into_iter()
            .partition(|inst| {
                if let Some(tval) = inst.tags.get(tag) {
                    if rexpr.is_match(tval) {  // Match on the value (assuming it can't be None?)
                        // println!("Matched {:?}", tval);
                        true
                    } else {
                        false
                    }
                } else {
                    false
                }
            });
        (matched, unmatched)
    }

    pub fn instances_matching_regex(pattern: String, interesting_tags: Vec<String>, instances: Vec<AshufInfo>) -> Vec<AshufInfo> {
        let rexpr = Regex::new(&pattern).unwrap();
        let mut unmatched_instances = Vec::new();
        let mut matched_instances = Vec::new();
        unmatched_instances.extend_from_slice(instances.as_slice());

        for ref tag in interesting_tags {
            let (m, u) = partition_matches(&rexpr, tag, unmatched_instances);
            // Re-bind unmatched instances for the next loop
            unmatched_instances = u;
            matched_instances.extend_from_slice(m.as_slice());
        }
        matched_instances
    }

    pub fn running_instances(instances: Vec<AshufInfo>) -> Vec<AshufInfo> {
        instances.into_iter()
            .filter(|i| i.state_name == "running".to_string())
            .collect::<Vec<AshufInfo>>()
    }

    pub fn ec2_res_to_instances(reservations: Vec<Reservation>) -> Vec<Instance> {
        // The ec2 `describe-instances` call returns a structure that describe
        // reservations, and the reservations contain instances.
        // Since I pretty much never, ever, ever need to know about reservations,
        // and always care about instances, I am removing the reservations info
        // and creating a vector of instances.

        let mut instances = Vec::new();
        for res in reservations {
            for res_instances in res.instances {
                for inst in res_instances {
                    instances.push(inst);
                }
            }
        }
        instances
    }

    // I think I want to change how this works.  I think it'd be better if
    // the default  case is that if an instance is found in the cache, then
    // don't refresh, just use it. Only refresh if an instance isn't found
    // and the cache has aged out. Or if the user requests it.
    pub fn ec2_cached_data(cache_dir: &String, account: &String, region_name: &String, cache_ttl: i64) -> Result<Vec<AshufInfo>, String> {
        // Look at the file at the provided path, and if the age of the
        // file is less than the specified age, get ec2 instance info
        // from it instead of from the api.
        // Otherwise go through the API and return that data
        // println!("cache_dir: {}", cache_dir);
        // println!("account: {}", account);
        // println!("region_name: {}", region_name);
        let data = match read_saved_json(&cache_dir, &account, &region_name) {
            Ok(saved_data) => saved_data,
            Err(error) => return Err(format!("{} while opening {}", error, "cache file"))
        };
        let difference = Utc::now().signed_duration_since(data.written_time); // Note that the order matters here.
        println!("Difference in time between cache record and now is {}", difference);

        if difference < Duration::seconds(cache_ttl) {
            // println!("Got data, and the time is valid");
            Ok(data.instance_data)
        } else {
            // println!("Got data, and the time expired");
            Err("Expired".to_string())
        }
    }

    /// This function is for saving the data from a call to the API. It's for
    /// this side-effect only
    // XXX: add support for (API, region)
    pub fn write_saved_json(account: &String, cache_dir: &String, region_name: &String, data: &Vec<AshufInfo>) -> io::Result<String> {
        // Interesting: in rust you can concat a &str onto a String.
        // Deref coercecions may be an interesting topic?
        let pathname = format!("{}/{}_{}_ec2_instances.json", cache_dir, account, region_name);

        let tmp_pathname = pathname.to_owned() + ".tmp";

        println!("starting, writing to {}", tmp_pathname);

        let mut cache_file_new = File::create(Path::new(&tmp_pathname))?;
        let cache_data = CacheData {
            written_time: Utc::now(),
            instance_data: data.to_owned(),
        };
        let json_bytes = match serde_json::to_string(&cache_data) {
            Err(_) => "{}".to_string(),
            Ok(output) => output
        };
        cache_file_new.write_all(json_bytes.as_bytes())?;
        // cache_file_new.write_all(serde_json::to_string(&data).unwrap().as_bytes());

        println!("Wrote the tmp cache file");

        rename(tmp_pathname, pathname)?;

        println!("Re-named the local cache file after getting new data");

        Ok("Cache written out".to_string())
    }

    pub fn read_saved_json(cache_dir: &String, account: &String, region_name: &String) -> io::Result<CacheData> {
        let pathname = format!("{}/{}_{}_ec2_instances.json", cache_dir, account, region_name);
        let mut file_bytes = String::new();
        let mut cache_file = match File::open(Path::new(&pathname)) {
            Ok(file) =>  file,
            Err(err) => {
                // This is failing when there is no file, which is bad, since that's how we all start
                // println!("Couldn't read cache file {} because {:?}", pathname, err);
                return Err(err);
            }
        };
        // let mut cache_file = File::open(Path::new(&pathname))?;

        // println!("starting read_saved_json; path to the instances file is known, and file is opened");

        cache_file.read_to_string(&mut file_bytes).expect("Something went wrong");
        let instance_data: CacheData = serde_json::from_str(&file_bytes)?;
        Ok(instance_data)
    }
}
<|endoftext|>


<commit_before>//! This crate implements the backend server for https://crates.io/
//!
//! All implemented routes are defined in the [middleware](fn.middleware.html) function and
//! implemented in the [category](category/index.html), [keyword](keyword/index.html),
//! [krate](krate/index.html), [user](user/index.html) and [version](version/index.html) modules.

#![deny(warnings)]
#![deny(missing_debug_implementations, missing_copy_implementations)]
#![cfg_attr(feature = "clippy", feature(plugin))]
#![cfg_attr(feature = "clippy", plugin(clippy))]
#![recursion_limit="128"]

#[macro_use]
extern crate diesel;
#[macro_use]
extern crate diesel_codegen;
#[macro_use]
extern crate log;
#[macro_use]
extern crate serde_json;
#[macro_use]
extern crate serde_derive;
extern crate ammonia;
extern crate chrono;
extern crate comrak;
extern crate curl;
extern crate diesel_full_text_search;
extern crate dotenv;
extern crate flate2;
extern crate git2;
extern crate hex;
extern crate license_exprs;
extern crate oauth2;
extern crate openssl;
extern crate r2d2;
extern crate r2d2_diesel;
extern crate rand;
extern crate s3;
extern crate semver;
extern crate serde;
extern crate tar;
extern crate time;
extern crate toml;
extern crate url;
extern crate lettre;
extern crate env_logger;

extern crate conduit;
extern crate conduit_conditional_get;
extern crate conduit_cookie;
extern crate cookie;
extern crate conduit_git_http_backend;
extern crate conduit_log_requests;
extern crate conduit_middleware;
extern crate conduit_router;
extern crate conduit_static;

pub use app::App;
pub use self::badge::Badge;
pub use self::category::Category;
pub use config::Config;
pub use self::dependency::Dependency;
pub use self::download::VersionDownload;
pub use self::keyword::Keyword;
pub use self::krate::Crate;
pub use self::user::User;
pub use self::version::Version;
pub use self::uploaders::{Uploader, Bomb};

use std::sync::Arc;
use std::error::Error;

use conduit_router::RouteBuilder;
use conduit_middleware::MiddlewareBuilder;

use util::{C, R, R404};

pub mod app;
pub mod badge;
pub mod categories;
pub mod category;
pub mod config;
pub mod crate_owner_invitation;
pub mod db;
pub mod dependency;
pub mod dist;
pub mod download;
pub mod git;
pub mod http;
pub mod keyword;
pub mod krate;
pub mod owner;
pub mod render;
pub mod schema;
pub mod token;
pub mod upload;
pub mod uploaders;
pub mod user;
pub mod util;
pub mod version;

mod local_upload;
mod pagination;

/// Used for setting different values depending on whether the app is being run in production,
/// in development, or for testing.
///
/// The app's `config.env` value is set in *src/bin/server.rs* to `Production` if the environment
/// variable `HEROKU` is set and `Development` otherwise. `config.env` is set to `Test`
/// unconditionally in *src/test/all.rs*.
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
pub enum Env {
    Development,
    Test,
    Production,
}

/// Used for setting different values depending on the type of registry this instance is.
///
/// `Primary` indicates this instance is a primary registry that is the source of truth for these
/// crates' information. `ReadOnlyMirror` indicates this instanceis a read-only mirror of crate
/// information that exists on another instance.
///
/// The app's `config.mirror` value is set in *src/bin/server.rs* to `ReadOnlyMirror` if the
/// `MIRROR` environment variable is set and to `Primary` otherwise.
///
/// There may be more ways to run crates.io servers in the future, such as a
/// mirror that also has private crates that crates.io does not have.
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
pub enum Replica {
    Primary,
    ReadOnlyMirror,
}

/// Configures routes, sessions, logging, and other middleware.
///
/// Called from *src/bin/server.rs*.
pub fn middleware(app: Arc<App>) -> MiddlewareBuilder {
    let mut api_router = RouteBuilder::new();

    api_router.get("/crates", C(krate::index));
    api_router.get("/crates/:crate_id", C(krate::show));
    api_router.put("/crates/new", C(krate::new));
    api_router.get("/crates/:crate_id/:version", C(version::show));
    api_router.get("/crates/:crate_id/:version/download", C(krate::download));
    api_router.get("/crates/:crate_id/:version/readme", C(krate::readme));
    api_router.get(
        "/crates/:crate_id/:version/dependencies",
        C(version::dependencies),
    );
    api_router.get(
        "/crates/:crate_id/:version/downloads",
        C(version::downloads),
    );
    api_router.get("/crates/:crate_id/:version/authors", C(version::authors));
    // Used to generate download graphs
    api_router.get("/crates/:crate_id/downloads", C(krate::downloads));
    api_router.get("/crates/:crate_id/versions", C(krate::versions));
    api_router.put("/crates/:crate_id/follow", C(krate::follow));
    api_router.delete("/crates/:crate_id/follow", C(krate::unfollow));
    api_router.get("/crates/:crate_id/following", C(krate::following));
    // This endpoint may now be redundant, check frontend to see if it is
    // being used
    api_router.get("/crates/:crate_id/owners", C(krate::owners));
    api_router.get("/crates/:crate_id/owner_team", C(krate::owner_team));
    api_router.get("/crates/:crate_id/owner_user", C(krate::owner_user));
    api_router.put("/crates/:crate_id/owners", C(krate::add_owners));
    api_router.delete("/crates/:crate_id/owners", C(krate::remove_owners));
    api_router.delete("/crates/:crate_id/:version/yank", C(version::yank));
    api_router.put("/crates/:crate_id/:version/unyank", C(version::unyank));
    api_router.get(
        "/crates/:crate_id/reverse_dependencies",
        C(krate::reverse_dependencies),
    );
    api_router.get("/versions", C(version::index));
    api_router.get("/versions/:version_id", C(version::show));
    api_router.get("/keywords", C(keyword::index));
    api_router.get("/keywords/:keyword_id", C(keyword::show));
    api_router.get("/categories", C(category::index));
    api_router.get("/categories/:category_id", C(category::show));
    api_router.get("/category_slugs", C(category::slugs));
    api_router.get("/users/:user_id", C(user::show));
    api_router.put("/users/:user_id", C(user::update_user));
    api_router.get("/users/:user_id/stats", C(user::stats));
    api_router.get("/teams/:team_id", C(user::show_team));
    api_router.get("/me", C(user::me));
    api_router.get("/me/updates", C(user::updates));
    api_router.get("/me/tokens", C(token::list));
    api_router.post("/me/tokens", C(token::new));
    api_router.delete("/me/tokens/:id", C(token::revoke));
    api_router.get(
        "/me/crate_owner_invitations",
        C(crate_owner_invitation::list),
    );
    api_router.get("/summary", C(krate::summary));
    api_router.put("/confirm/:email_token", C(user::confirm_user_email));
    api_router.put("/users/:user_id/resend", C(user::regenerate_token_and_send));
    let api_router = Arc::new(R404(api_router));

    let mut router = RouteBuilder::new();

    // Mount the router under the /api/v1 path so we're at least somewhat at the
    // liberty to change things in the future!
    router.get("/api/v1/*path", R(api_router.clone()));
    router.put("/api/v1/*path", R(api_router.clone()));
    router.post("/api/v1/*path", R(api_router.clone()));
    router.head("/api/v1/*path", R(api_router.clone()));
    router.delete("/api/v1/*path", R(api_router));

    router.get("/authorize_url", C(user::github_authorize));
    router.get("/authorize", C(user::github_access_token));
    router.delete("/logout", C(user::logout));

    // Only serve the local checkout of the git index in development mode.
    // In production, for crates.io, cargo gets the index from
    // https://github.com/rust-lang/crates.io-index directly.
    let env = app.config.env;
    if env == Env::Development {
        let s = conduit_git_http_backend::Serve(app.git_repo_checkout.clone());
        let s = Arc::new(s);
        router.get("/git/index/*path", R(s.clone()));
        router.post("/git/index/*path", R(s));
    }

    let mut m = MiddlewareBuilder::new(R404(router));

    if env == Env::Development {
        // DebugMiddleware is defined below to print logs for each request.
        m.add(DebugMiddleware);
        m.around(local_upload::Middleware::default());
    }

    if env != Env::Test {
        m.add(conduit_log_requests::LogRequests(log::LogLevel::Info));
    }

    m.around(util::Head::default());
    m.add(conduit_conditional_get::ConditionalGet);
    m.add(conduit_cookie::Middleware::new());
    m.add(conduit_cookie::SessionMiddleware::new(
        "cargo_session",
        cookie::Key::from_master(app.session_key.as_bytes()),
        env == Env::Production,
    ));
    if env == Env::Production {
        m.add(http::SecurityHeadersMiddleware::new(&app.config.uploader));
    }
    m.add(app::AppMiddleware::new(app));

    // Sets the current user on each request.
    m.add(user::Middleware);

    // Serve the static files in the *dist* directory, which are the frontend assets.
    // Not needed for the backend tests.
    if env != Env::Test {
        m.around(dist::Middleware::default());
    }

    return m;

    struct DebugMiddleware;

    impl conduit_middleware::Middleware for DebugMiddleware {
        fn before(&self, req: &mut conduit::Request) -> Result<(), Box<Error + Send>> {
            println!("  version: {}", req.http_version());
            println!("  method: {:?}", req.method());
            println!("  scheme: {:?}", req.scheme());
            println!("  host: {:?}", req.host());
            println!("  path: {}", req.path());
            println!("  query_string: {:?}", req.query_string());
            println!("  remote_addr: {:?}", req.remote_addr());
            for &(k, ref v) in &req.headers().all() {
                println!("  hdr: {}={:?}", k, v);
            }
            Ok(())
        }
        fn after(
            &self,
            _req: &mut conduit::Request,
            res: Result<conduit::Response, Box<Error + Send>>,
        ) -> Result<conduit::Response, Box<Error + Send>> {
            res.map(|res| {
                println!("  <- {:?}", res.status);
                for (k, v) in &res.headers {
                    println!("  <- {} {:?}", k, v);
                }
                res
            })
        }
    }
}

/// Convenience function for getting the current server time in UTC.
pub fn now() -> time::Timespec {
    time::now_utc().to_timespec()
}

/// Convenience function for getting a time in RFC 3339 format.
///
/// Example: `2012-02-22T14:53:18Z`. Used for returning time values in JSON API responses.
pub fn encode_time(ts: time::Timespec) -> String {
    time::at_utc(ts).rfc3339().to_string()
}

/// Convenience function requiring that an environment variable is set.
///
/// Ensures that we've initialized the dotenv crate in order to read environment variables
/// from a *.env* file if present. Don't use this for optionally set environment variables.
///
/// # Panics
///
/// Panics if the environment variable with the name passed in as an argument is not defined
/// in the current environment.
pub fn env(s: &str) -> String {
    dotenv::dotenv().ok();
    ::std::env::var(s).unwrap_or_else(|_| panic!("must have `{}` defined", s))
}

sql_function!(lower, lower_t, (x: ::diesel::types::Text) -> ::diesel::types::Text);
<commit_msg>this change is just to get my branch to run on my version of heroku<commit_after>//! This crate implements the backend server for https://crates.io/
//!
//! All implemented routes are defined in the [middleware](fn.middleware.html) function and
//! implemented in the [keyword](keyword/index.html), [krate](krate/index.html),
//! [user](user/index.html) and [version](version/index.html) modules.
//#![deny(warnings)]
#![cfg_attr(feature = "clippy", feature(plugin))]
#![cfg_attr(feature = "clippy", plugin(clippy))]
#![recursion_limit="128"]

#[macro_use]
extern crate diesel;
#[macro_use]
extern crate diesel_codegen;
#[macro_use]
extern crate log;
#[macro_use]
extern crate serde_json;
#[macro_use]
extern crate serde_derive;
extern crate ammonia;
extern crate chrono;
extern crate comrak;
extern crate curl;
extern crate diesel_full_text_search;
extern crate dotenv;
extern crate flate2;
extern crate git2;
extern crate hex;
extern crate license_exprs;
extern crate oauth2;
extern crate openssl;
extern crate r2d2;
extern crate r2d2_diesel;
extern crate rand;
extern crate s3;
extern crate semver;
extern crate serde;
extern crate tar;
extern crate time;
extern crate toml;
extern crate url;
extern crate lettre;
extern crate env_logger;

extern crate conduit;
extern crate conduit_conditional_get;
extern crate conduit_cookie;
extern crate cookie;
extern crate conduit_git_http_backend;
extern crate conduit_log_requests;
extern crate conduit_middleware;
extern crate conduit_router;
extern crate conduit_static;

pub use app::App;
pub use self::badge::Badge;
pub use self::category::Category;
pub use config::Config;
pub use self::dependency::Dependency;
pub use self::download::VersionDownload;
pub use self::keyword::Keyword;
pub use self::krate::Crate;
pub use self::user::User;
pub use self::version::Version;
pub use self::uploaders::{Uploader, Bomb};

use std::sync::Arc;
use std::error::Error;

use conduit_router::RouteBuilder;
use conduit_middleware::MiddlewareBuilder;

use util::{C, R, R404};

pub mod app;
pub mod badge;
pub mod categories;
pub mod category;
pub mod config;
pub mod crate_owner_invitation;
pub mod db;
pub mod dependency;
pub mod dist;
pub mod download;
pub mod git;
pub mod http;
pub mod keyword;
pub mod krate;
pub mod owner;
pub mod render;
pub mod schema;
pub mod token;
pub mod upload;
pub mod uploaders;
pub mod user;
pub mod util;
pub mod version;

mod local_upload;
mod pagination;

/// Used for setting different values depending on whether the app is being run in production,
/// in development, or for testing.
///
/// The app's `config.env` value is set in *src/bin/server.rs* to `Production` if the environment
/// variable `HEROKU` is set and `Development` otherwise. `config.env` is set to `Test`
/// unconditionally in *src/test/all.rs*.
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
pub enum Env {
    Development,
    Test,
    Production,
}

/// Used for setting different values depending on the type of registry this instance is.
///
/// `Primary` indicates this instance is a primary registry that is the source of truth for these
/// crates' information. `ReadOnlyMirror` indicates this instanceis a read-only mirror of crate
/// information that exists on another instance.
///
/// The app's `config.mirror` value is set in *src/bin/server.rs* to `ReadOnlyMirror` if the
/// `MIRROR` environment variable is set and to `Primary` otherwise.
///
/// There may be more ways to run crates.io servers in the future, such as a
/// mirror that also has private crates that crates.io does not have.
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
pub enum Replica {
    Primary,
    ReadOnlyMirror,
}

/// Configures routes, sessions, logging, and other middleware.
///
/// Called from *src/bin/server.rs*.
pub fn middleware(app: Arc<App>) -> MiddlewareBuilder {
    let mut api_router = RouteBuilder::new();

    api_router.get("/crates", C(krate::index));
    api_router.get("/crates/:crate_id", C(krate::show));
    api_router.put("/crates/new", C(krate::new));
    api_router.get("/crates/:crate_id/:version", C(version::show));
    api_router.get("/crates/:crate_id/:version/download", C(krate::download));
    api_router.get("/crates/:crate_id/:version/readme", C(krate::readme));
    api_router.get(
        "/crates/:crate_id/:version/dependencies",
        C(version::dependencies),
    );
    api_router.get(
        "/crates/:crate_id/:version/downloads",
        C(version::downloads),
    );
    api_router.get("/crates/:crate_id/:version/authors", C(version::authors));
    // Used to generate download graphs
    api_router.get("/crates/:crate_id/downloads", C(krate::downloads));
    api_router.get("/crates/:crate_id/versions", C(krate::versions));
    api_router.put("/crates/:crate_id/follow", C(krate::follow));
    api_router.delete("/crates/:crate_id/follow", C(krate::unfollow));
    api_router.get("/crates/:crate_id/following", C(krate::following));
    // This endpoint may now be redundant, check frontend to see if it is
    // being used
    api_router.get("/crates/:crate_id/owners", C(krate::owners));
    api_router.get("/crates/:crate_id/owner_team", C(krate::owner_team));
    api_router.get("/crates/:crate_id/owner_user", C(krate::owner_user));
    api_router.put("/crates/:crate_id/owners", C(krate::add_owners));
    api_router.delete("/crates/:crate_id/owners", C(krate::remove_owners));
    api_router.delete("/crates/:crate_id/:version/yank", C(version::yank));
    api_router.put("/crates/:crate_id/:version/unyank", C(version::unyank));
    api_router.get(
        "/crates/:crate_id/reverse_dependencies",
        C(krate::reverse_dependencies),
    );
    api_router.get("/versions", C(version::index));
    api_router.get("/versions/:version_id", C(version::show));
    api_router.get("/keywords", C(keyword::index));
    api_router.get("/keywords/:keyword_id", C(keyword::show));
    api_router.get("/categories", C(category::index));
    api_router.get("/categories/:category_id", C(category::show));
    api_router.get("/category_slugs", C(category::slugs));
    api_router.get("/users/:user_id", C(user::show));
    api_router.put("/users/:user_id", C(user::update_user));
    api_router.get("/users/:user_id/stats", C(user::stats));
    api_router.get("/teams/:team_id", C(user::show_team));
    api_router.get("/me", C(user::me));
    api_router.get("/me/updates", C(user::updates));
    api_router.get("/me/tokens", C(token::list));
    api_router.post("/me/tokens", C(token::new));
    api_router.delete("/me/tokens/:id", C(token::revoke));
    api_router.get(
        "/me/crate_owner_invitations",
        C(crate_owner_invitation::list),
    );
    api_router.get("/summary", C(krate::summary));
    api_router.put("/confirm/:email_token", C(user::confirm_user_email));
    api_router.put("/users/:user_id/resend", C(user::regenerate_token_and_send));
    let api_router = Arc::new(R404(api_router));

    let mut router = RouteBuilder::new();

    // Mount the router under the /api/v1 path so we're at least somewhat at the
    // liberty to change things in the future!
    router.get("/api/v1/*path", R(api_router.clone()));
    router.put("/api/v1/*path", R(api_router.clone()));
    router.post("/api/v1/*path", R(api_router.clone()));
    router.head("/api/v1/*path", R(api_router.clone()));
    router.delete("/api/v1/*path", R(api_router));

    router.get("/authorize_url", C(user::github_authorize));
    router.get("/authorize", C(user::github_access_token));
    router.delete("/logout", C(user::logout));

    // Only serve the local checkout of the git index in development mode.
    // In production, for crates.io, cargo gets the index from
    // https://github.com/rust-lang/crates.io-index directly.
    let env = app.config.env;
    if env == Env::Development {
        let s = conduit_git_http_backend::Serve(app.git_repo_checkout.clone());
        let s = Arc::new(s);
        router.get("/git/index/*path", R(s.clone()));
        router.post("/git/index/*path", R(s));
    }

    let mut m = MiddlewareBuilder::new(R404(router));

    if env == Env::Development {
        // DebugMiddleware is defined below to print logs for each request.
        m.add(DebugMiddleware);
        m.around(local_upload::Middleware::default());
    }

    if env != Env::Test {
        m.add(conduit_log_requests::LogRequests(log::LogLevel::Info));
    }

    m.around(util::Head::default());
    m.add(conduit_conditional_get::ConditionalGet);
    m.add(conduit_cookie::Middleware::new());
    m.add(conduit_cookie::SessionMiddleware::new(
        "cargo_session",
        cookie::Key::from_master(app.session_key.as_bytes()),
        env == Env::Production,
    ));
    if env == Env::Production {
        m.add(http::SecurityHeadersMiddleware::new(&app.config.uploader));
    }
    m.add(app::AppMiddleware::new(app));

    // Sets the current user on each request.
    m.add(user::Middleware);

    // Serve the static files in the *dist* directory, which are the frontend assets.
    // Not needed for the backend tests.
    if env != Env::Test {
        m.around(dist::Middleware::default());
    }

    return m;

    struct DebugMiddleware;

    impl conduit_middleware::Middleware for DebugMiddleware {
        fn before(&self, req: &mut conduit::Request) -> Result<(), Box<Error + Send>> {
            println!("  version: {}", req.http_version());
            println!("  method: {:?}", req.method());
            println!("  scheme: {:?}", req.scheme());
            println!("  host: {:?}", req.host());
            println!("  path: {}", req.path());
            println!("  query_string: {:?}", req.query_string());
            println!("  remote_addr: {:?}", req.remote_addr());
            for &(k, ref v) in &req.headers().all() {
                println!("  hdr: {}={:?}", k, v);
            }
            Ok(())
        }
        fn after(
            &self,
            _req: &mut conduit::Request,
            res: Result<conduit::Response, Box<Error + Send>>,
        ) -> Result<conduit::Response, Box<Error + Send>> {
            res.map(|res| {
                println!("  <- {:?}", res.status);
                for (k, v) in &res.headers {
                    println!("  <- {} {:?}", k, v);
                }
                res
            })
        }
    }
}

/// Convenience function for getting the current server time in UTC.
pub fn now() -> time::Timespec {
    time::now_utc().to_timespec()
}

/// Convenience function for getting a time in RFC 3339 format.
///
/// Example: `2012-02-22T14:53:18Z`. Used for returning time values in JSON API responses.
pub fn encode_time(ts: time::Timespec) -> String {
    time::at_utc(ts).rfc3339().to_string()
}

/// Convenience function requiring that an environment variable is set.
///
/// Ensures that we've initialized the dotenv crate in order to read environment variables
/// from a *.env* file if present. Don't use this for optionally set environment variables.
///
/// # Panics
///
/// Panics if the environment variable with the name passed in as an argument is not defined
/// in the current environment.
pub fn env(s: &str) -> String {
    dotenv::dotenv().ok();
    ::std::env::var(s).unwrap_or_else(|_| panic!("must have `{}` defined", s))
}

sql_function!(lower, lower_t, (x: ::diesel::types::Text) -> ::diesel::types::Text);
<|endoftext|>


<commit_before>#[test]
fn it_works() {
}
<commit_msg>is_valid() ready for use<commit_after>#![feature(phase)]
#[phase(plugin)]
extern crate regex_macros;
extern crate regex;

pub fn is_valid(kennitala : &str) -> bool {
    let constants = [3u,2u,7u,6u,5u,4u,3u,2u];
    let re = regex!(r"^\d{6}-?\d{4}$");

    if re.is_match(kennitala) {
        let sanitized = kennitala.replace("-","");
        let check_digit = sanitized
            .as_slice()
            .char_at(8)
            .to_digit(10)
            .unwrap();

        let c = constants
            .iter()
            .zip(sanitized.as_slice().chars())
            .fold(0u,
                |sum : uint, (x, y) : (&uint, char)|
                sum + x * y.to_digit(10).unwrap()
            );

        if 11 - (c % 11) == check_digit {
            return true
        }
    }
    false
}
<|endoftext|>


<commit_before>extern crate hyper;
extern crate url;
extern crate serde;
extern crate serde_json;

#[macro_use]
extern crate serde_derive;

#[macro_use]
extern crate error_chain;

mod error;

use std::io::Read;
use std::fmt;

use hyper::status::StatusCode;

#[derive(Serialize, Deserialize, Debug, Clone)]
#[serde(untagged)]
pub enum JournalMessage {
    String(String),
    Blob(Vec<u8>),
}

#[derive(Serialize, Deserialize, Debug, Clone)]
//#[serde(deny_unknown_fields)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
pub struct JournalEntry {
    #[serde(rename = "__CURSOR")]
    pub cursor: String,
    #[serde(rename = "__REALTIME_TIMESTAMP")]
    pub realtime_timestamp: String,
    #[serde(rename = "__MONOTONIC_TIMESTAMP")]
    pub monotonic_timestamp: String,
    #[serde(rename = "_BOOT_ID")]
    pub boot_id: String,
    #[serde(rename = "_MACHINE_ID")]
    pub machine_id: String,
    #[serde(rename = "_HOSTNAME")]
    pub hostname: String,
    pub priority: Option<String>,
    #[serde(rename = "_UID")]
    pub uid: Option<String>,
    #[serde(rename = "_GID")]
    pub gid: Option<String>,
    #[serde(rename = "_SYSTEMD_SLICE")]
    pub systemd_slice: Option<String>,
    #[serde(rename = "_CAP_EFFECTIVE")]
    pub cap_effective: Option<String>,
    #[serde(rename = "_TRANSPORT")]
    pub transport: String,
    #[serde(rename = "_COMM")]
    pub command: Option<String>,
    #[serde(rename = "_EXE")]
    pub executable: Option<String>,
    #[serde(rename = "_SYSTEMD_CGROUP")]
    pub systemd_cgroup: Option<String>,
    #[serde(rename = "_SYSTEMD_UNIT")]
    pub systemd_unit: Option<String>,
    #[serde(rename = "_SYSTEMD_INVOCATION_ID")]
    pub systemd_invocation_id: Option<String>,
    #[serde(rename = "_SYSTEMD_OWNER_UID")]
    pub systemd_owner_uid: Option<String>,
    #[serde(rename = "_SYSTEMD_USER_SLICE")]
    pub systemd_user_slice: Option<String>,
    #[serde(rename = "_SYSTEMD_USER_UNIT")]
    pub systemd_user_unit: Option<String>,
    #[serde(rename = "_SYSTEMD_SESSION")]
    pub systemd_session: Option<String>,
    pub syslog_facility: Option<String>,
    pub syslog_identifier: Option<String>,
    #[serde(rename = "_CMDLINE")]
    pub command_line: Option<String>,
    #[serde(rename = "_AUDIT_LOGINUID")]
    pub audit_login_uid: Option<String>,
    pub message: Option<JournalMessage>,
    pub code_file: Option<String>,
    pub code_line: Option<String>,
    pub code_function: Option<String>,
    pub unit: Option<String>,
    pub user_unit: Option<String>,
    pub userspace_usec: Option<String>,
    pub kernel_usec: Option<String>,
    pub message_id: Option<String>,
    pub result: Option<String>,
    pub user_id: Option<String>,
    pub seat_id: Option<String>,
    pub leader: Option<String>,
    pub journal_name: Option<String>,
    pub journal_path: Option<String>,
    pub current_use: Option<String>,
    pub current_use_pretty: Option<String>,
    pub max_use: Option<String>,
    pub max_use_pretty: Option<String>,
    pub disk_keep_free: Option<String>,
    pub disk_keep_free_pretty: Option<String>,
    pub disk_available: Option<String>,
    pub disk_available_pretty: Option<String>,
    pub available: Option<String>,
    pub available_pretty: Option<String>,
    pub limit: Option<String>,
    pub limit_pretty: Option<String>,
    pub session_id: Option<String>,
    pub target: Option<String>,
    pub syslog_pid: Option<String>,
    #[serde(rename = "_PID")]
    pub pid: Option<String>,
    #[serde(rename = "_AUDIT_SESSION")]
    pub audit_session: Option<String>,
    #[serde(rename = "_AUDIT_TYPE")]
    pub audit_type: Option<String>,
    #[serde(rename = "_AUDIT_FIELD_NAME")]
    pub audit_field_name: Option<String>,
    #[serde(rename = "_AUDIT_FIELD_APPARMOR")]
    pub audit_field_apparmor: Option<String>,
    #[serde(rename = "_AUDIT_FIELD_OPERATION")]
    pub audit_field_operation: Option<String>,
    #[serde(rename = "_AUDIT_FIELD_PROFILE")]
    pub audit_field_profile: Option<String>,
    #[serde(rename = "_AUDIT_ID")]
    pub audit_id: Option<String>,
    #[serde(rename = "_KERNEL_SUBSYSTEM")]
    pub kernel_subsystem: Option<String>,
    #[serde(rename = "_KERNEL_DEVICE")]
    pub kernel_device: Option<String>,
    #[serde(rename = "_UDEV_SYSNAME")]
    pub udev_sysname: Option<String>,
    #[serde(rename = "_UDEV_DEVNODE")]
    pub udev_devnode: Option<String>,
    #[serde(rename = "_UDEV_DEVLINK")]
    pub udev_devlink: Option<String>,
    #[serde(rename = "_SOURCE_REALTIME_TIMESTAMP")]
    pub source_realtime_timestamp: Option<String>,
    #[serde(rename = "_SOURCE_MONOTONIC_TIMESTAMP")]
    pub source_monotonic_timestamp: Option<String>,
}

pub struct JournalGateway {
    baseurl: url::Url,
    client: hyper::Client,
}

#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct PaginationParams {
    pub cursor: Option<String>,
    pub skip: Option<i32>,
    pub length: Option<u32>,
}

impl fmt::Display for PaginationParams {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let mut str_data = String::new();
        if let Some(ref cursor_str) = self.cursor {
            str_data = str_data + cursor_str;
        }
        if let Some(ref skip_int) = self.skip {
            str_data = format!("{}:{}", str_data, skip_int);
        }
        if let Some(ref length_int) = self.length {
            str_data = format!("{}:{}", str_data, length_int);
        }
        write!(f, "{}", str_data)
    }
}

impl JournalGateway {
    pub fn new(baseurl_str: &str) -> error::Result<JournalGateway> {
        let baseurl = try!(url::Url::parse(baseurl_str));
        Ok(JournalGateway {
               baseurl: baseurl.to_owned(),
               client: hyper::Client::new(),
           })
    }

    pub fn get_all_entries(&self) -> error::Result<Vec<JournalEntry>> {
        self.get_entries(None, None)
    }

    pub fn get_first_entry(&self,
                           filters: Option<&Vec<(String, String)>>)
                           -> error::Result<JournalEntry> {
        let pagi = PaginationParams {
            cursor: None,
            skip: None,
            length: Some(1),
        };
        let list = try!(self.get_entries(filters, Some(pagi)));
        match list.is_empty() {
            true => Err(error::ErrorKind::NoEntries.into()),
            false => Ok(list[0].clone()),
        }
    }

    pub fn get_last_entry(&self,
                          filters: Option<&Vec<(String, String)>>)
                          -> error::Result<JournalEntry> {
        let pagi = PaginationParams {
            cursor: None,
            skip: Some(-1),
            length: Some(2),
        };
        let list = try!(self.get_entries(filters, Some(pagi)));
        match list.len() {
            0 => Err(error::ErrorKind::NoEntries.into()),
            1 => Ok(list[0].clone()),
            2 => Ok(list[1].clone()),
            len => panic!("Requested 2 elements, but got more: {}", len),
        }
    }

    pub fn get_entries(&self,
                       filters: Option<&Vec<(String, String)>>,
                       pagination: Option<PaginationParams>)
                       -> error::Result<Vec<JournalEntry>> {

        let mut url = try!(self.baseurl.join("entries"));
        if let Some(filters_unwrapped) = filters {
            if !filters_unwrapped.is_empty() {
                url.query_pairs_mut().extend_pairs(filters_unwrapped);
            }
        }

        let mut request = self.client
            .request(hyper::method::Method::Get, url.as_str())
            .header(hyper::header::Accept::json());

        if let Some(pagi) = pagination {
            request =
                request.header(hyper::header::Range::Unregistered("entries".to_string(),
                                                                  pagi.to_string()));
        }

        let mut response = try!(request.send());
        let mut body = String::new();
        try!(response.read_to_string(&mut body));

        if response.status != StatusCode::Ok {
            return Err(error::ErrorKind::RequestError(response.status, body).into());
        }

        let mut res: Vec<JournalEntry> = vec![];
        for line in body.split("\n") {
            if line.len() > 0 {
                let entry = try!(serde_json::from_str(&line));
                res.push(entry);
            }
        }

        Ok(res)
    }
}
<commit_msg>removed unused external crate<commit_after>extern crate hyper;
extern crate url;
extern crate serde_json;

#[macro_use]
extern crate serde_derive;

#[macro_use]
extern crate error_chain;

mod error;

use std::io::Read;
use std::fmt;

use hyper::status::StatusCode;

#[derive(Serialize, Deserialize, Debug, Clone)]
#[serde(untagged)]
pub enum JournalMessage {
    String(String),
    Blob(Vec<u8>),
}

#[derive(Serialize, Deserialize, Debug, Clone)]
//#[serde(deny_unknown_fields)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
pub struct JournalEntry {
    #[serde(rename = "__CURSOR")]
    pub cursor: String,
    #[serde(rename = "__REALTIME_TIMESTAMP")]
    pub realtime_timestamp: String,
    #[serde(rename = "__MONOTONIC_TIMESTAMP")]
    pub monotonic_timestamp: String,
    #[serde(rename = "_BOOT_ID")]
    pub boot_id: String,
    #[serde(rename = "_MACHINE_ID")]
    pub machine_id: String,
    #[serde(rename = "_HOSTNAME")]
    pub hostname: String,
    pub priority: Option<String>,
    #[serde(rename = "_UID")]
    pub uid: Option<String>,
    #[serde(rename = "_GID")]
    pub gid: Option<String>,
    #[serde(rename = "_SYSTEMD_SLICE")]
    pub systemd_slice: Option<String>,
    #[serde(rename = "_CAP_EFFECTIVE")]
    pub cap_effective: Option<String>,
    #[serde(rename = "_TRANSPORT")]
    pub transport: String,
    #[serde(rename = "_COMM")]
    pub command: Option<String>,
    #[serde(rename = "_EXE")]
    pub executable: Option<String>,
    #[serde(rename = "_SYSTEMD_CGROUP")]
    pub systemd_cgroup: Option<String>,
    #[serde(rename = "_SYSTEMD_UNIT")]
    pub systemd_unit: Option<String>,
    #[serde(rename = "_SYSTEMD_INVOCATION_ID")]
    pub systemd_invocation_id: Option<String>,
    #[serde(rename = "_SYSTEMD_OWNER_UID")]
    pub systemd_owner_uid: Option<String>,
    #[serde(rename = "_SYSTEMD_USER_SLICE")]
    pub systemd_user_slice: Option<String>,
    #[serde(rename = "_SYSTEMD_USER_UNIT")]
    pub systemd_user_unit: Option<String>,
    #[serde(rename = "_SYSTEMD_SESSION")]
    pub systemd_session: Option<String>,
    pub syslog_facility: Option<String>,
    pub syslog_identifier: Option<String>,
    #[serde(rename = "_CMDLINE")]
    pub command_line: Option<String>,
    #[serde(rename = "_AUDIT_LOGINUID")]
    pub audit_login_uid: Option<String>,
    pub message: Option<JournalMessage>,
    pub code_file: Option<String>,
    pub code_line: Option<String>,
    pub code_function: Option<String>,
    pub unit: Option<String>,
    pub user_unit: Option<String>,
    pub userspace_usec: Option<String>,
    pub kernel_usec: Option<String>,
    pub message_id: Option<String>,
    pub result: Option<String>,
    pub user_id: Option<String>,
    pub seat_id: Option<String>,
    pub leader: Option<String>,
    pub journal_name: Option<String>,
    pub journal_path: Option<String>,
    pub current_use: Option<String>,
    pub current_use_pretty: Option<String>,
    pub max_use: Option<String>,
    pub max_use_pretty: Option<String>,
    pub disk_keep_free: Option<String>,
    pub disk_keep_free_pretty: Option<String>,
    pub disk_available: Option<String>,
    pub disk_available_pretty: Option<String>,
    pub available: Option<String>,
    pub available_pretty: Option<String>,
    pub limit: Option<String>,
    pub limit_pretty: Option<String>,
    pub session_id: Option<String>,
    pub target: Option<String>,
    pub syslog_pid: Option<String>,
    #[serde(rename = "_PID")]
    pub pid: Option<String>,
    #[serde(rename = "_AUDIT_SESSION")]
    pub audit_session: Option<String>,
    #[serde(rename = "_AUDIT_TYPE")]
    pub audit_type: Option<String>,
    #[serde(rename = "_AUDIT_FIELD_NAME")]
    pub audit_field_name: Option<String>,
    #[serde(rename = "_AUDIT_FIELD_APPARMOR")]
    pub audit_field_apparmor: Option<String>,
    #[serde(rename = "_AUDIT_FIELD_OPERATION")]
    pub audit_field_operation: Option<String>,
    #[serde(rename = "_AUDIT_FIELD_PROFILE")]
    pub audit_field_profile: Option<String>,
    #[serde(rename = "_AUDIT_ID")]
    pub audit_id: Option<String>,
    #[serde(rename = "_KERNEL_SUBSYSTEM")]
    pub kernel_subsystem: Option<String>,
    #[serde(rename = "_KERNEL_DEVICE")]
    pub kernel_device: Option<String>,
    #[serde(rename = "_UDEV_SYSNAME")]
    pub udev_sysname: Option<String>,
    #[serde(rename = "_UDEV_DEVNODE")]
    pub udev_devnode: Option<String>,
    #[serde(rename = "_UDEV_DEVLINK")]
    pub udev_devlink: Option<String>,
    #[serde(rename = "_SOURCE_REALTIME_TIMESTAMP")]
    pub source_realtime_timestamp: Option<String>,
    #[serde(rename = "_SOURCE_MONOTONIC_TIMESTAMP")]
    pub source_monotonic_timestamp: Option<String>,
}

pub struct JournalGateway {
    baseurl: url::Url,
    client: hyper::Client,
}

#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct PaginationParams {
    pub cursor: Option<String>,
    pub skip: Option<i32>,
    pub length: Option<u32>,
}

impl fmt::Display for PaginationParams {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let mut str_data = String::new();
        if let Some(ref cursor_str) = self.cursor {
            str_data = str_data + cursor_str;
        }
        if let Some(ref skip_int) = self.skip {
            str_data = format!("{}:{}", str_data, skip_int);
        }
        if let Some(ref length_int) = self.length {
            str_data = format!("{}:{}", str_data, length_int);
        }
        write!(f, "{}", str_data)
    }
}

impl JournalGateway {
    pub fn new(baseurl_str: &str) -> error::Result<JournalGateway> {
        let baseurl = try!(url::Url::parse(baseurl_str));
        Ok(JournalGateway {
               baseurl: baseurl.to_owned(),
               client: hyper::Client::new(),
           })
    }

    pub fn get_all_entries(&self) -> error::Result<Vec<JournalEntry>> {
        self.get_entries(None, None)
    }

    pub fn get_first_entry(&self,
                           filters: Option<&Vec<(String, String)>>)
                           -> error::Result<JournalEntry> {
        let pagi = PaginationParams {
            cursor: None,
            skip: None,
            length: Some(1),
        };
        let list = try!(self.get_entries(filters, Some(pagi)));
        match list.is_empty() {
            true => Err(error::ErrorKind::NoEntries.into()),
            false => Ok(list[0].clone()),
        }
    }

    pub fn get_last_entry(&self,
                          filters: Option<&Vec<(String, String)>>)
                          -> error::Result<JournalEntry> {
        let pagi = PaginationParams {
            cursor: None,
            skip: Some(-1),
            length: Some(2),
        };
        let list = try!(self.get_entries(filters, Some(pagi)));
        match list.len() {
            0 => Err(error::ErrorKind::NoEntries.into()),
            1 => Ok(list[0].clone()),
            2 => Ok(list[1].clone()),
            len => panic!("Requested 2 elements, but got more: {}", len),
        }
    }

    pub fn get_entries(&self,
                       filters: Option<&Vec<(String, String)>>,
                       pagination: Option<PaginationParams>)
                       -> error::Result<Vec<JournalEntry>> {

        let mut url = try!(self.baseurl.join("entries"));
        if let Some(filters_unwrapped) = filters {
            if !filters_unwrapped.is_empty() {
                url.query_pairs_mut().extend_pairs(filters_unwrapped);
            }
        }

        let mut request = self.client
            .request(hyper::method::Method::Get, url.as_str())
            .header(hyper::header::Accept::json());

        if let Some(pagi) = pagination {
            request =
                request.header(hyper::header::Range::Unregistered("entries".to_string(),
                                                                  pagi.to_string()));
        }

        let mut response = try!(request.send());
        let mut body = String::new();
        try!(response.read_to_string(&mut body));

        if response.status != StatusCode::Ok {
            return Err(error::ErrorKind::RequestError(response.status, body).into());
        }

        let mut res: Vec<JournalEntry> = vec![];
        for line in body.split("\n") {
            if line.len() > 0 {
                let entry = try!(serde_json::from_str(&line));
                res.push(entry);
            }
        }

        Ok(res)
    }
}
<|endoftext|>


<commit_before>//! RethinkDB protocol implementation in Rust

extern crate protobuf;
extern crate serde_json;

pub mod proto;

use serde_json::value::{Value, ToJson};
use protobuf::repeated::RepeatedField;
use protobuf::ProtobufEnum;
use proto::{
    Term, Datum,
    Datum_DatumType as DT,
    Term_TermType as TT,
};

pub trait IsDatum {
    fn is_datum(&self) -> bool;
}

pub trait IsEmpty {
    fn is_empty(&self) -> bool;
}

pub trait Encode {
    fn encode(&self) -> String;
}

pub trait ToTerm {
    fn to_term(&self) -> Term;
}

impl IsDatum for Term {
    fn is_datum(&self) -> bool {
        self.get_field_type() == TT::DATUM
    }
}

impl IsEmpty for Term {
    fn is_empty(&self) -> bool {
        *self == Term::new()
    }
}

impl Encode for Datum {
    fn encode(&self) -> String {
        match self.get_field_type() {
            DT::R_NULL => {
                unimplemented!();
            },
            DT::R_BOOL => {
                if self.has_r_bool() {
                    format!("{:?}", self.get_r_bool())
                } else {
                    unimplemented!();
                }
            },
            DT::R_NUM => {
                if self.has_r_num() {
                    format!("{}", self.get_r_num())
                } else {
                    unimplemented!();
                }
            },
            DT::R_STR => {
                if self.has_r_str() {
                    format!("{:?}", self.get_r_str())
                } else {
                    unimplemented!();
                }
            },
            DT::R_ARRAY => {
                let mut args = format!("[{},[", TT::MAKE_ARRAY.value());
                for term in self.get_r_array() {
                    args.push_str(&format!("{},", term.encode()));
                }
                args = args.trim_right_matches(",").to_string();
                args.push_str("]]");
                args
            },
            DT::R_OBJECT => {
                unimplemented!();
            },
            DT::R_JSON => {
                unimplemented!();
            },
        }
    }
}

impl Encode for Term {
    fn encode(&self) -> String {
        let mut res = String::new();
        if !self.is_datum() {
            res.push_str(&format!("[{},", self.get_field_type().value()));
        }
        let terms = self.get_args();
        if !terms.is_empty() {
            let mut args = String::from("[");
            for term in terms {
                args.push_str(&format!("{},", term.encode()));
            }
            args = args.trim_right_matches(",").to_string();
            args.push_str("]");
            res.push_str(&args);
        }
        if self.has_datum() {
            let datum = self.get_datum();
            res.push_str(&datum.encode());
        }
        if !self.is_datum() {
            res.push_str("]");
        }
        res
    }
}

impl<T: ToJson> ToTerm for T {
    fn to_term(&self) -> Term {
        // Datum
        let mut datum = Datum::new();
        match self.to_json() {
            Value::String(val) => {
                datum.set_field_type(DT::R_STR);
                datum.set_r_str(val);
            },
            Value::Bool(val) => {
                datum.set_field_type(DT::R_BOOL);
                datum.set_r_bool(val);
            },
            Value::I64(val) => {
                datum.set_field_type(DT::R_NUM);
                datum.set_r_num(val as f64);
            },
            Value::U64(val) => {
                datum.set_field_type(DT::R_NUM);
                datum.set_r_num(val as f64);
            },
            Value::F64(val) => {
                datum.set_field_type(DT::R_NUM);
                datum.set_r_num(val);
            },
            Value::Array(_val) => {
                unimplemented!();
            },
            Value::Object(_val) => {
                unimplemented!();
            },
            Value::Null => {
                datum.set_field_type(DT::R_NULL);
            },
        }
        // Term
        let mut term = Term::new();
        term.set_field_type(TT::DATUM);
        term.set_datum(datum);
        term
    }
}

impl ToTerm for Term {
    fn to_term(&self) -> Term {
        self.clone()
    }
}

macro_rules! command {
    ($T:expr, $cmd:expr, $args:expr, $opts:expr) => {{
        let mut term = Term::new();
        term.set_field_type($T);
        let mut args = Vec::new();
        let cmd = $cmd.into();
        if !cmd.is_empty() {
            args.push(cmd);
        }
        if let Some(list) = $args {
            args.extend(list);
        }
        /*
           if let Some(_opt) = $opts {
           unimplemented!();
           }
           */
        if !args.is_empty() {
            let args = RepeatedField::from_vec(args);
            term.set_args(args);
        }
        From::from(term)
    }}
}

macro_rules! closure_par {
    () => {{
        // ID
        let mut id = Datum::new();
        id.set_field_type(DT::R_NUM);
        id.set_r_num(1.0);
        // DATUM
        let mut datum = Term::new();
        datum.set_field_type(TT::DATUM);
        datum.set_datum(id);
        // VAR
        let mut var = Term::new();
        var.set_field_type(TT::VAR);
        let args = RepeatedField::from_vec(vec![datum]);
        var.set_args(args);
        From::from(var)
    }}
}

macro_rules! closure_arg {
    ($res:expr) => {{
        // ID
        let mut id = Datum::new();
        id.set_field_type(DT::R_NUM);
        id.set_r_num(1.0);
        // ARRAY
        let mut array = Datum::new();
        array.set_field_type(DT::R_ARRAY);
        let args = RepeatedField::from_vec(vec![id]);
        array.set_r_array(args);
        // DATUM
        let mut datum = Term::new();
        datum.set_field_type(TT::DATUM);
        datum.set_datum(array);
        // FUNC
        let mut func = Term::new();
        func.set_field_type(TT::FUNC);
        let args = RepeatedField::from_vec(vec![datum, $res.into()]);
        func.set_args(args);
        func
    }}
}

pub trait Command
where Self: Sized + From<Term> + Into<Term>
{
    fn db<T>(self, arg: T) -> Self
        where T: ToTerm
    {
        let arg = arg.to_term();
        command!(TT::DB, self, Some(vec![arg]), None)
    }

    fn table<T>(self, arg: T) -> Self
        where T: ToTerm
    {
        let arg = arg.to_term();
        command!(TT::TABLE, self, Some(vec![arg]), None)
    }

    fn get_field<T>(self, arg: T) -> Self
        where T: ToTerm
    {
        let arg = arg.to_term();
        command!(TT::GET_FIELD, self, Some(vec![arg]), None)
    }

    fn map<F>(self, func: F) -> Self
        where F: Fn(Self) -> Self
        {
            let res = func(closure_par!());
            let arg = closure_arg!(res);
            command!(TT::MAP, self, Some(vec![arg]), None)
        }

    fn array(self, arg: Vec<&ToTerm>) -> Self {
        let args: Vec<Term> = arg.iter()
            .map(|a| a.to_term())
            .collect();
        command!(TT::MAKE_ARRAY, self, Some(args), None)
    }
}

#[test]
fn test_commands_can_be_chained() {
    impl Command for Term { }
    let r = Term::new();
    let term = r.db("heroes").table("marvel").map(|row| row.get_field("first_appearance"));
    let term: Term = term.into();
    panic!(format!("{:?}\n\n{}", term, term.encode()));
}
<commit_msg>Add more commands<commit_after>//! RethinkDB protocol implementation in Rust

extern crate protobuf;
extern crate serde_json;

pub mod proto;

use serde_json::value::{Value, ToJson};
use protobuf::repeated::RepeatedField;
use protobuf::ProtobufEnum;
use proto::{
    Term, Datum,
    Datum_DatumType as DT,
    Term_TermType as TT,
};

pub trait IsDatum {
    fn is_datum(&self) -> bool;
}

pub trait IsEmpty {
    fn is_empty(&self) -> bool;
}

pub trait Encode {
    fn encode(&self) -> String;
}

pub trait ToTerm {
    fn to_term(&self) -> Term;
}

impl IsDatum for Term {
    fn is_datum(&self) -> bool {
        self.get_field_type() == TT::DATUM
    }
}

impl IsEmpty for Term {
    fn is_empty(&self) -> bool {
        *self == Term::new()
    }
}

impl Encode for Datum {
    fn encode(&self) -> String {
        match self.get_field_type() {
            DT::R_NULL => {
                unimplemented!();
            },
            DT::R_BOOL => {
                if self.has_r_bool() {
                    format!("{:?}", self.get_r_bool())
                } else {
                    unimplemented!();
                }
            },
            DT::R_NUM => {
                if self.has_r_num() {
                    format!("{}", self.get_r_num())
                } else {
                    unimplemented!();
                }
            },
            DT::R_STR => {
                if self.has_r_str() {
                    format!("{:?}", self.get_r_str())
                } else {
                    unimplemented!();
                }
            },
            DT::R_ARRAY => {
                let mut args = format!("[{},[", TT::MAKE_ARRAY.value());
                for term in self.get_r_array() {
                    args.push_str(&format!("{},", term.encode()));
                }
                args = args.trim_right_matches(",").to_string();
                args.push_str("]]");
                args
            },
            DT::R_OBJECT => {
                unimplemented!();
            },
            DT::R_JSON => {
                unimplemented!();
            },
        }
    }
}

impl Encode for Term {
    fn encode(&self) -> String {
        let mut res = String::new();
        if !self.is_datum() {
            res.push_str(&format!("[{},", self.get_field_type().value()));
        }
        let terms = self.get_args();
        if !terms.is_empty() {
            let mut args = String::from("[");
            for term in terms {
                args.push_str(&format!("{},", term.encode()));
            }
            args = args.trim_right_matches(",").to_string();
            args.push_str("]");
            res.push_str(&args);
        }
        if self.has_datum() {
            let datum = self.get_datum();
            res.push_str(&datum.encode());
        }
        if !self.is_datum() {
            res.push_str("]");
        }
        res
    }
}

impl<T: ToJson> ToTerm for T {
    fn to_term(&self) -> Term {
        // Datum
        let mut datum = Datum::new();
        match self.to_json() {
            Value::String(val) => {
                datum.set_field_type(DT::R_STR);
                datum.set_r_str(val);
            },
            Value::Bool(val) => {
                datum.set_field_type(DT::R_BOOL);
                datum.set_r_bool(val);
            },
            Value::I64(val) => {
                datum.set_field_type(DT::R_NUM);
                datum.set_r_num(val as f64);
            },
            Value::U64(val) => {
                datum.set_field_type(DT::R_NUM);
                datum.set_r_num(val as f64);
            },
            Value::F64(val) => {
                datum.set_field_type(DT::R_NUM);
                datum.set_r_num(val);
            },
            Value::Array(_val) => {
                unimplemented!();
            },
            Value::Object(_val) => {
                unimplemented!();
            },
            Value::Null => {
                datum.set_field_type(DT::R_NULL);
            },
        }
        // Term
        let mut term = Term::new();
        term.set_field_type(TT::DATUM);
        term.set_datum(datum);
        term
    }
}

impl ToTerm for Term {
    fn to_term(&self) -> Term {
        self.clone()
    }
}

macro_rules! command {
    ($T:expr, $cmd:expr, $args:expr, $opts:expr) => {{
        let mut term = Term::new();
        term.set_field_type($T);
        let mut args = Vec::new();
        let cmd = $cmd.into();
        if !cmd.is_empty() {
            args.push(cmd);
        }
        if let Some(list) = $args {
            args.extend(list);
        }
        /*
           if let Some(_opt) = $opts {
           unimplemented!();
           }
           */
        if !args.is_empty() {
            let args = RepeatedField::from_vec(args);
            term.set_args(args);
        }
        From::from(term)
    }}
}

macro_rules! closure_par {
    () => {{
        // ID
        let mut id = Datum::new();
        id.set_field_type(DT::R_NUM);
        id.set_r_num(1.0);
        // DATUM
        let mut datum = Term::new();
        datum.set_field_type(TT::DATUM);
        datum.set_datum(id);
        // VAR
        let mut var = Term::new();
        var.set_field_type(TT::VAR);
        let args = RepeatedField::from_vec(vec![datum]);
        var.set_args(args);
        From::from(var)
    }}
}

macro_rules! closure_arg {
    ($res:expr) => {{
        // ID
        let mut id = Datum::new();
        id.set_field_type(DT::R_NUM);
        id.set_r_num(1.0);
        // ARRAY
        let mut array = Datum::new();
        array.set_field_type(DT::R_ARRAY);
        let args = RepeatedField::from_vec(vec![id]);
        array.set_r_array(args);
        // DATUM
        let mut datum = Term::new();
        datum.set_field_type(TT::DATUM);
        datum.set_datum(array);
        // FUNC
        let mut func = Term::new();
        func.set_field_type(TT::FUNC);
        let args = RepeatedField::from_vec(vec![datum, $res.into()]);
        func.set_args(args);
        func
    }}
}

pub trait Command
where Self: Sized + From<Term> + Into<Term>
{
    fn expr<T>(self, arg: T) -> Self
        where T: ToTerm
    {
        From::from(arg.to_term())
    }

    fn db<T>(self, arg: T) -> Self
        where T: ToTerm
    {
        let arg = arg.to_term();
        command!(TT::DB, self, Some(vec![arg]), None)
    }

    fn db_create<T>(self, arg: T) -> Self
        where T: ToTerm
    {
        let arg = arg.to_term();
        command!(TT::DB_CREATE, self, Some(vec![arg]), None)
    }

    fn db_drop<T>(self, arg: T) -> Self
        where T: ToTerm
    {
        let arg = arg.to_term();
        command!(TT::DB_DROP, self, Some(vec![arg]), None)
    }

    fn table<T>(self, arg: T) -> Self
        where T: ToTerm
    {
        let arg = arg.to_term();
        command!(TT::TABLE, self, Some(vec![arg]), None)
    }

    fn table_create<T>(self, arg: T) -> Self
        where T: ToTerm
    {
        let arg = arg.to_term();
        command!(TT::TABLE_CREATE, self, Some(vec![arg]), None)
    }

    fn table_drop<T>(self, arg: T) -> Self
        where T: ToTerm
    {
        let arg = arg.to_term();
        command!(TT::TABLE_DROP, self, Some(vec![arg]), None)
    }

    fn get_field<T>(self, arg: T) -> Self
        where T: ToTerm
    {
        let arg = arg.to_term();
        command!(TT::GET_FIELD, self, Some(vec![arg]), None)
    }

    fn map<F>(self, func: F) -> Self
        where F: Fn(Self) -> Self
        {
            let res = func(closure_par!());
            let arg = closure_arg!(res);
            command!(TT::MAP, self, Some(vec![arg]), None)
        }

    fn object(self, arg: Vec<&ToTerm>) -> Self {
        let args: Vec<Term> = arg.iter()
            .map(|a| a.to_term())
            .collect();
        command!(TT::MAKE_OBJ, self, Some(args), None)
    }

    fn array(self, arg: Vec<&ToTerm>) -> Self {
        let args: Vec<Term> = arg.iter()
            .map(|a| a.to_term())
            .collect();
        command!(TT::MAKE_ARRAY, self, Some(args), None)
    }
}

#[test]
fn test_commands_can_be_chained() {
    impl Command for Term { }
    let r = Term::new();
    let term = r.db("heroes").table("marvel").map(|row| row.get_field("first_appearance"));
    let term: Term = term.into();
    panic!(format!("{:?}\n\n{}", term, term.encode()));
}
<|endoftext|>


<commit_before>//! Performs topological sorting.

#![warn(bad_style, missing_docs,
        unused, unused_extern_crates, unused_import_braces,
        unused_qualifications, unused_results, unused_typecasts)]

#![feature(std_misc)]

use std::collections::{HashMap, HashSet};
use std::collections::hash_map::Entry;
use std::hash::Hash;

struct Dependency<T> {
    num_prec: usize,
    succ: HashSet<T>
}

impl<T: Hash + Eq> Dependency<T> {
    fn new() -> Dependency<T> { Dependency { num_prec: 0, succ: HashSet::new() } }
}

/// Performs topological sorting.
pub struct TopologicalSort<T> {
    top: HashMap<T, Dependency<T>>
}

impl<T: Hash + Eq + Clone> TopologicalSort<T> {
    /// Creates new empty `TopologicalSort`.
    ///
    /// ```rust
    ///# extern crate "topological-sort" as tsort;
    ///# fn main() {
    /// use tsort::TopologicalSort;
    /// let mut ts = TopologicalSort::new();
    /// ts.add_dependency("hello_world.o", "hello_world");
    /// ts.add_dependency("hello_world.c", "hello_world");
    /// ts.add_dependency("stdio.h", "hello_world.o");
    /// ts.add_dependency("glibc.so", "hello_world");
    /// assert_eq!(vec!["glibc.so", "hello_world.c", "stdio.h"],
    ///            { let mut v = ts.pop_all(); v.sort(); v });
    /// assert_eq!(vec!["hello_world.o"],
    ///            { let mut v = ts.pop_all(); v.sort(); v });
    /// assert_eq!(vec!["hello_world"],
    ///            { let mut v = ts.pop_all(); v.sort(); v });
    /// assert!(ts.pop_all().is_empty());
    ///# }
    /// ```
    #[inline]
    pub fn new() -> TopologicalSort<T> { TopologicalSort { top: HashMap::new() } }

    /// Returns the number of elements in the `TopologicalSort`.
    #[inline]
    pub fn len(&self) -> usize { self.top.len() }

    /// Returns true if the `TopologicalSort` contains no elements.
    #[inline]
    pub fn is_empty(&self) -> bool { self.top.is_empty() }

    /// Registers the two elements' dependency.
    ///
    /// # Arguments
    ///
    /// * `prec` - The element appears before `succ`. `prec` is depended on by `succ`.
    /// * `succ` - The element appears after `prec`. `succ` depends on `prec`.
    pub fn add_dependency(&mut self, prec: T, succ: T) {
        match self.top.entry(prec) {
            Entry::Vacant(e) => {
                let mut dep = Dependency::new();
                dep.succ.insert(succ.clone());
                let _ = e.insert(dep);
            },
            Entry::Occupied(e) => {
                if !e.into_mut().succ.insert(succ.clone()) {
                    // Already registered
                    return
                }
            }
        }

        match self.top.entry(succ) {
            Entry::Vacant(e) => {
                let mut dep = Dependency::new();
                dep.num_prec += 1;
                let _ = e.insert(dep);
            }
            Entry::Occupied(e) => {
                e.into_mut().num_prec += 1;
            }
        }
    }

    /// Removes the item that is not depended on by any other items and returns it, or `None` if there is no such item.
    ///
    /// If `pop` returns `None` and `len` is not 0, there is cyclic dependencies.
    pub fn pop(&mut self) -> Option<T> {
        self.top
            .iter()
            .filter(|&(_, v)| v.num_prec == 0)
            .next()
            .map(|(k, _)| k.clone())
            .map(|key| {
                let _ = self.remove(&key);
                key
            })
    }

    /// Removes all items that are not depended on by any other items and returns it, or empty vector if there are no such items.
    ///
    /// If `pop_all` returns an empty vector and `len` is not 0, there is cyclic dependencies.
    pub fn pop_all(&mut self) -> Vec<T> {
        let keys = self.top
            .iter()
            .filter(|&(_, v)| v.num_prec == 0)
            .map(|(k, _)| k.clone())
            .collect::<Vec<_>>();
        for k in keys.iter() {
            let _ = self.remove(k);
        }
        keys
    }

    fn remove(&mut self, prec: &T) -> Option<Dependency<T>> {
        let result = self.top.remove(prec);
        if let Some(ref p) = result {
            for s in p.succ.iter() {
                if let Some(y) = self.top.get_mut(s) {
                    y.num_prec -= 1;
                }
            }
        }
        result
    }
}

impl<T: Hash + Eq + Clone> Iterator for TopologicalSort<T> {
    type Item = T;

    fn next(&mut self) -> Option<T> { self.pop() }
}

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

    #[test]
    fn iter() {
        let mut ts = TopologicalSort::<i32>::new();
        ts.add_dependency(1, 2);
        ts.add_dependency(2, 3);
        ts.add_dependency(3, 4);
        assert_eq!(Some(1), ts.next());
        assert_eq!(Some(2), ts.next());
        assert_eq!(Some(3), ts.next());
        assert_eq!(Some(4), ts.next());
        assert_eq!(None, ts.next());
    }

    #[test]
    fn pop_all() {
        fn check(result: &[i32], ts: &mut TopologicalSort<i32>) {
            let l = ts.len();
            let mut v = ts.pop_all();
            v.sort();
            assert_eq!(result, v);
            assert_eq!(l - result.len(), ts.len());
        }

        let mut ts = TopologicalSort::new();
        ts.add_dependency(7, 11);
        assert_eq!(2, ts.len());
        ts.add_dependency(7, 8);
        assert_eq!(3, ts.len());
        ts.add_dependency(5, 11);
        assert_eq!(4, ts.len());
        ts.add_dependency(3, 8);
        assert_eq!(5, ts.len());
        ts.add_dependency(3, 10);
        assert_eq!(6, ts.len());
        ts.add_dependency(11, 2);
        assert_eq!(7, ts.len());
        ts.add_dependency(11, 9);
        assert_eq!(8, ts.len());
        ts.add_dependency(11, 10);
        assert_eq!(8, ts.len());
        ts.add_dependency(8, 9);
        assert_eq!(8, ts.len());

        check(&[3, 5, 7], &mut ts);
        check(&[8, 11], &mut ts);
        check(&[2, 9, 10], &mut ts);
        check(&[], &mut ts);
    }
}

<commit_msg>update to rustc 1.0.0-nightly (199bdcfef 2015-03-26) (built 2015-03-27)<commit_after>//! Performs topological sorting.

#![warn(bad_style, missing_docs,
        unused, unused_extern_crates, unused_import_braces,
        unused_qualifications, unused_results)]

use std::collections::{HashMap, HashSet};
use std::collections::hash_map::Entry;
use std::hash::Hash;

struct Dependency<T> {
    num_prec: usize,
    succ: HashSet<T>
}

impl<T: Hash + Eq> Dependency<T> {
    fn new() -> Dependency<T> { Dependency { num_prec: 0, succ: HashSet::new() } }
}

/// Performs topological sorting.
pub struct TopologicalSort<T> {
    top: HashMap<T, Dependency<T>>
}

impl<T: Hash + Eq + Clone> TopologicalSort<T> {
    /// Creates new empty `TopologicalSort`.
    ///
    /// ```rust
    ///# extern crate "topological-sort" as tsort;
    ///# fn main() {
    /// use tsort::TopologicalSort;
    /// let mut ts = TopologicalSort::new();
    /// ts.add_dependency("hello_world.o", "hello_world");
    /// ts.add_dependency("hello_world.c", "hello_world");
    /// ts.add_dependency("stdio.h", "hello_world.o");
    /// ts.add_dependency("glibc.so", "hello_world");
    /// assert_eq!(vec!["glibc.so", "hello_world.c", "stdio.h"],
    ///            { let mut v = ts.pop_all(); v.sort(); v });
    /// assert_eq!(vec!["hello_world.o"],
    ///            { let mut v = ts.pop_all(); v.sort(); v });
    /// assert_eq!(vec!["hello_world"],
    ///            { let mut v = ts.pop_all(); v.sort(); v });
    /// assert!(ts.pop_all().is_empty());
    ///# }
    /// ```
    #[inline]
    pub fn new() -> TopologicalSort<T> { TopologicalSort { top: HashMap::new() } }

    /// Returns the number of elements in the `TopologicalSort`.
    #[inline]
    pub fn len(&self) -> usize { self.top.len() }

    /// Returns true if the `TopologicalSort` contains no elements.
    #[inline]
    pub fn is_empty(&self) -> bool { self.top.is_empty() }

    /// Registers the two elements' dependency.
    ///
    /// # Arguments
    ///
    /// * `prec` - The element appears before `succ`. `prec` is depended on by `succ`.
    /// * `succ` - The element appears after `prec`. `succ` depends on `prec`.
    pub fn add_dependency(&mut self, prec: T, succ: T) {
        match self.top.entry(prec) {
            Entry::Vacant(e) => {
                let mut dep = Dependency::new();
                dep.succ.insert(succ.clone());
                let _ = e.insert(dep);
            },
            Entry::Occupied(e) => {
                if !e.into_mut().succ.insert(succ.clone()) {
                    // Already registered
                    return
                }
            }
        }

        match self.top.entry(succ) {
            Entry::Vacant(e) => {
                let mut dep = Dependency::new();
                dep.num_prec += 1;
                let _ = e.insert(dep);
            }
            Entry::Occupied(e) => {
                e.into_mut().num_prec += 1;
            }
        }
    }

    /// Removes the item that is not depended on by any other items and returns it, or `None` if there is no such item.
    ///
    /// If `pop` returns `None` and `len` is not 0, there is cyclic dependencies.
    pub fn pop(&mut self) -> Option<T> {
        self.top
            .iter()
            .filter(|&(_, v)| v.num_prec == 0)
            .next()
            .map(|(k, _)| k.clone())
            .map(|key| {
                let _ = self.remove(&key);
                key
            })
    }

    /// Removes all items that are not depended on by any other items and returns it, or empty vector if there are no such items.
    ///
    /// If `pop_all` returns an empty vector and `len` is not 0, there is cyclic dependencies.
    pub fn pop_all(&mut self) -> Vec<T> {
        let keys = self.top
            .iter()
            .filter(|&(_, v)| v.num_prec == 0)
            .map(|(k, _)| k.clone())
            .collect::<Vec<_>>();
        for k in keys.iter() {
            let _ = self.remove(k);
        }
        keys
    }

    fn remove(&mut self, prec: &T) -> Option<Dependency<T>> {
        let result = self.top.remove(prec);
        if let Some(ref p) = result {
            for s in p.succ.iter() {
                if let Some(y) = self.top.get_mut(s) {
                    y.num_prec -= 1;
                }
            }
        }
        result
    }
}

impl<T: Hash + Eq + Clone> Iterator for TopologicalSort<T> {
    type Item = T;

    fn next(&mut self) -> Option<T> { self.pop() }
}

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

    #[test]
    fn iter() {
        let mut ts = TopologicalSort::<i32>::new();
        ts.add_dependency(1, 2);
        ts.add_dependency(2, 3);
        ts.add_dependency(3, 4);
        assert_eq!(Some(1), ts.next());
        assert_eq!(Some(2), ts.next());
        assert_eq!(Some(3), ts.next());
        assert_eq!(Some(4), ts.next());
        assert_eq!(None, ts.next());
    }

    #[test]
    fn pop_all() {
        fn check(result: &[i32], ts: &mut TopologicalSort<i32>) {
            let l = ts.len();
            let mut v = ts.pop_all();
            v.sort();
            assert_eq!(result, v);
            assert_eq!(l - result.len(), ts.len());
        }

        let mut ts = TopologicalSort::new();
        ts.add_dependency(7, 11);
        assert_eq!(2, ts.len());
        ts.add_dependency(7, 8);
        assert_eq!(3, ts.len());
        ts.add_dependency(5, 11);
        assert_eq!(4, ts.len());
        ts.add_dependency(3, 8);
        assert_eq!(5, ts.len());
        ts.add_dependency(3, 10);
        assert_eq!(6, ts.len());
        ts.add_dependency(11, 2);
        assert_eq!(7, ts.len());
        ts.add_dependency(11, 9);
        assert_eq!(8, ts.len());
        ts.add_dependency(11, 10);
        assert_eq!(8, ts.len());
        ts.add_dependency(8, 9);
        assert_eq!(8, ts.len());

        check(&[3, 5, 7], &mut ts);
        check(&[8, 11], &mut ts);
        check(&[2, 9, 10], &mut ts);
        check(&[], &mut ts);
    }
}

<|endoftext|>