<commit_before>extern crate rand;
use HaltCondition::{ Epochs, MSE };
use UpdateRule::{ Stochastic, Batch };
use std::iter::{Zip, Enumerate};
use std::slice;
use std::num::Float;

#[test]
fn it_works() {
}

type Node = Vec<f64>;
type Layer = Vec<Node>;

#[derive(Debug)]
#[derive(Clone)]
pub enum HaltCondition {
    Epochs(usize),
    MSE(f64),
}

#[derive(Debug)]
#[derive(Clone)]
pub enum UpdateRule {
    Stochastic,
    Batch
}

static DEFAULT_LEARNING_RATE: f64 = 0.3f64;
static DEFAULT_MOMENTUM: f64 = 0f64;
static DEFAULT_EPOCHS: usize = 1000;

#[derive(Debug)]
struct Trainer<'a> {
    rate: f64,
    momentum: f64,
    log_interval: Option<usize>,
    halt_condition: HaltCondition,
    update_rule: UpdateRule,
    threads: usize,
    nn: &'a mut NN,
}

impl<'a> Trainer<'a>  {

    fn rate(&mut self, rate: f64) -> &mut Trainer<'a> {
        self.rate = rate;
        self
    }

    fn momentum(&mut self, momentum: f64) -> &mut Trainer<'a> {
        self.momentum = momentum;
        self
    }

    fn log_interval(&mut self, log_interval: Option<usize>) -> &mut Trainer<'a> {
        self.log_interval = log_interval;
        self
    }

    fn halt_condition(&mut self, halt_condition: HaltCondition) -> &mut Trainer<'a> {
        self.halt_condition = halt_condition;
        self
    }

    fn update_rule(&mut self, update_rule: UpdateRule) -> &mut Trainer<'a> {
        self.update_rule = update_rule;
        self
    }

    fn threads(&mut self, threads: usize) -> &mut Trainer<'a> {
        self.threads = threads;
        self
    }

    fn go(&mut self, examples: &[(&Vec<f64>, &Vec<f64>)]) -> Result<usize, &str> {
        self.nn.train_option(
            examples,
            self.rate,
            self.momentum,
            self.log_interval,
            self.halt_condition.clone(),
            self.update_rule.clone(),
            self.threads
        )
    }

}

#[derive(Debug)]
#[derive(Clone)]
pub struct NN {
    layers: Vec<Layer>,
    num_inputs: usize,
}

impl NN {
    fn train(&mut self) -> Trainer {
        Trainer {
            rate: DEFAULT_LEARNING_RATE,
            momentum: DEFAULT_MOMENTUM,
            log_interval: None,
            halt_condition: Epochs(DEFAULT_EPOCHS),
            update_rule: Stochastic,
            threads: 1,
            nn: self
        }
        // unimplemented!()
    }

    // returns a results with ok being the error rate of the network found at the end and
    // Err is a string with an explaination
    fn train_option(&mut self, examples: &[(&Vec<f64>, &Vec<f64>)], rate: f64, momentum: f64, log_interval: Option<usize>,
                    halt_condition: HaltCondition, update_rule: UpdateRule, threads: usize) -> Result<usize, &str> {

        let mut epochs = 0;
        let mut error_rate = 0.0;

        loop {

            let mut prev_deltas = make_weights_tracker(&self.layers, 0.0f64);
            let mut batch_data = make_weights_tracker(&self.layers, 0.0f64);
            let mut error_sum: f64 = 0.0;

            for &(inputs, targets) in examples.iter() {
                let results = do_run(self, inputs);
                let weight_updates = calculate_weight_update_info(self, &results, &targets);
                error_sum += calculate_error(&results, targets);
                match update_rule {
                    Batch => update_batch_data(&mut batch_data, &weight_updates),
                    Stochastic => update_weights(self, &weight_updates, &mut prev_deltas, rate, momentum),
                }
            }

            // if we're in batch mode, update the weights now
            match update_rule {
                Batch => update_weights(self, &batch_data, &mut prev_deltas, rate, momentum),
                Stochastic => (),
            }

            epochs += 1;

            // log error rate if neccessary
            match log_interval {
                Some(interval) if epochs % interval == 0 => {
                    println!("error rate: {}", error_rate);
                },
                _ => (),
            }

            // check if we've met the halt condition yet
            match halt_condition {
                Epochs(epochs_halt) => {
                    if epochs == epochs_halt { break; }
                },
                MSE(target_error) => {
                    if target_error == error_rate { break; }
                }
            }
        }

        unimplemented!();
    }

    fn run(&self, inputs: &Vec<f64>) -> Vec<f64> {
        do_run(self, inputs).pop().unwrap()
    }
}

fn do_run(nn: &NN, inputs: &Vec<f64>) -> Vec<Vec<f64>> {
    let mut results = Vec::new();
    results.push(inputs.clone());
    let mut prev_layer_results = inputs;
    for layer in nn.layers.iter() {
        let mut layer_results = Vec::new();
        for node in layer.iter() {
            layer_results.push( sigmoid(modified_dotprod(&node, &prev_layer_results)) )
        }
        results.push(layer_results);
    }
    results
}

fn modified_dotprod(node: &Node, values: &Vec<f64>) -> f64 {
    let mut total = node[0]; // for the threshold weight
    for (weight, value) in node.iter().skip(1).zip(values.iter()) {
        total += weight * value;
    }
    total
}

fn sigmoid(y: f64) -> f64 {
    1.0 / (1.0 + Float::exp(-y))
}

fn update_batch_data(batch_data: &mut Vec<Vec<Vec<f64>>> , weight_updates: &Vec<Vec<Vec<f64>>>) {
    unimplemented!()
}

fn update_weights(nn: &mut NN, network_weight_updates: &Vec<Vec<Vec<f64>>>, prev_deltas: &mut Vec<Vec<Vec<f64>>>, rate: f64, momentum: f64) {
    for layer_index in 0..nn.layers.len() {
        let mut layer = &mut nn.layers[layer_index];
        let layer_weight_updates = &network_weight_updates[layer_index];
        for node_index in 0..layer.len() {
            let mut node = &mut layer[node_index];
            let node_weight_updates = &layer_weight_updates[node_index];
            for weight_index in 0..node.len() {
                let weight = node[weight_index];
                let weight_update = node_weight_updates[weight_index];
                let prev_delta = prev_deltas[layer_index][node_index][weight_index];
                let delta = (rate * weight_update) + (momentum * prev_delta);
                node[weight_index] += delta;
                prev_deltas[layer_index][node_index][weight_index] = delta;
            }
        }
    }
}

fn calculate_weight_update_info(nn: &NN, results: &Vec<Vec<f64>>, targets: &[f64]) -> Vec<Vec<Vec<f64>>> {
    let mut network_errors = Vec::new();
    let mut network_weight_updates = Vec::new();

    let mut next_layer_errors: Vec<f64> = Vec::new(); // TODO: find a way to not do this

    let layers = &nn.layers;
    let network_results = &results[1..]; // skip the input layer

    for (layer_index, (layer_nodes, layer_results)) in iterZipEnum(layers, network_results) {
        let prev_layer_results = &results[layer_index];
        let mut layer_errors = Vec::new();
        let mut layer_weight_updates = Vec::new();

        let next_layer_nodes: &Layer = &&layers[layer_index+1];

        for (node_index, (node, &result)) in iterZipEnum(layer_nodes, layer_results) {
            let mut node_weight_updates = Vec::new();
            let mut node_error = 0f64;

            // calculate error for this node
            if layer_index == layers.len() - 1 {
                node_error = result * (1f64 - result) * (targets[node_index] - result);
            } else {
                let mut sum = 0f64;
                for (next_node, &next_node_error_data) in next_layer_nodes.iter().zip(next_layer_errors.iter()) {
                    sum += next_node[node_index+1] * next_node_error_data; // +1 because the 0th weight is the threshold
                }
                node_error = result * (1f64 - result) * sum;
            }

            // calculate weight updates for this node
            for weight_index in 0..node.len() {
                let mut prev_layer_result = 1f64;
                if weight_index == 0 {
                    prev_layer_result = 1.0; // theshold
                } else {
                    prev_layer_result = prev_layer_results[weight_index-1];
                }
                let weight_update = node_error * prev_layer_result;
                node_weight_updates.push(weight_update);
            }

            layer_errors.push(node_error);
            layer_weight_updates.push(node_weight_updates);
        }

        next_layer_errors = layer_errors.clone();

        network_errors.push(layer_errors);
        network_weight_updates.push(layer_weight_updates);
    }

    // updates were build by backpropagation so reverse them
    network_weight_updates.reverse();

    network_weight_updates
}

fn iterZipEnum<'s, 't, S: 's, T: 't>(s: &'s [S], t: &'t [T]) ->
    Enumerate<Zip<slice::Iter<'s, S>, slice::Iter<'t, T>>>  {
    s.iter().zip(t.iter()).enumerate()
}

fn calculate_error(results: &Vec<Vec<f64>>, targets: &[f64]) -> f64 {
    unimplemented!()
}

fn make_weights_tracker<T: Clone>(layers: &Vec<Layer>, place_holder: T) -> Vec<Vec<Vec<T>>> {
    let mut network_level = Vec::new();
    for layer in layers.iter() {
        let mut layer_level = Vec::new();
        for node in layer.iter() {
            let mut node_level = Vec::new();
            for weight in node.iter() {
                node_level.push(place_holder.clone());
            }
            layer_level.push(node_level);
        }
        network_level.push(layer_level);
    }

    network_level
}

fn new_nn(layers_sizes: &[usize]) -> NN {
        if layers_sizes.len() < 2 {
            panic!("must have at least two layers");
        }
        let mut layers = Vec::new();
        let mut it = layers_sizes.iter();
        // get the first layer size
        let first_layer_size = *it.next().unwrap();

        // setup the rest of the layers
        let mut prev_layer_size = first_layer_size;
        for &layer_size in it {
            let mut layer: Layer = Vec::new();
            for _ in 0..layer_size {
                let mut node: Node = vec![rand::random(); prev_layer_size];
                node.shrink_to_fit();
                layer.push(node)
            }
            layer.shrink_to_fit();
            layers.push(layer);
            prev_layer_size = layer_size;
        }
        layers.shrink_to_fit();
        NN { layers: layers, num_inputs: first_layer_size }
    }<commit_msg>wrote batch weight combiner<commit_after>extern crate rand;
use HaltCondition::{ Epochs, MSE };
use UpdateRule::{ Stochastic, Batch };
use std::iter::{Zip, Enumerate};
use std::slice;
use std::num::Float;

static DEFAULT_LEARNING_RATE: f64 = 0.3f64;
static DEFAULT_MOMENTUM: f64 = 0f64;
static DEFAULT_EPOCHS: usize = 1000;

#[test]
fn it_works() {
}

type Node = Vec<f64>;
type Layer = Vec<Node>;

#[derive(Debug)]
#[derive(Clone)]
pub enum HaltCondition {
    Epochs(usize),
    MSE(f64),
}

#[derive(Debug)]
#[derive(Clone)]
pub enum UpdateRule {
    Stochastic,
    Batch
}

#[derive(Debug)]
struct Trainer<'a> {
    rate: f64,
    momentum: f64,
    log_interval: Option<usize>,
    halt_condition: HaltCondition,
    update_rule: UpdateRule,
    threads: usize,
    nn: &'a mut NN,
}

impl<'a> Trainer<'a>  {

    fn rate(&mut self, rate: f64) -> &mut Trainer<'a> {
        self.rate = rate;
        self
    }

    fn momentum(&mut self, momentum: f64) -> &mut Trainer<'a> {
        self.momentum = momentum;
        self
    }

    fn log_interval(&mut self, log_interval: Option<usize>) -> &mut Trainer<'a> {
        self.log_interval = log_interval;
        self
    }

    fn halt_condition(&mut self, halt_condition: HaltCondition) -> &mut Trainer<'a> {
        self.halt_condition = halt_condition;
        self
    }

    fn update_rule(&mut self, update_rule: UpdateRule) -> &mut Trainer<'a> {
        self.update_rule = update_rule;
        self
    }

    fn threads(&mut self, threads: usize) -> &mut Trainer<'a> {
        self.threads = threads;
        self
    }

    fn go(&mut self, examples: &[(&Vec<f64>, &Vec<f64>)]) -> Result<usize, &str> {
        self.nn.train_option(
            examples,
            self.rate,
            self.momentum,
            self.log_interval,
            self.halt_condition.clone(),
            self.update_rule.clone(),
            self.threads
        )
    }

}

#[derive(Debug)]
#[derive(Clone)]
pub struct NN {
    layers: Vec<Layer>,
    num_inputs: usize,
}

impl NN {
    fn train(&mut self) -> Trainer {
        Trainer {
            rate: DEFAULT_LEARNING_RATE,
            momentum: DEFAULT_MOMENTUM,
            log_interval: None,
            halt_condition: Epochs(DEFAULT_EPOCHS),
            update_rule: Stochastic,
            threads: 1,
            nn: self
        }
    }

    // returns a results with ok being the error rate of the network found at the end and
    // Err is a string with an explaination
    fn train_option(&mut self, examples: &[(&Vec<f64>, &Vec<f64>)], rate: f64, momentum: f64, log_interval: Option<usize>,
                    halt_condition: HaltCondition, update_rule: UpdateRule, threads: usize) -> Result<usize, &str> {

        let mut epochs = 0;
        let mut error_rate = 0.0;

        loop {

            let mut prev_deltas = make_weights_tracker(&self.layers, 0.0f64);
            let mut batch_data = make_weights_tracker(&self.layers, 0.0f64);
            let mut error_sum: f64 = 0.0;

            for &(inputs, targets) in examples.iter() {
                let results = do_run(self, inputs);
                let weight_updates = calculate_weight_updates(self, &results, &targets);
                error_sum += calculate_error(&results, targets);
                match update_rule {
                    Batch => update_batch_data(&mut batch_data, &weight_updates),
                    Stochastic => update_weights(self, &weight_updates, &mut prev_deltas, rate, momentum),
                }
            }

            // if we're in batch mode, update the weights now
            match update_rule {
                Batch => update_weights(self, &batch_data, &mut prev_deltas, rate, momentum),
                Stochastic => (),
            }

            epochs += 1;

            // log error rate if neccessary
            match log_interval {
                Some(interval) if epochs % interval == 0 => {
                    println!("error rate: {}", error_rate);
                },
                _ => (),
            }

            // check if we've met the halt condition yet
            match halt_condition {
                Epochs(epochs_halt) => {
                    if epochs == epochs_halt { break; }
                },
                MSE(target_error) => {
                    if target_error == error_rate { break; }
                }
            }
        }

        unimplemented!();
    }

    fn run(&self, inputs: &Vec<f64>) -> Vec<f64> {
        do_run(self, inputs).pop().unwrap()
    }
}

fn do_run(nn: &NN, inputs: &Vec<f64>) -> Vec<Vec<f64>> {
    let mut results = Vec::new();
    results.push(inputs.clone());
    let mut prev_layer_results = inputs;
    for layer in nn.layers.iter() {
        let mut layer_results = Vec::new();
        for node in layer.iter() {
            layer_results.push( sigmoid(modified_dotprod(&node, &prev_layer_results)) )
        }
        results.push(layer_results);
    }
    results
}

fn modified_dotprod(node: &Node, values: &Vec<f64>) -> f64 {
    let mut total = node[0]; // for the threshold weight
    for (weight, value) in node.iter().skip(1).zip(values.iter()) {
        total += weight * value;
    }
    total
}

fn sigmoid(y: f64) -> f64 {
    1f64 / (1f64 + Float::exp(-y))
}

// adds new network weight updates into the update updates already collected
fn update_batch_data(batch_data: &mut Vec<Vec<Vec<f64>>> , network_weight_updates: &Vec<Vec<Vec<f64>>>) {
    for layer_index in 0..batch_data.len() {
        let mut batch_layer = &mut batch_data[layer_index];
        let layer_weight_updates = &network_weight_updates[layer_index];
        for node_index in 0..batch_layer.len() {
            let mut batch_node = &mut batch_layer[node_index];
            let node_weight_updates = &layer_weight_updates[node_index];
            for weight_index in 0..batch_node.len() {
                batch_node[weight_index] += node_weight_updates[weight_index];
            }
        }
    }
}

// updates all weights in the network
fn update_weights(nn: &mut NN, network_weight_updates: &Vec<Vec<Vec<f64>>>, prev_deltas: &mut Vec<Vec<Vec<f64>>>, rate: f64, momentum: f64) {
    for layer_index in 0..nn.layers.len() {
        let mut layer = &mut nn.layers[layer_index];
        let layer_weight_updates = &network_weight_updates[layer_index];
        for node_index in 0..layer.len() {
            let mut node = &mut layer[node_index];
            let node_weight_updates = &layer_weight_updates[node_index];
            for weight_index in 0..node.len() {
                let weight = node[weight_index];
                let weight_update = node_weight_updates[weight_index];
                let prev_delta = prev_deltas[layer_index][node_index][weight_index];
                let delta = (rate * weight_update) + (momentum * prev_delta);
                node[weight_index] += delta;
                prev_deltas[layer_index][node_index][weight_index] = delta;
            }
        }
    }
}

// calculates all weight updates by backpropagation
fn calculate_weight_updates(nn: &NN, results: &Vec<Vec<f64>>, targets: &[f64]) -> Vec<Vec<Vec<f64>>> {
    let mut network_errors = Vec::new();
    let mut network_weight_updates = Vec::new();

    let mut next_layer_errors: Vec<f64> = Vec::new(); // TODO: find a way to not do this

    let layers = &nn.layers;
    let network_results = &results[1..]; // skip the input layer

    for (layer_index, (layer_nodes, layer_results)) in iter_zip_enum(layers, network_results) {
        let prev_layer_results = &results[layer_index];
        let mut layer_errors = Vec::new();
        let mut layer_weight_updates = Vec::new();

        let next_layer_nodes: &Layer = &&layers[layer_index+1];

        for (node_index, (node, &result)) in iter_zip_enum(layer_nodes, layer_results) {
            let mut node_weight_updates = Vec::new();
            let mut node_error = 0f64;

            // calculate error for this node
            if layer_index == layers.len() - 1 {
                node_error = result * (1f64 - result) * (targets[node_index] - result);
            } else {
                let mut sum = 0f64;
                for (next_node, &next_node_error_data) in next_layer_nodes.iter().zip(next_layer_errors.iter()) {
                    sum += next_node[node_index+1] * next_node_error_data; // +1 because the 0th weight is the threshold
                }
                node_error = result * (1f64 - result) * sum;
            }

            // calculate weight updates for this node
            for weight_index in 0..node.len() {
                let mut prev_layer_result = 1f64;
                if weight_index == 0 {
                    prev_layer_result = 1f64; // theshold
                } else {
                    prev_layer_result = prev_layer_results[weight_index-1];
                }
                let weight_update = node_error * prev_layer_result;
                node_weight_updates.push(weight_update);
            }

            layer_errors.push(node_error);
            layer_weight_updates.push(node_weight_updates);
        }

        next_layer_errors = layer_errors.clone();

        network_errors.push(layer_errors);
        network_weight_updates.push(layer_weight_updates);
    }

    // updates were build by backpropagation so reverse them
    network_weight_updates.reverse();

    network_weight_updates
}

fn iter_zip_enum<'s, 't, S: 's, T: 't>(s: &'s [S], t: &'t [T]) ->
    Enumerate<Zip<slice::Iter<'s, S>, slice::Iter<'t, T>>>  {
    s.iter().zip(t.iter()).enumerate()
}

fn calculate_error(results: &Vec<Vec<f64>>, targets: &[f64]) -> f64 {
    unimplemented!()
}

fn make_weights_tracker<T: Clone>(layers: &Vec<Layer>, place_holder: T) -> Vec<Vec<Vec<T>>> {
    let mut network_level = Vec::new();
    for layer in layers.iter() {
        let mut layer_level = Vec::new();
        for node in layer.iter() {
            let mut node_level = Vec::new();
            for _ in node.iter() {
                node_level.push(place_holder.clone());
            }
            layer_level.push(node_level);
        }
        network_level.push(layer_level);
    }

    network_level
}

fn new(layers_sizes: &[usize]) -> NN {
        if layers_sizes.len() < 2 {
            panic!("must have at least two layers");
        }
        let mut layers = Vec::new();
        let mut it = layers_sizes.iter();
        // get the first layer size
        let first_layer_size = *it.next().unwrap();

        // setup the rest of the layers
        let mut prev_layer_size = first_layer_size;
        for &layer_size in it {
            let mut layer: Layer = Vec::new();
            for _ in 0..layer_size {
                let mut node: Node = vec![rand::random(); prev_layer_size];
                node.shrink_to_fit();
                layer.push(node)
            }
            layer.shrink_to_fit();
            layers.push(layer);
            prev_layer_size = layer_size;
        }
        layers.shrink_to_fit();
        NN { layers: layers, num_inputs: first_layer_size }
    }<|endoftext|>


<commit_before>use std::collections::HashMap;

/// Checks whether its argument is a valid UUID, per
/// https://webbluetoothcg.github.io/web-bluetooth/#dfn-valid-uuid
pub fn valid_uuid(uuid: &str) -> bool {
    if uuid.len() != 36 {
        return false;
    }
    for (index, char) in uuid.chars().enumerate() {
        match index {
            8 | 13 | 18 | 23 => {
                if char != '-' {
                    return false;
                }
            }
            _ => {
                match char {
                    '0' ... '9' | 'a' ... 'f' => (),
                    _ => return false,
                }
            }
        }
    }
    return true;
}

/// Checks whether its argument is a blacklist file that's usable in the algorithm at
/// https://webbluetoothcg.github.io/web-bluetooth/#dfn-parsing-the-blacklist
pub fn validate_blacklist(blacklist: &str) -> Option<String> {
    let mut result = HashMap::<&str, &str>::new();
    for (index, line) in blacklist.lines().enumerate() {
        let line_num = index + 1;
        if line.is_empty() || line.starts_with("#") {
            // Comment or blank line.
            continue;
        }
        if line.starts_with(" ") {
            return Some(format!("line {}: Starts with extraneous space", line_num));
        }
        if line.ends_with(" ") {
            return Some(format!("line {}: Ends with extraneous space", line_num));
        }
        let tokens: Vec<&str> = line.split(' ').collect();
        match tokens.len() {
            0 => unreachable!("split(' ') never returns an empty array"),
            1 | 2 => {
                let uuid = tokens[0];
                if !valid_uuid(uuid) {
                    return Some(format!("line {}: '{}' is not a valid UUID", line_num, uuid));
                }
                let mut exclusion = "exclude";
                if tokens.len() == 2 {
                    exclusion = tokens[1];
                    match exclusion {
                        "exclude-reads" | "exclude-writes" => (),
                        _ => return Some(format!(
			    "line {}: '{}' should be 'exclude-reads' or 'exclude-writes'",
			    line_num, exclusion)),
                    }
                }
                if let Some(_) = result.insert(uuid, exclusion) {
                    return Some(format!(
                        "line {}: '{}' appears multiple times",
                        line_num, uuid));
                }
            },
            _ => return Some(format!("line {}: Too many tokens", line_num)),
        }
    }
    return None;
}

#[cfg(test)]
mod tests {
    use super::*;
    use std::fs::File;
    use std::io::prelude::*;

    #[test]
    fn test_valid_uuid() {
	assert!( valid_uuid("00000000-0000-0000-0000-000000000000"));
	assert!( valid_uuid("01234567-89ab-cdef-0123-456789abcdef"));
	assert!(!valid_uuid("01234567-89AB-CDEF-0123-456789ABCDEF"));
	assert!(!valid_uuid("g1234567-89ab-cdef-0123-456789abcdef"));
	assert!(!valid_uuid("01234567-89ab-cdef-0123-456789abcdef0"));
	assert!(!valid_uuid("0123456789abcdef0123456789abcdef"));
	assert!(!valid_uuid("01234567089ab0cdef001230456789abcdef"));
    }

    #[test]
    fn test_validate_blacklist() {
        assert_eq!(None, validate_blacklist(""));
        // Lines are terminated by \n, not \r\n.
        assert_eq!(Some("line 1: '\r' is not a valid UUID".to_string()),
                   validate_blacklist("\r\n"));
        assert_eq!(None, validate_blacklist("# comment"));
        assert_eq!(None, validate_blacklist("# comment\n\
                                             00001812-0000-1000-8000-00805f9b34fb"));

        // No extraneous spaces:
        assert_eq!(Some("line 1: Starts with extraneous space".to_string()),
		   validate_blacklist("  # comment"));
        assert_eq!(Some("line 1: Starts with extraneous space".to_string()),
		   validate_blacklist(" 00001812-0000-1000-8000-00805f9b34fb"));
        assert_eq!(Some("line 1: Ends with extraneous space".to_string()),
		   validate_blacklist("00001812-0000-1000-8000-00805f9b34fb "));

        assert_eq!(
	    Some("line 1: Too many tokens".to_string()),
	    validate_blacklist("00001812-0000-1000-8000-00805f9b34fb # not a comment"));
        assert_eq!(
            Some("line 1: 'X0001812-0000-1000-8000-00805f9b34fb' is not a valid UUID".to_string()),
            validate_blacklist("X0001812-0000-1000-8000-00805f9b34fb"));
        assert_eq!(None,
                   validate_blacklist("00001812-0000-1000-8000-00805f9b34fb exclude-reads"));
        assert_eq!(None,
                   validate_blacklist("00001812-0000-1000-8000-00805f9b34fb exclude-writes"));
        assert_eq!(
            Some("line 1: '00001812-0000-1000-8000-00805f9b34fb\u{A0}exclude-reads' is not a valid UUID".to_string()),
            validate_blacklist("00001812-0000-1000-8000-00805f9b34fb\u{A0}exclude-reads"));
        assert_eq!(
            Some("line 1: 'X0001812-0000-1000-8000-00805f9b34fb' is not a valid UUID".to_string()),
            validate_blacklist("X0001812-0000-1000-8000-00805f9b34fb exclude-reads"));
        assert_eq!(
            Some("line 1: 'exclude' should be 'exclude-reads' or 'exclude-writes'".to_string()),
            validate_blacklist("00001812-0000-1000-8000-00805f9b34fb exclude"));
        assert_eq!(Some("line 1: Too many tokens".to_string()),
                   validate_blacklist("00001812-0000-1000-8000-00805f9b34fb token token"));

        // Check all variants of repeated UUIDs.
        assert_eq!(
            Some("line 3: '00001812-0000-1000-8000-00805f9b34fb' appears multiple times".to_string()),
            validate_blacklist("00001812-0000-1000-8000-00805f9b34fb\n\
                                00001810-0000-1000-8000-00805f9b34fb\n\
                                00001812-0000-1000-8000-00805f9b34fb\n"));
        assert_eq!(
            Some("line 3: '00001812-0000-1000-8000-00805f9b34fb' appears multiple times".to_string()),
            validate_blacklist("00001812-0000-1000-8000-00805f9b34fb\n\
                                00001810-0000-1000-8000-00805f9b34fb\n\
                                00001812-0000-1000-8000-00805f9b34fb exclude-reads\n"));
        assert_eq!(
            Some("line 3: '00001812-0000-1000-8000-00805f9b34fb' appears multiple times".to_string()),
            validate_blacklist("00001812-0000-1000-8000-00805f9b34fb exclude-reads\n\
                                00001810-0000-1000-8000-00805f9b34fb\n\
                                00001812-0000-1000-8000-00805f9b34fb\n"));
        assert_eq!(
            Some("line 3: '00001812-0000-1000-8000-00805f9b34fb' appears multiple times".to_string()),
            validate_blacklist("00001812-0000-1000-8000-00805f9b34fb exclude-reads\n\
                                00001810-0000-1000-8000-00805f9b34fb\n\
                                00001812-0000-1000-8000-00805f9b34fb exclude-reads\n"));
    }

    #[test]
    fn validate_gatt_blacklist() {
	let filename = "gatt_blacklist.txt";
        let content = File::open(filename).and_then(|mut file| {
            let mut result = String::new();
            try!(file.read_to_string(&mut result));
            Ok(result)
        }).unwrap_or_else(|e| { panic!("Error reading {}: {}", filename, e) });

        if let Some(error) = validate_blacklist(&content) {
	    panic!("{} is invalid: {}", filename, error);
	}
    }
}
<commit_msg>Fix the test after Rust changed .lines() to accept '\r\n'.<commit_after>use std::collections::HashMap;

/// Checks whether its argument is a valid UUID, per
/// https://webbluetoothcg.github.io/web-bluetooth/#dfn-valid-uuid
pub fn valid_uuid(uuid: &str) -> bool {
    if uuid.len() != 36 {
        return false;
    }
    for (index, char) in uuid.chars().enumerate() {
        match index {
            8 | 13 | 18 | 23 => {
                if char != '-' {
                    return false;
                }
            }
            _ => {
                match char {
                    '0' ... '9' | 'a' ... 'f' => (),
                    _ => return false,
                }
            }
        }
    }
    return true;
}

/// Checks whether its argument is a blacklist file that's usable in the algorithm at
/// https://webbluetoothcg.github.io/web-bluetooth/#dfn-parsing-the-blacklist
pub fn validate_blacklist(blacklist: &str) -> Option<String> {
    let mut result = HashMap::<&str, &str>::new();
    for (index, line) in blacklist.split('\n').enumerate() {
        let line_num = index + 1;
        if line.is_empty() || line.starts_with("#") {
            // Comment or blank line.
            continue;
        }
        if line.starts_with(" ") {
            return Some(format!("line {}: Starts with extraneous space", line_num));
        }
        if line.ends_with(" ") {
            return Some(format!("line {}: Ends with extraneous space", line_num));
        }
        let tokens: Vec<&str> = line.split(' ').collect();
        match tokens.len() {
            0 => unreachable!("split(' ') never returns an empty array"),
            1 | 2 => {
                let uuid = tokens[0];
                if !valid_uuid(uuid) {
                    return Some(format!("line {}: '{}' is not a valid UUID", line_num, uuid));
                }
                let mut exclusion = "exclude";
                if tokens.len() == 2 {
                    exclusion = tokens[1];
                    match exclusion {
                        "exclude-reads" | "exclude-writes" => (),
                        _ => return Some(format!(
			    "line {}: '{}' should be 'exclude-reads' or 'exclude-writes'",
			    line_num, exclusion)),
                    }
                }
                if let Some(_) = result.insert(uuid, exclusion) {
                    return Some(format!(
                        "line {}: '{}' appears multiple times",
                        line_num, uuid));
                }
            },
            _ => return Some(format!("line {}: Too many tokens", line_num)),
        }
    }
    return None;
}

#[cfg(test)]
mod tests {
    use super::*;
    use std::fs::File;
    use std::io::prelude::*;

    #[test]
    fn test_valid_uuid() {
	assert!( valid_uuid("00000000-0000-0000-0000-000000000000"));
	assert!( valid_uuid("01234567-89ab-cdef-0123-456789abcdef"));
	assert!(!valid_uuid("01234567-89AB-CDEF-0123-456789ABCDEF"));
	assert!(!valid_uuid("g1234567-89ab-cdef-0123-456789abcdef"));
	assert!(!valid_uuid("01234567-89ab-cdef-0123-456789abcdef0"));
	assert!(!valid_uuid("0123456789abcdef0123456789abcdef"));
	assert!(!valid_uuid("01234567089ab0cdef001230456789abcdef"));
    }

    #[test]
    fn test_validate_blacklist() {
        assert_eq!(None, validate_blacklist(""));
        // Lines are terminated by \n, not \r\n.
        assert_eq!(Some("line 1: '\r' is not a valid UUID".to_string()),
                   validate_blacklist("\r\n"));
        assert_eq!(None, validate_blacklist("# comment"));
        assert_eq!(None, validate_blacklist("# comment\n\
                                             00001812-0000-1000-8000-00805f9b34fb"));

        // No extraneous spaces:
        assert_eq!(Some("line 1: Starts with extraneous space".to_string()),
		   validate_blacklist("  # comment"));
        assert_eq!(Some("line 1: Starts with extraneous space".to_string()),
		   validate_blacklist(" 00001812-0000-1000-8000-00805f9b34fb"));
        assert_eq!(Some("line 1: Ends with extraneous space".to_string()),
		   validate_blacklist("00001812-0000-1000-8000-00805f9b34fb "));

        assert_eq!(
	    Some("line 1: Too many tokens".to_string()),
	    validate_blacklist("00001812-0000-1000-8000-00805f9b34fb # not a comment"));
        assert_eq!(
            Some("line 1: 'X0001812-0000-1000-8000-00805f9b34fb' is not a valid UUID".to_string()),
            validate_blacklist("X0001812-0000-1000-8000-00805f9b34fb"));
        assert_eq!(None,
                   validate_blacklist("00001812-0000-1000-8000-00805f9b34fb exclude-reads"));
        assert_eq!(None,
                   validate_blacklist("00001812-0000-1000-8000-00805f9b34fb exclude-writes"));
        assert_eq!(
            Some("line 1: '00001812-0000-1000-8000-00805f9b34fb\u{A0}exclude-reads' is not a valid UUID".to_string()),
            validate_blacklist("00001812-0000-1000-8000-00805f9b34fb\u{A0}exclude-reads"));
        assert_eq!(
            Some("line 1: 'X0001812-0000-1000-8000-00805f9b34fb' is not a valid UUID".to_string()),
            validate_blacklist("X0001812-0000-1000-8000-00805f9b34fb exclude-reads"));
        assert_eq!(
            Some("line 1: 'exclude' should be 'exclude-reads' or 'exclude-writes'".to_string()),
            validate_blacklist("00001812-0000-1000-8000-00805f9b34fb exclude"));
        assert_eq!(Some("line 1: Too many tokens".to_string()),
                   validate_blacklist("00001812-0000-1000-8000-00805f9b34fb token token"));

        // Check all variants of repeated UUIDs.
        assert_eq!(
            Some("line 3: '00001812-0000-1000-8000-00805f9b34fb' appears multiple times".to_string()),
            validate_blacklist("00001812-0000-1000-8000-00805f9b34fb\n\
                                00001810-0000-1000-8000-00805f9b34fb\n\
                                00001812-0000-1000-8000-00805f9b34fb\n"));
        assert_eq!(
            Some("line 3: '00001812-0000-1000-8000-00805f9b34fb' appears multiple times".to_string()),
            validate_blacklist("00001812-0000-1000-8000-00805f9b34fb\n\
                                00001810-0000-1000-8000-00805f9b34fb\n\
                                00001812-0000-1000-8000-00805f9b34fb exclude-reads\n"));
        assert_eq!(
            Some("line 3: '00001812-0000-1000-8000-00805f9b34fb' appears multiple times".to_string()),
            validate_blacklist("00001812-0000-1000-8000-00805f9b34fb exclude-reads\n\
                                00001810-0000-1000-8000-00805f9b34fb\n\
                                00001812-0000-1000-8000-00805f9b34fb\n"));
        assert_eq!(
            Some("line 3: '00001812-0000-1000-8000-00805f9b34fb' appears multiple times".to_string()),
            validate_blacklist("00001812-0000-1000-8000-00805f9b34fb exclude-reads\n\
                                00001810-0000-1000-8000-00805f9b34fb\n\
                                00001812-0000-1000-8000-00805f9b34fb exclude-reads\n"));
    }

    #[test]
    fn validate_gatt_blacklist() {
	let filename = "gatt_blacklist.txt";
        let content = File::open(filename).and_then(|mut file| {
            let mut result = String::new();
            try!(file.read_to_string(&mut result));
            Ok(result)
        }).unwrap_or_else(|e| { panic!("Error reading {}: {}", filename, e) });

        if let Some(error) = validate_blacklist(&content) {
	    panic!("{} is invalid: {}", filename, error);
	}
    }
}
<|endoftext|>


<commit_before>#![feature(box_syntax)]
#![cfg_attr(feature="clippy", feature(plugin))]
#![cfg_attr(feature="clippy", plugin(clippy))]

extern crate notify;
extern crate parking_lot;

use std::collections::HashMap;
use std::path::Path;
use std::sync::Arc;
use std::sync::mpsc::{channel, Receiver};
use parking_lot::Mutex;
use notify::{RecommendedWatcher, Watcher};

#[derive(Debug)]
pub struct Event {
    pub path: String,
    op_flags: u32
}

impl Event {
    fn check(&self, op: notify::op::Op) -> bool {
        let flag = op.bits();
        self.op_flags & flag == flag
    }

    pub fn chmoded(&self) -> bool {
        self.check(notify::op::CHMOD)
    }

    pub fn created(&self) -> bool {
        self.check(notify::op::CREATE)
    }

    pub fn changed(&self) -> bool {
        self.check(notify::op::WRITE)
    }

    pub fn removed(&self) -> bool {
        self.check(notify::op::REMOVE)
    }

    pub fn renamed(&self) -> bool {
        self.check(notify::op::RENAME)
    }

    pub fn ignored(&self) -> bool {
        self.check(notify::op::IGNORED)
    }
}

fn event_from_notify(e: notify::Event) -> Option<Event> {
    if let notify::Event { path: Some(path), op: Ok(op) } = e {
        if cfg!(debug_assertions) {
            println!("{:?} {:?}", op, path);
        }
        path.to_str()
            .map(|s| s.to_string())
            .map(|s| Event {
                path: s,
                op_flags: op.bits()
            })
    } else {
        None
    }
}

#[derive(Debug)]
pub enum Error {
    Io(std::io::Error),
    Notify(notify::Error)
}

type HotwatchResult<T> = Result<T, Error>;

type Handler = Box<Fn(Event) + Send>;
type HandlerMapMutex = Arc<Mutex<HashMap<String, Handler>>>;

pub struct Hotwatch {
    watcher: RecommendedWatcher,
    handler_map_mutex: HandlerMapMutex,
}

impl Hotwatch {
    /// Creates a new hotwatch instance.
    ///
    /// # Errors
    ///
    /// This function can fail if the underlying [notify](https://github.com/passcod/rsnotify)
    /// instance fails to initialize. This will unfortunately expose you to notify's own error
    /// type; hotwatch doesn't perfectly encapsulate this.
    ///
    /// # Examples
    ///
    /// ```
    /// use hotwatch::Hotwatch;
    ///
    /// let hotwatch = Hotwatch::new().expect("Hotwatch failed to initialize.");
    /// ```
    pub fn new() -> HotwatchResult<Self> {
        let (tx, rx) = channel();
        let handler_map_mutex = Arc::new(Mutex::new(HashMap::new()));
        Hotwatch::run(handler_map_mutex.clone(), rx);
        Watcher::new(tx)
            .map_err(Error::Notify)
            .map(|watcher| Hotwatch {
                watcher: watcher,
                handler_map_mutex: handler_map_mutex,
            })
    }

    /// Watch a path and register a handler to it.
    ///
    /// Note that handlers will be run in hotwatch's watch thread, so you'll have to use `move`
    /// if the closure captures anything.
    ///
    /// # Examples
    ///
    /// ```
    /// use hotwatch::Hotwatch;
    ///
    /// let mut hotwatch = Hotwatch::new().expect("Hotwatch failed to initialize.");
    /// hotwatch.watch("README.md", |e: hotwatch::Event| {
    ///  if e.changed() {
    ///      println!("{} changed!", e.path);
    ///    }
    /// }).expect("Failed to watch file!");
    /// ```
    pub fn watch<F>(&mut self, path: &str, handler: F) -> HotwatchResult<()>
    where F: 'static + Fn(Event) + Send {
        let mut handlers = self.handler_map_mutex.lock();
        self.watcher.watch(Path::new(path))
            .map_err(|e| match e {
                notify::Error::Io(e) => Error::Io(e),
                _ => Error::Notify(e)
            })
            .map(|_| {
                (*handlers).insert(path.to_string(), box handler);
            })
    }

    fn run(handler_map_mutex: HandlerMapMutex, rx: Receiver<notify::Event>) {
        std::thread::spawn(move || {
            loop {
                match rx.recv() {
                    Ok(notify_event) => {
                        if let Some(event) = event_from_notify(notify_event) {
                            let handlers = handler_map_mutex.lock();
                            if let Some(handler) = (*handlers).get(&event.path) {
                                handler(event);
                            }
                        }
                    },
                    Err(e) => {
                        if cfg!(debug_assertions) {
                            println!("Receiver error: {:?}", e);
                        }
                    }
                }
            }
        });
    }
}
<commit_msg>Fixed indentation in doc example.<commit_after>#![feature(box_syntax)]
#![cfg_attr(feature="clippy", feature(plugin))]
#![cfg_attr(feature="clippy", plugin(clippy))]

extern crate notify;
extern crate parking_lot;

use std::collections::HashMap;
use std::path::Path;
use std::sync::Arc;
use std::sync::mpsc::{channel, Receiver};
use parking_lot::Mutex;
use notify::{RecommendedWatcher, Watcher};

#[derive(Debug)]
pub struct Event {
    pub path: String,
    op_flags: u32
}

impl Event {
    fn check(&self, op: notify::op::Op) -> bool {
        let flag = op.bits();
        self.op_flags & flag == flag
    }

    pub fn chmoded(&self) -> bool {
        self.check(notify::op::CHMOD)
    }

    pub fn created(&self) -> bool {
        self.check(notify::op::CREATE)
    }

    pub fn changed(&self) -> bool {
        self.check(notify::op::WRITE)
    }

    pub fn removed(&self) -> bool {
        self.check(notify::op::REMOVE)
    }

    pub fn renamed(&self) -> bool {
        self.check(notify::op::RENAME)
    }

    pub fn ignored(&self) -> bool {
        self.check(notify::op::IGNORED)
    }
}

fn event_from_notify(e: notify::Event) -> Option<Event> {
    if let notify::Event { path: Some(path), op: Ok(op) } = e {
        if cfg!(debug_assertions) {
            println!("{:?} {:?}", op, path);
        }
        path.to_str()
            .map(|s| s.to_string())
            .map(|s| Event {
                path: s,
                op_flags: op.bits()
            })
    } else {
        None
    }
}

#[derive(Debug)]
pub enum Error {
    Io(std::io::Error),
    Notify(notify::Error)
}

type HotwatchResult<T> = Result<T, Error>;

type Handler = Box<Fn(Event) + Send>;
type HandlerMapMutex = Arc<Mutex<HashMap<String, Handler>>>;

pub struct Hotwatch {
    watcher: RecommendedWatcher,
    handler_map_mutex: HandlerMapMutex,
}

impl Hotwatch {
    /// Creates a new hotwatch instance.
    ///
    /// # Errors
    ///
    /// This function can fail if the underlying [notify](https://github.com/passcod/rsnotify)
    /// instance fails to initialize. This will unfortunately expose you to notify's own error
    /// type; hotwatch doesn't perfectly encapsulate this.
    ///
    /// # Examples
    ///
    /// ```
    /// use hotwatch::Hotwatch;
    ///
    /// let hotwatch = Hotwatch::new().expect("Hotwatch failed to initialize.");
    /// ```
    pub fn new() -> HotwatchResult<Self> {
        let (tx, rx) = channel();
        let handler_map_mutex = Arc::new(Mutex::new(HashMap::new()));
        Hotwatch::run(handler_map_mutex.clone(), rx);
        Watcher::new(tx)
            .map_err(Error::Notify)
            .map(|watcher| Hotwatch {
                watcher: watcher,
                handler_map_mutex: handler_map_mutex,
            })
    }

    /// Watch a path and register a handler to it.
    ///
    /// Note that handlers will be run in hotwatch's watch thread, so you'll have to use `move`
    /// if the closure captures anything.
    ///
    /// # Examples
    ///
    /// ```
    /// use hotwatch::Hotwatch;
    ///
    /// let mut hotwatch = Hotwatch::new().expect("Hotwatch failed to initialize.");
    /// hotwatch.watch("README.md", |e: hotwatch::Event| {
    ///   if e.changed() {
    ///     println!("{} changed!", e.path);
    ///   }
    /// }).expect("Failed to watch file!");
    /// ```
    pub fn watch<F>(&mut self, path: &str, handler: F) -> HotwatchResult<()>
    where F: 'static + Fn(Event) + Send {
        let mut handlers = self.handler_map_mutex.lock();
        self.watcher.watch(Path::new(path))
            .map_err(|e| match e {
                notify::Error::Io(e) => Error::Io(e),
                _ => Error::Notify(e)
            })
            .map(|_| {
                (*handlers).insert(path.to_string(), box handler);
            })
    }

    fn run(handler_map_mutex: HandlerMapMutex, rx: Receiver<notify::Event>) {
        std::thread::spawn(move || {
            loop {
                match rx.recv() {
                    Ok(notify_event) => {
                        if let Some(event) = event_from_notify(notify_event) {
                            let handlers = handler_map_mutex.lock();
                            if let Some(handler) = (*handlers).get(&event.path) {
                                handler(event);
                            }
                        }
                    },
                    Err(e) => {
                        if cfg!(debug_assertions) {
                            println!("Receiver error: {:?}", e);
                        }
                    }
                }
            }
        });
    }
}
<|endoftext|>


<commit_before>//! Defines "provider" traits and implementations for different types of I/O operations.
//!
//! The purpose of this is mainly for dependency injection: by having your code depend on a
//! generic provider, it can be tested by giving it a virtual, inspectable implementation of that
//! provider. In production, the "real" implementation can be used.
//!
//! Each type of provider exists in its own submodule and can be used independently. However,
//! this module also contains the all-encompassing `IoProvider` trait which provides access to
//! all of them. If you have a lot of I/O dependencies, it might be easier to create and pass
//! around one `&IoProvider` rather than several different providers.
//!
//! TODO: example

pub mod env;
pub mod stream;

/// Provides access to an environment provider and a stream provider.
pub trait IoProvider {
    /// Gets the `env::Provider`.
    fn env<'a>(&'a self) -> &'a env::Provider;

    /// Gets the `stream::Provider`.
    fn stream<'a>(&'a self) -> &'a stream::Provider;
}

/// "Real" implementer of `IoProvider`, using standard streams and the local environment.
pub struct LocalIoProvider {
    env_provider: env::Local,
    stream_provider: stream::Std,
}

impl LocalIoProvider {
    /// Creates a new `LocalIoProvider`.
    pub fn new() -> LocalIoProvider {
        LocalIoProvider {
            env_provider: env::Local,
            stream_provider: stream::Std::new(),
        }
    }
}

impl IoProvider for LocalIoProvider {
    fn env<'a>(&'a self) -> &'a env::Provider {
        &self.env_provider
    }

    fn stream<'a>(&'a self) -> &'a stream::Provider {
        &self.stream_provider
    }
}

/// Virtual implementer of `IoProvider`, using in-memory data which can be inspected.
pub struct VirtualIoProvider {
    env_provider: env::Virtual,
    stream_provider: stream::Virtual,
}

impl VirtualIoProvider {
    /// Creates a new `VirtualIoProvider`.
    pub fn new() -> VirtualIoProvider {
        VirtualIoProvider {
            env_provider: env::Virtual::new(),
            stream_provider: stream::Virtual::new(),
        }
    }
}

impl IoProvider for VirtualIoProvider {
    fn env<'a>(&'a self) -> &'a env::Provider {
        &self.env_provider
    }

    fn stream<'a>(&'a self) -> &'a stream::Provider {
        &self.stream_provider
    }
}
<commit_msg>Fill in crate root documentation<commit_after>//! Defines "provider" traits and implementations for different types of I/O operations.
//!
//! The purpose of this is mainly for dependency injection: by having your code depend on a
//! generic provider, it can be tested by giving it a virtual, inspectable implementation of that
//! provider. In production, the "real" implementation can be used.
//!
//! Each type of provider exists in its own submodule and can be used independently. However,
//! this module also contains the all-encompassing `IoProvider` trait which provides access to
//! all of them. If you have a lot of I/O dependencies, it might be easier to create and pass
//! around one `&IoProvider` rather than several different providers.
//!
//! # Examples
//!
//! ```
//! extern crate io_providers;
//!
//! use std::io::Write;
//! use std::path::Path;
//! use io_providers::{IoProvider, LocalIoProvider, VirtualIoProvider};
//! use io_providers::env::Provider as EnvProvider;
//! use io_providers::stream::Provider as StreamProvider;
//!
//! /// Gets the current working directory and prints it to stdout.
//! fn do_work(io: &mut IoProvider) {
//!     let cur_dir = io.env().current_dir().unwrap();
//!     let stdout = io.stream().output();
//!     writeln!(stdout, "The current directory is: {}", cur_dir.to_str().unwrap()).unwrap();
//! }
//!
//! fn main() {
//!     test_do_work_prints_current_dir();
//!
//!     // Use a local I/O provider here to get real interaction with the system
//!     let mut io = LocalIoProvider::new();
//!     do_work(&mut io);
//! }
//!
//! fn test_do_work_prints_current_dir() {
//!     // Use a virtual I/O provider here so we can verify how it was used
//!     let mut virtual_io = VirtualIoProvider::new();
//!     virtual_io.env().set_current_dir(Path::new("/foo/bar")).unwrap();
//!
//!     do_work(&mut virtual_io);
//!
//!     assert_eq!(
//!         "The current directory is: /foo/bar\n",
//!         ::std::str::from_utf8(virtual_io.virtual_stream().read_output()).unwrap());
//! }
//! ```

pub mod env;
pub mod stream;

/// Provides access to an environment provider and a stream provider.
pub trait IoProvider {
    /// Gets the `env::Provider`.
    fn env<'a>(&'a mut self) -> &'a mut env::Provider;

    /// Gets the `stream::Provider`.
    fn stream<'a>(&'a mut self) -> &'a mut stream::Provider;
}

/// "Real" implementer of `IoProvider`, using standard streams and the local environment.
pub struct LocalIoProvider {
    env_provider: env::Local,
    stream_provider: stream::Std,
}

impl LocalIoProvider {
    /// Creates a new `LocalIoProvider`.
    pub fn new() -> LocalIoProvider {
        LocalIoProvider {
            env_provider: env::Local,
            stream_provider: stream::Std::new(),
        }
    }
}

impl IoProvider for LocalIoProvider {
    fn env<'a>(&'a mut self) -> &'a mut env::Provider {
        &mut self.env_provider
    }

    fn stream<'a>(&'a mut self) -> &'a mut stream::Provider {
        &mut self.stream_provider
    }
}

/// Virtual implementer of `IoProvider`, using in-memory data which can be inspected.
pub struct VirtualIoProvider {
    env_provider: env::Virtual,
    stream_provider: stream::Virtual,
}

impl VirtualIoProvider {
    /// Creates a new `VirtualIoProvider`.
    pub fn new() -> VirtualIoProvider {
        VirtualIoProvider {
            env_provider: env::Virtual::new(),
            stream_provider: stream::Virtual::new(),
        }
    }

    /// Gets the `env::Virtual` provider.
    pub fn virtual_env<'a>(&'a mut self) -> &'a mut env::Virtual {
        &mut self.env_provider
    }

    /// Gets the `stream::Virtual` provider.
    pub fn virtual_stream<'a>(&'a mut self) -> &'a mut stream::Virtual {
        &mut self.stream_provider
    }
}

impl IoProvider for VirtualIoProvider {
    fn env<'a>(&'a mut self) -> &'a mut env::Provider {
        &mut self.env_provider
    }

    fn stream<'a>(&'a mut self) -> &'a mut stream::Provider {
        &mut self.stream_provider
    }
}
<|endoftext|>


<commit_before>extern crate xml;

use std::fs::File;
use std::io::BufReader;
use std::str::FromStr;

use xml::reader::{EventReader, XmlEvent};
use xml::attribute::OwnedAttribute;

mod error;
use error::{ErrorKind, AttributeError};

pub type Coordinate = f64;
pub type Id = i64;
pub type Role = String;

#[derive(Debug, PartialEq)]
pub struct Tag {
    pub key: String,
    pub val: String
}

#[derive(Debug, PartialEq)]
pub struct Bounds {
    pub minlat: Coordinate,
    pub minlon: Coordinate,
    pub maxlat: Coordinate,
    pub maxlon: Coordinate,
}

#[derive(Debug, PartialEq)]
pub struct Node {
    pub id: Id,
    pub lat: Coordinate,
    pub lon: Coordinate,
    pub tags: Vec<Tag>
}

#[derive(Debug, PartialEq)]
pub struct Way {
    pub id: Id,
    pub tags: Vec<Tag>,
    node_ids: Vec<Id>
}

#[derive(Debug, PartialEq)]
pub struct Relation {
    pub id: Id,
    pub members: Vec<Member>,
    pub tags: Vec<Tag>
}

#[derive(Debug, PartialEq)]
pub enum Member {
    Node(Id, Role),
    Way(Id, Role),
    Relation(Id, Role),
}

#[derive(Debug)]
pub struct OSM {
    pub bounds: Option<Bounds>,
    pub nodes: Vec<Node>,
    pub ways: Vec<Way>,
    pub relations: Vec<Relation>
}

impl OSM {
    fn empty() -> OSM {
        OSM {
            bounds: None,
            nodes: Vec::new(),
            ways: Vec::new(),
            relations: Vec::new(),
        }
    }

    pub fn parse(file: File) -> Option<OSM> {
        let mut osm = OSM::empty();

        let reader = BufReader::new(file);
        let mut parser = EventReader::new(reader);

        loop {
            match parse_element_data(&mut parser) {
                Err(ErrorKind::XmlParseError(_)) => return None,
                Err(ErrorKind::BoundsMissing(_)) => osm.bounds = None,
                Err(ErrorKind::MalformedTag(_))       |
                Err(ErrorKind::MalformedNode(_))      |
                Err(ErrorKind::MalformedWay(_))       |
                Err(ErrorKind::MalformedRelation(_))  |
                Err(ErrorKind::UnknownElement)        => continue,
                Ok(data) => {
                    match data {
                        ElementData::EndOfDocument => return Some(osm),
                        ElementData::Ignored => continue,
                        ElementData::Bounds(minlat, minlon, maxlat, maxlon) => {
                            osm.bounds = Some(Bounds {
                                minlat: minlat,
                                minlon: minlon,
                                maxlat: maxlat,
                                maxlon: maxlon
                            });
                        },
                        ElementData::Node(id, lat, lon, tags) => {
                            osm.nodes.push(Node {
                                id: id,
                                lat: lat,
                                lon: lon,
                                tags: tags
                            });
                        },
                        ElementData::Way(id, node_ids, tags) => {
                            osm.ways.push(Way {
                                id: id,
                                node_ids: node_ids,
                                tags: tags
                            });
                        },
                        ElementData::Relation(relation) => {
                            osm.relations.push(relation);
                        }
                    }
                }
            }
        }
    }

    pub fn nodes_for(&self, way: &Way) -> Vec<&Node> {
        way.node_ids.iter()
            .map(|id| self.nodes.iter().find(|node| node.id == *id).unwrap())
            .collect()
    }
}

enum ElementType {
    Bounds,
    Node,
    Way,
    Relation,
    Tag,
    NodeRef,
    Member,
}

enum ElementData {
    Bounds(Coordinate, Coordinate, Coordinate, Coordinate),
    Node(Id, Coordinate, Coordinate, Vec<Tag>),
    Way(Id, Vec<Id>, Vec<Tag>),
    Relation(Relation),
    // These two are here so we can terminate and skip uninteresting data without
    // using error handling.
    EndOfDocument,
    Ignored
}

impl FromStr for ElementType {
    type Err = ErrorKind;

    fn from_str(s: &str) -> Result<ElementType, ErrorKind> {
        let downcased = s.to_lowercase();

        if downcased == "bounds" {
            return Ok(ElementType::Bounds);
        }

        if downcased == "node" {
            return Ok(ElementType::Node);
        }

        if downcased == "way" {
            return Ok(ElementType::Way);
        }

        if downcased == "relation" {
            return Ok(ElementType::Relation);
        }

        if downcased == "tag" {
            return Ok(ElementType::Tag);
        }

        if downcased == "nd" {
            return Ok(ElementType::NodeRef);
        }

        if downcased == "member" {
            return Ok(ElementType::Member);
        }

        Err(ErrorKind::UnknownElement)
    }
}

fn parse_element_data(parser: &mut EventReader<BufReader<File>>) -> Result<ElementData, ErrorKind> {
    let element = try!(parser.next());
    match element {
        XmlEvent::EndDocument => Ok(ElementData::EndOfDocument),
        XmlEvent::StartElement { name, attributes, .. } => {
            let element_type = try!(ElementType::from_str(&name.local_name));

            match element_type {
                ElementType::Bounds => parse_bounds(&attributes),
                ElementType::Node => parse_node(parser, &attributes),
                ElementType::Way => parse_way(parser, &attributes),
                ElementType::Relation => parse_relation(parser, &attributes),
                _ => Err(ErrorKind::UnknownElement)
            }
        },
        _ => Ok(ElementData::Ignored)
    }
}

fn parse_relation(parser: &mut EventReader<BufReader<File>>, attrs: &Vec<OwnedAttribute>) -> Result<ElementData, ErrorKind> {
    let id = try!(find_attribute("id", attrs).map_err(ErrorKind::MalformedRelation));

    let mut members = Vec::new();
    let mut tags = Vec::new();

    loop {
        match try!(parser.next()) {
            XmlEvent::EndElement { name } => {
                let element_type = try!(ElementType::from_str(&name.local_name));

                match element_type {
                    ElementType::Relation => return Ok(ElementData::Relation(Relation {
                        id: id,
                        members: members,
                        tags: tags
                    })),
                    _ => continue
                }
            },
            XmlEvent::StartElement { name, attributes, .. } => {
                let element_type = try!(ElementType::from_str(&name.local_name));

                match element_type {
                    ElementType::Tag => {
                        if let Ok(tag) = parse_tag(&attributes) {
                            tags.push(tag);
                        }
                    },
                    ElementType::Member => {
                        let el_type = try!(find_attribute_uncasted("type", &attributes).map_err(ErrorKind::MalformedRelation));
                        let el_ref = try!(find_attribute("ref", &attributes).map_err(ErrorKind::MalformedRelation));
                        let el_role = try!(find_attribute_uncasted("role", &attributes).map_err(ErrorKind::MalformedRelation));

                        let el = match el_type.to_lowercase().as_ref() {
                            "node" => Member::Node(el_ref, el_role),
                            "way" => Member::Way(el_ref, el_role),
                            "relation" => Member::Relation(el_ref, el_role),
                            _ => return Err(ErrorKind::MalformedRelation(AttributeError::Missing))
                        };

                        members.push(el);
                    },
                    ElementType::Bounds   |
                    ElementType::Node     |
                    ElementType::Relation |
                    ElementType::Way      |
                    ElementType::NodeRef  => return Err(
                        ErrorKind::MalformedRelation(AttributeError::IllegalNesting)
                    )
                }
            },
            _ => continue
        }
    }
}

fn parse_way(parser: &mut EventReader<BufReader<File>>, attrs: &Vec<OwnedAttribute>) -> Result<ElementData, ErrorKind> {
    let id = try!(find_attribute("id", attrs).map_err(ErrorKind::MalformedWay));

    let mut node_refs = Vec::new();
    let mut tags = Vec::new();

    loop {
        match try!(parser.next()) {
            XmlEvent::EndElement { name } => {
                let element_type = try!(ElementType::from_str(&name.local_name));

                match element_type {
                    ElementType::Way => return Ok(ElementData::Way(id, node_refs, tags)),
                    _ => continue
                }
            },
            XmlEvent::StartElement { name, attributes, .. } => {
                let element_type = try!(ElementType::from_str(&name.local_name));

                match element_type {
                    ElementType::Tag => {
                        if let Ok(tag) = parse_tag(&attributes) {
                            tags.push(tag);
                        }
                    },
                    ElementType::NodeRef => {
                        let node_ref = try!(find_attribute("ref", &attributes).map_err(ErrorKind::MalformedWay));
                        node_refs.push(node_ref);
                    },
                    ElementType::Bounds   |
                    ElementType::Node     |
                    ElementType::Relation |
                    ElementType::Way      |
                    ElementType::Member   => return Err(
                        ErrorKind::MalformedWay(AttributeError::IllegalNesting)
                    )
                }
            },
            _ => continue
        }
    }

}

fn parse_node(parser: &mut EventReader<BufReader<File>>, attrs: &Vec<OwnedAttribute>) -> Result<ElementData, ErrorKind> {
    let id = try!(find_attribute("id", attrs).map_err(ErrorKind::MalformedNode));
    let lat = try!(find_attribute("lat", attrs).map_err(ErrorKind::MalformedNode));
    let lon = try!(find_attribute("lon", attrs).map_err(ErrorKind::MalformedNode));

    let mut tags = Vec::new();

    loop {
        match try!(parser.next()) {
            XmlEvent::EndElement { name } => {
                let element_type = try!(ElementType::from_str(&name.local_name));

                match element_type {
                    ElementType::Node => return Ok(ElementData::Node(id, lat, lon, tags)),
                    _ => continue
                }
            },
            XmlEvent::StartElement { name, attributes, .. } => {
                let element_type = try!(ElementType::from_str(&name.local_name));

                match element_type {
                    ElementType::Tag => {
                        if let Ok(tag) = parse_tag(&attributes) {
                            tags.push(tag);
                        }
                    },
                    ElementType::Bounds   |
                    ElementType::Node     |
                    ElementType::Relation |
                    ElementType::Way      |
                    ElementType::NodeRef  |
                    ElementType::Member   => return Err(
                        ErrorKind::MalformedNode(AttributeError::IllegalNesting)
                    )
                }
            },
            _ => continue
        }
    }
}

fn parse_tag(attributes: &Vec<OwnedAttribute>) -> Result<Tag, ErrorKind> {
    let key = try!(find_attribute_uncasted("k", attributes).map_err(ErrorKind::MalformedTag));
    let val = try!(find_attribute_uncasted("v", attributes).map_err(ErrorKind::MalformedTag));
    Ok(Tag { key: key, val: val })
}

fn parse_bounds(attrs: &Vec<OwnedAttribute>) -> Result<ElementData, ErrorKind> {
    let minlat = try!(find_attribute("minlat", attrs).map_err(ErrorKind::BoundsMissing));
    let minlon = try!(find_attribute("minlon", attrs).map_err(ErrorKind::BoundsMissing));
    let maxlat = try!(find_attribute("maxlat", attrs).map_err(ErrorKind::BoundsMissing));
    let maxlon = try!(find_attribute("maxlon", attrs).map_err(ErrorKind::BoundsMissing));

    Ok(ElementData::Bounds(minlat, minlon, maxlat, maxlon))
}

fn find_attribute<T>(name: &str, attrs: &Vec<OwnedAttribute>) -> Result<T, AttributeError>
    where AttributeError: std::convert::From<<T as std::str::FromStr>::Err>,
          T: FromStr
{
    let val_raw = try!(find_attribute_uncasted(name, attrs));
    let val = try!(val_raw.parse::<T>());
    Ok(val)
}

fn find_attribute_uncasted(name: &str, attrs: &Vec<OwnedAttribute>) -> Result<String, AttributeError> {
    let attr = attrs.iter().find(|attr| attr.name.local_name == name);
    match attr {
        Some(a) => {
            Ok(a.value.clone())
        },
        None => Err(AttributeError::Missing)
    }
}
<commit_msg>Introduce reference datatypes<commit_after>extern crate xml;

use std::fs::File;
use std::io::BufReader;
use std::str::FromStr;

use xml::reader::{EventReader, XmlEvent};
use xml::attribute::OwnedAttribute;

mod error;
use error::{ErrorKind, AttributeError};

pub type Coordinate = f64;
pub type Id = i64;
pub type Role = String;

#[derive(Debug, PartialEq)]
pub struct Tag {
    pub key: String,
    pub val: String
}

#[derive(Debug, PartialEq)]
pub struct Bounds {
    pub minlat: Coordinate,
    pub minlon: Coordinate,
    pub maxlat: Coordinate,
    pub maxlon: Coordinate,
}

#[derive(Debug, PartialEq)]
pub struct Node {
    pub id: Id,
    pub lat: Coordinate,
    pub lon: Coordinate,
    pub tags: Vec<Tag>
}

#[derive(Debug, PartialEq)]
pub struct Way {
    pub id: Id,
    pub tags: Vec<Tag>,
    node_ids: Vec<Id>
}

#[derive(Debug, PartialEq)]
pub struct Relation {
    pub id: Id,
    pub members: Vec<Member>,
    pub tags: Vec<Tag>
}

#[derive(Debug, PartialEq)]
pub enum Member {
    Node(Id, Role),
    Way(Id, Role),
    Relation(Id, Role),
}

#[derive(Debug, PartialEq)]
pub enum UnresolvedReference {
    Node(Id),
    Way(Id),
    Relation(Id)
}

#[derive(Debug, PartialEq)]
pub enum Reference<'a> {
    Node(&'a Node),
    Way(&'a Way),
    Relation(&'a Relation),
    Unresolved
}

#[derive(Debug)]
pub struct OSM {
    pub bounds: Option<Bounds>,
    pub nodes: Vec<Node>,
    pub ways: Vec<Way>,
    pub relations: Vec<Relation>
}

impl OSM {
    fn empty() -> OSM {
        OSM {
            bounds: None,
            nodes: Vec::new(),
            ways: Vec::new(),
            relations: Vec::new(),
        }
    }

    pub fn parse(file: File) -> Option<OSM> {
        let mut osm = OSM::empty();

        let reader = BufReader::new(file);
        let mut parser = EventReader::new(reader);

        loop {
            match parse_element_data(&mut parser) {
                Err(ErrorKind::XmlParseError(_)) => return None,
                Err(ErrorKind::BoundsMissing(_)) => osm.bounds = None,
                Err(ErrorKind::MalformedTag(_))       |
                Err(ErrorKind::MalformedNode(_))      |
                Err(ErrorKind::MalformedWay(_))       |
                Err(ErrorKind::MalformedRelation(_))  |
                Err(ErrorKind::UnknownElement)        => continue,
                Ok(data) => {
                    match data {
                        ElementData::EndOfDocument => return Some(osm),
                        ElementData::Ignored => continue,
                        ElementData::Bounds(minlat, minlon, maxlat, maxlon) => {
                            osm.bounds = Some(Bounds {
                                minlat: minlat,
                                minlon: minlon,
                                maxlat: maxlat,
                                maxlon: maxlon
                            });
                        },
                        ElementData::Node(id, lat, lon, tags) => {
                            osm.nodes.push(Node {
                                id: id,
                                lat: lat,
                                lon: lon,
                                tags: tags
                            });
                        },
                        ElementData::Way(id, node_ids, tags) => {
                            osm.ways.push(Way {
                                id: id,
                                node_ids: node_ids,
                                tags: tags
                            });
                        },
                        ElementData::Relation(relation) => {
                            osm.relations.push(relation);
                        }
                    }
                }
            }
        }
    }

    pub fn nodes_for(&self, way: &Way) -> Vec<&Node> {
        way.node_ids.iter()
            .map(|id| self.nodes.iter().find(|node| node.id == *id).unwrap())
            .collect()
    }

    pub fn resolve_reference<'a>(&self, reference: UnresolvedReference) -> Reference {
        match reference {
            UnresolvedReference::Node(id) => {
                match self.nodes.iter().find(|node| node.id == id) {
                    Some(node) => Reference::Node(&node),
                    None => Reference::Unresolved
                }
            },
            UnresolvedReference::Way(id) => {
                match self.ways.iter().find(|way| way.id == id) {
                    Some(way) => Reference::Way(&way),
                    None => Reference::Unresolved
                }
            },
            UnresolvedReference::Relation(id) => {
                match self.relations.iter().find(|relation| relation.id == id) {
                    Some(relation) => Reference::Relation(&relation),
                    None => Reference::Unresolved
                }
            }
        }
    }
}

enum ElementType {
    Bounds,
    Node,
    Way,
    Relation,
    Tag,
    NodeRef,
    Member,
}

enum ElementData {
    Bounds(Coordinate, Coordinate, Coordinate, Coordinate),
    Node(Id, Coordinate, Coordinate, Vec<Tag>),
    Way(Id, Vec<Id>, Vec<Tag>),
    Relation(Relation),
    // These two are here so we can terminate and skip uninteresting data without
    // using error handling.
    EndOfDocument,
    Ignored
}

impl FromStr for ElementType {
    type Err = ErrorKind;

    fn from_str(s: &str) -> Result<ElementType, ErrorKind> {
        let downcased = s.to_lowercase();

        if downcased == "bounds" {
            return Ok(ElementType::Bounds);
        }

        if downcased == "node" {
            return Ok(ElementType::Node);
        }

        if downcased == "way" {
            return Ok(ElementType::Way);
        }

        if downcased == "relation" {
            return Ok(ElementType::Relation);
        }

        if downcased == "tag" {
            return Ok(ElementType::Tag);
        }

        if downcased == "nd" {
            return Ok(ElementType::NodeRef);
        }

        if downcased == "member" {
            return Ok(ElementType::Member);
        }

        Err(ErrorKind::UnknownElement)
    }
}

fn parse_element_data(parser: &mut EventReader<BufReader<File>>) -> Result<ElementData, ErrorKind> {
    let element = try!(parser.next());
    match element {
        XmlEvent::EndDocument => Ok(ElementData::EndOfDocument),
        XmlEvent::StartElement { name, attributes, .. } => {
            let element_type = try!(ElementType::from_str(&name.local_name));

            match element_type {
                ElementType::Bounds => parse_bounds(&attributes),
                ElementType::Node => parse_node(parser, &attributes),
                ElementType::Way => parse_way(parser, &attributes),
                ElementType::Relation => parse_relation(parser, &attributes),
                _ => Err(ErrorKind::UnknownElement)
            }
        },
        _ => Ok(ElementData::Ignored)
    }
}

fn parse_relation(parser: &mut EventReader<BufReader<File>>, attrs: &Vec<OwnedAttribute>) -> Result<ElementData, ErrorKind> {
    let id = try!(find_attribute("id", attrs).map_err(ErrorKind::MalformedRelation));

    let mut members = Vec::new();
    let mut tags = Vec::new();

    loop {
        match try!(parser.next()) {
            XmlEvent::EndElement { name } => {
                let element_type = try!(ElementType::from_str(&name.local_name));

                match element_type {
                    ElementType::Relation => return Ok(ElementData::Relation(Relation {
                        id: id,
                        members: members,
                        tags: tags
                    })),
                    _ => continue
                }
            },
            XmlEvent::StartElement { name, attributes, .. } => {
                let element_type = try!(ElementType::from_str(&name.local_name));

                match element_type {
                    ElementType::Tag => {
                        if let Ok(tag) = parse_tag(&attributes) {
                            tags.push(tag);
                        }
                    },
                    ElementType::Member => {
                        let el_type = try!(find_attribute_uncasted("type", &attributes).map_err(ErrorKind::MalformedRelation));
                        let el_ref = try!(find_attribute("ref", &attributes).map_err(ErrorKind::MalformedRelation));
                        let el_role = try!(find_attribute_uncasted("role", &attributes).map_err(ErrorKind::MalformedRelation));

                        let el = match el_type.to_lowercase().as_ref() {
                            "node" => Member::Node(el_ref, el_role),
                            "way" => Member::Way(el_ref, el_role),
                            "relation" => Member::Relation(el_ref, el_role),
                            _ => return Err(ErrorKind::MalformedRelation(AttributeError::Missing))
                        };

                        members.push(el);
                    },
                    ElementType::Bounds   |
                    ElementType::Node     |
                    ElementType::Relation |
                    ElementType::Way      |
                    ElementType::NodeRef  => return Err(
                        ErrorKind::MalformedRelation(AttributeError::IllegalNesting)
                    )
                }
            },
            _ => continue
        }
    }
}

fn parse_way(parser: &mut EventReader<BufReader<File>>, attrs: &Vec<OwnedAttribute>) -> Result<ElementData, ErrorKind> {
    let id = try!(find_attribute("id", attrs).map_err(ErrorKind::MalformedWay));

    let mut node_refs = Vec::new();
    let mut tags = Vec::new();

    loop {
        match try!(parser.next()) {
            XmlEvent::EndElement { name } => {
                let element_type = try!(ElementType::from_str(&name.local_name));

                match element_type {
                    ElementType::Way => return Ok(ElementData::Way(id, node_refs, tags)),
                    _ => continue
                }
            },
            XmlEvent::StartElement { name, attributes, .. } => {
                let element_type = try!(ElementType::from_str(&name.local_name));

                match element_type {
                    ElementType::Tag => {
                        if let Ok(tag) = parse_tag(&attributes) {
                            tags.push(tag);
                        }
                    },
                    ElementType::NodeRef => {
                        let node_ref = try!(find_attribute("ref", &attributes).map_err(ErrorKind::MalformedWay));
                        node_refs.push(node_ref);
                    },
                    ElementType::Bounds   |
                    ElementType::Node     |
                    ElementType::Relation |
                    ElementType::Way      |
                    ElementType::Member   => return Err(
                        ErrorKind::MalformedWay(AttributeError::IllegalNesting)
                    )
                }
            },
            _ => continue
        }
    }

}

fn parse_node(parser: &mut EventReader<BufReader<File>>, attrs: &Vec<OwnedAttribute>) -> Result<ElementData, ErrorKind> {
    let id = try!(find_attribute("id", attrs).map_err(ErrorKind::MalformedNode));
    let lat = try!(find_attribute("lat", attrs).map_err(ErrorKind::MalformedNode));
    let lon = try!(find_attribute("lon", attrs).map_err(ErrorKind::MalformedNode));

    let mut tags = Vec::new();

    loop {
        match try!(parser.next()) {
            XmlEvent::EndElement { name } => {
                let element_type = try!(ElementType::from_str(&name.local_name));

                match element_type {
                    ElementType::Node => return Ok(ElementData::Node(id, lat, lon, tags)),
                    _ => continue
                }
            },
            XmlEvent::StartElement { name, attributes, .. } => {
                let element_type = try!(ElementType::from_str(&name.local_name));

                match element_type {
                    ElementType::Tag => {
                        if let Ok(tag) = parse_tag(&attributes) {
                            tags.push(tag);
                        }
                    },
                    ElementType::Bounds   |
                    ElementType::Node     |
                    ElementType::Relation |
                    ElementType::Way      |
                    ElementType::NodeRef  |
                    ElementType::Member   => return Err(
                        ErrorKind::MalformedNode(AttributeError::IllegalNesting)
                    )
                }
            },
            _ => continue
        }
    }
}

fn parse_tag(attributes: &Vec<OwnedAttribute>) -> Result<Tag, ErrorKind> {
    let key = try!(find_attribute_uncasted("k", attributes).map_err(ErrorKind::MalformedTag));
    let val = try!(find_attribute_uncasted("v", attributes).map_err(ErrorKind::MalformedTag));
    Ok(Tag { key: key, val: val })
}

fn parse_bounds(attrs: &Vec<OwnedAttribute>) -> Result<ElementData, ErrorKind> {
    let minlat = try!(find_attribute("minlat", attrs).map_err(ErrorKind::BoundsMissing));
    let minlon = try!(find_attribute("minlon", attrs).map_err(ErrorKind::BoundsMissing));
    let maxlat = try!(find_attribute("maxlat", attrs).map_err(ErrorKind::BoundsMissing));
    let maxlon = try!(find_attribute("maxlon", attrs).map_err(ErrorKind::BoundsMissing));

    Ok(ElementData::Bounds(minlat, minlon, maxlat, maxlon))
}

fn find_attribute<T>(name: &str, attrs: &Vec<OwnedAttribute>) -> Result<T, AttributeError>
    where AttributeError: std::convert::From<<T as std::str::FromStr>::Err>,
          T: FromStr
{
    let val_raw = try!(find_attribute_uncasted(name, attrs));
    let val = try!(val_raw.parse::<T>());
    Ok(val)
}

fn find_attribute_uncasted(name: &str, attrs: &Vec<OwnedAttribute>) -> Result<String, AttributeError> {
    let attr = attrs.iter().find(|attr| attr.name.local_name == name);
    match attr {
        Some(a) => {
            Ok(a.value.clone())
        },
        None => Err(AttributeError::Missing)
    }
}
<|endoftext|>


<commit_before>extern crate "libnotify-sys" as sys;
extern crate "glib-2_0-sys" as glib;

use std::ffi::CString;
use std::marker::PhantomData;

use glib::types::{
    TRUE,
    FALSE
};

/// Error that can happen on context creation
#[derive(Debug)]
pub enum ContextCreationError {
    /// Context already exists
    AlreadyExists,
    InitFailure,
    NulError
}

#[derive(Debug)]
pub enum NotificationCreationError {
    NulError,
    Unknown
}

pub struct Context;

impl Context {
    pub fn new(app_name: &str) -> Result<Context, ContextCreationError> {
        unsafe {
            if sys::notify_is_initted() == TRUE {
                return Err(ContextCreationError::AlreadyExists);
            }
            let app_name = match CString::new(app_name) {
                Ok(name) => name,
                Err(_) => return Err(ContextCreationError::NulError)
            };
            if sys::notify_init(app_name.as_ptr()) == FALSE {
                return Err(ContextCreationError::InitFailure);
            }
        }
        Ok(Context)
    }
    pub fn new_notification(&self, summary: &str,
                                   body: Option<&str>,
                                   icon: Option<&str>)
        -> Result<Notification, NotificationCreationError> {
        let summary = match CString::new(summary) {
            Ok(cstr) => cstr,
            Err(_) => return Err(NotificationCreationError::NulError)
        };
        let body = match body {
            Some(body) => match CString::new(body) {
                Ok(cstr) => Some(cstr),
                Err(_) => return Err(NotificationCreationError::NulError)
            },
            None => None
        };
        let body_ptr = match body {
            Some(body) => body.as_ptr(),
            None => std::ptr::null()
        };
        let icon = match icon {
            Some(icon) => match CString::new(icon) {
                Ok(cstr) => Some(cstr),
                Err(_) => return Err(NotificationCreationError::NulError)
            },
            None => None
        };
        let icon_ptr = match icon {
            Some(icon) => icon.as_ptr(),
            None => std::ptr::null()
        };
        unsafe {
            let n = sys::notify_notification_new(summary.as_ptr(),
                                                 body_ptr,
                                                 icon_ptr);
            if n.is_null() {
                return Err(NotificationCreationError::Unknown);
            }

            Ok(Notification{handle: n, _phantom: PhantomData})
        }
    }
}

impl Drop for Context {
    fn drop(&mut self) {
        unsafe {
            sys::notify_uninit();
        }
    }
}

pub struct Notification<'a> {
    handle: *mut sys::NotifyNotification,
    _phantom: PhantomData<&'a Context>
}

impl<'a> Notification<'a> {
    pub fn show(&'a self) -> Result<(), ()> {
        unsafe {
            let mut err: *mut glib::GError = std::ptr::null_mut();
            sys::notify_notification_show(self.handle, &mut err);
            if !err.is_null() {
                glib::g_error_free(err);
                return Err(())
            }
            return Ok(())
        }
    }
}
<commit_msg>Add some documentation<commit_after>extern crate "libnotify-sys" as sys;
extern crate "glib-2_0-sys" as glib;

use std::ffi::CString;
use std::marker::PhantomData;

use glib::types::{
    TRUE,
    FALSE
};

/// Error that can happen on context creation
#[derive(Debug)]
pub enum ContextCreationError {
    /// Context already exists
    AlreadyExists,
    InitFailure,
    NulError
}

#[derive(Debug)]
pub enum NotificationCreationError {
    NulError,
    Unknown
}

/// The context which within libnotify operates
pub struct Context;

impl Context {
    /// Create a new context
    ///
    /// Arguments:
    ///
    /// - app_name: The name of the application using the context
    pub fn new(app_name: &str) -> Result<Context, ContextCreationError> {
        unsafe {
            if sys::notify_is_initted() == TRUE {
                return Err(ContextCreationError::AlreadyExists);
            }
            let app_name = match CString::new(app_name) {
                Ok(name) => name,
                Err(_) => return Err(ContextCreationError::NulError)
            };
            if sys::notify_init(app_name.as_ptr()) == FALSE {
                return Err(ContextCreationError::InitFailure);
            }
        }
        Ok(Context)
    }
    /// Creates a new Notification.
    ///
    /// Arguments:
    ///
    /// - summary: Required summary text
    /// - body: Optional body text
    /// - icon: Optional icon theme icon name or filename
    pub fn new_notification(&self, summary: &str,
                                   body: Option<&str>,
                                   icon: Option<&str>)
        -> Result<Notification, NotificationCreationError> {
        let summary = match CString::new(summary) {
            Ok(cstr) => cstr,
            Err(_) => return Err(NotificationCreationError::NulError)
        };
        let body = match body {
            Some(body) => match CString::new(body) {
                Ok(cstr) => Some(cstr),
                Err(_) => return Err(NotificationCreationError::NulError)
            },
            None => None
        };
        let body_ptr = match body {
            Some(body) => body.as_ptr(),
            None => std::ptr::null()
        };
        let icon = match icon {
            Some(icon) => match CString::new(icon) {
                Ok(cstr) => Some(cstr),
                Err(_) => return Err(NotificationCreationError::NulError)
            },
            None => None
        };
        let icon_ptr = match icon {
            Some(icon) => icon.as_ptr(),
            None => std::ptr::null()
        };
        unsafe {
            let n = sys::notify_notification_new(summary.as_ptr(),
                                                 body_ptr,
                                                 icon_ptr);
            if n.is_null() {
                return Err(NotificationCreationError::Unknown);
            }

            Ok(Notification{handle: n, _phantom: PhantomData})
        }
    }
}

impl Drop for Context {
    fn drop(&mut self) {
        unsafe {
            sys::notify_uninit();
        }
    }
}

/// A passive pop-up notification
pub struct Notification<'a> {
    handle: *mut sys::NotifyNotification,
    _phantom: PhantomData<&'a Context>
}

impl<'a> Notification<'a> {
    /// Tells the notification server to display the notification
    /// on the screen.
    pub fn show(&'a self) -> Result<(), ()> {
        unsafe {
            let mut err: *mut glib::GError = std::ptr::null_mut();
            sys::notify_notification_show(self.handle, &mut err);
            if !err.is_null() {
                glib::g_error_free(err);
                return Err(())
            }
            return Ok(())
        }
    }
}
<|endoftext|>


<commit_before>use std::io::{Read, Result, Write};

/// An adapter for readers whose inputs
/// are written to a tee(1)'d writer
pub struct TeeReader<R: Read, W: Write> {
    reader: R,
    writer: W,
}

impl<R: Read, W: Write> TeeReader<R, W> {
    /// Returns a TeeReader which can be used as Read whose
    /// reads delegate ready bytes read to the provided reader and write to the provided
    /// writer. This write must complete before the read completes.
    ///
    /// Errors reported by the read will be interpreted as Errors for the read
    pub fn new(reader: R, writer: W) -> TeeReader<R, W> {
        TeeReader {
            reader: reader,
            writer: writer,
        }
    }
}

impl<R: Read, W: Write> Read for TeeReader<R, W> {
    fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
        let n = try!(self.reader.read(buf));
        try!(self.writer.write_all(&buf[..n]));
        Ok(n)
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use std::io::Read;

    #[test]
    fn tee() {
        let mut reader = "It's over 9000!".as_bytes();
        let mut teeout = Vec::new();
        let mut stdout = Vec::new();
        {
            let mut tee = TeeReader::new(&mut reader, &mut teeout);
            let _ = tee.read_to_end(&mut stdout);
        }
        assert_eq!(teeout, stdout);
    }
}
<commit_msg>doc typos<commit_after>use std::io::{Read, Result, Write};

/// An adapter for readers whose inputs
/// are written to a "tee"'d writer
pub struct TeeReader<R: Read, W: Write> {
    reader: R,
    writer: W,
}

impl<R: Read, W: Write> TeeReader<R, W> {
    /// Returns a TeeReader which can be used as Read whose
    /// reads delegate bytes read to the provided reader and write to the provided
    /// writer. The write operation must complete before the read completes.
    ///
    /// Errors reported by the write operation will be interpreted as errors for the read
    pub fn new(reader: R, writer: W) -> TeeReader<R, W> {
        TeeReader {
            reader: reader,
            writer: writer,
        }
    }
}

impl<R: Read, W: Write> Read for TeeReader<R, W> {
    fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
        let n = try!(self.reader.read(buf));
        try!(self.writer.write_all(&buf[..n]));
        Ok(n)
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use std::io::Read;

    #[test]
    fn tee() {
        let mut reader = "It's over 9000!".as_bytes();
        let mut teeout = Vec::new();
        let mut stdout = Vec::new();
        {
            let mut tee = TeeReader::new(&mut reader, &mut teeout);
            let _ = tee.read_to_end(&mut stdout);
        }
        assert_eq!(teeout, stdout);
    }
}
<|endoftext|>


<commit_before>// Copyright 2013-2016, The Gtk-rs Project Developers.
// See the COPYRIGHT file at the top-level directory of this distribution.
// Licensed under the MIT license, see the LICENSE file or <http://opensource.org/licenses/MIT>

extern crate cairo_sys as ffi;
extern crate libc;

#[macro_use]
extern crate bitflags;

#[cfg(feature = "use_glib")]
#[macro_use]
extern crate glib;

#[cfg(feature = "use_glib")]
extern crate glib_sys as glib_ffi;

#[cfg(feature = "use_glib")]
extern crate gobject_sys as gobject_ffi;

#[cfg(test)]
extern crate tempfile;

// Helper macro for our GValue related trait impls
#[cfg(feature = "use_glib")]
macro_rules! gvalue_impl {
    ($name:ty, $ffi_name:ty, $get_type:expr) => {
        use glib;
        #[allow(unused_imports)]
        use glib::translate::*;
        use glib_ffi;
        use gobject_ffi;

        impl glib::types::StaticType for $name {
            fn static_type() -> glib::types::Type {
                unsafe { from_glib($get_type()) }
            }
        }

        impl<'a> glib::value::FromValueOptional<'a> for $name {
            unsafe fn from_value_optional(v: &'a glib::value::Value) -> Option<Self> {
                let ptr = gobject_ffi::g_value_get_boxed(v.to_glib_none().0);
                assert!(!ptr.is_null());
                from_glib_none(ptr as *mut $ffi_name)
            }
        }

        impl glib::value::SetValue for $name {
            unsafe fn set_value(v: &mut glib::value::Value, s: &Self) {
                gobject_ffi::g_value_set_boxed(
                    v.to_glib_none_mut().0,
                    s.to_glib_none().0 as glib_ffi::gpointer,
                );
            }
        }

        impl glib::value::SetValueOptional for $name {
            unsafe fn set_value_optional(v: &mut glib::value::Value, s: Option<&Self>) {
                if let Some(s) = s {
                    gobject_ffi::g_value_set_boxed(
                        v.to_glib_none_mut().0,
                        s.to_glib_none().0 as glib_ffi::gpointer,
                    );
                } else {
                    gobject_ffi::g_value_set_boxed(v.to_glib_none_mut().0, ::std::ptr::null_mut());
                }
            }
        }
    };
}

pub use user_data::UserDataKey;

pub use context::{Context, RectangleList};

pub use paths::{Path, PathSegment, PathSegments};

pub use device::Device;

pub use enums::*;

pub use error::{BorrowError, IoError};

pub use patterns::{LinearGradient, Mesh, Pattern, RadialGradient, SolidPattern, SurfacePattern};

pub use font::{
    FontExtents, FontFace, FontOptions, FontSlant, FontType, FontWeight, Glyph, ScaledFont,
    TextCluster, TextExtents,
};

pub use matrices::Matrix;

pub use recording_surface::RecordingSurface;
pub use rectangle::Rectangle;
pub use rectangle_int::RectangleInt;

pub use region::Region;

pub use surface::{MappedImageSurface, Surface};

pub use image_surface::{ImageSurface, ImageSurfaceData};

#[cfg(any(feature = "pdf", feature = "svg", feature = "ps", feature = "dox"))]
pub use stream::StreamWithError;

#[cfg(any(feature = "pdf", feature = "dox"))]
pub use pdf::PdfSurface;

#[cfg(any(feature = "ps", feature = "dox"))]
pub use ps::PsSurface;

#[cfg(any(feature = "svg", feature = "dox"))]
pub use svg::SvgSurface;

#[cfg(any(feature = "xcb", feature = "dox"))]
pub use xcb::{
    XCBConnection, XCBDrawable, XCBPixmap, XCBRenderPictFormInfo, XCBScreen, XCBSurface,
    XCBVisualType,
};

#[macro_use]
mod user_data;
mod constants;
pub use constants::*;
mod utils;
pub use utils::*;

mod context;
mod device;
mod enums;
mod error;
mod font;
mod image_surface;
#[cfg(any(feature = "png", feature = "dox"))]
mod image_surface_png;
mod matrices;
mod paths;
mod patterns;
mod recording_surface;
mod rectangle;
mod rectangle_int;
mod region;
mod surface;
#[cfg(any(feature = "xcb", feature = "dox"))]
mod xcb;

#[cfg(any(feature = "pdf", feature = "svg", feature = "ps", feature = "dox"))]
#[macro_use]
mod stream;
#[cfg(any(feature = "pdf", feature = "dox"))]
mod pdf;
#[cfg(any(feature = "ps", feature = "dox"))]
mod ps;
#[cfg(any(feature = "svg", feature = "dox"))]
mod svg;

#[cfg(any(target_os = "macos", target_os = "ios", feature = "dox"))]
mod quartz_surface;
#[cfg(any(target_os = "macos", target_os = "ios", feature = "dox"))]
pub use quartz_surface::QuartzSurface;

#[cfg(any(windows, feature = "dox"))]
mod win32_surface;

#[cfg(any(windows, feature = "dox"))]
pub use win32_surface::Win32Surface;
<commit_msg>Reexport the Gradient type too.<commit_after>// Copyright 2013-2016, The Gtk-rs Project Developers.
// See the COPYRIGHT file at the top-level directory of this distribution.
// Licensed under the MIT license, see the LICENSE file or <http://opensource.org/licenses/MIT>

extern crate cairo_sys as ffi;
extern crate libc;

#[macro_use]
extern crate bitflags;

#[cfg(feature = "use_glib")]
#[macro_use]
extern crate glib;

#[cfg(feature = "use_glib")]
extern crate glib_sys as glib_ffi;

#[cfg(feature = "use_glib")]
extern crate gobject_sys as gobject_ffi;

#[cfg(test)]
extern crate tempfile;

// Helper macro for our GValue related trait impls
#[cfg(feature = "use_glib")]
macro_rules! gvalue_impl {
    ($name:ty, $ffi_name:ty, $get_type:expr) => {
        use glib;
        #[allow(unused_imports)]
        use glib::translate::*;
        use glib_ffi;
        use gobject_ffi;

        impl glib::types::StaticType for $name {
            fn static_type() -> glib::types::Type {
                unsafe { from_glib($get_type()) }
            }
        }

        impl<'a> glib::value::FromValueOptional<'a> for $name {
            unsafe fn from_value_optional(v: &'a glib::value::Value) -> Option<Self> {
                let ptr = gobject_ffi::g_value_get_boxed(v.to_glib_none().0);
                assert!(!ptr.is_null());
                from_glib_none(ptr as *mut $ffi_name)
            }
        }

        impl glib::value::SetValue for $name {
            unsafe fn set_value(v: &mut glib::value::Value, s: &Self) {
                gobject_ffi::g_value_set_boxed(
                    v.to_glib_none_mut().0,
                    s.to_glib_none().0 as glib_ffi::gpointer,
                );
            }
        }

        impl glib::value::SetValueOptional for $name {
            unsafe fn set_value_optional(v: &mut glib::value::Value, s: Option<&Self>) {
                if let Some(s) = s {
                    gobject_ffi::g_value_set_boxed(
                        v.to_glib_none_mut().0,
                        s.to_glib_none().0 as glib_ffi::gpointer,
                    );
                } else {
                    gobject_ffi::g_value_set_boxed(v.to_glib_none_mut().0, ::std::ptr::null_mut());
                }
            }
        }
    };
}

pub use user_data::UserDataKey;

pub use context::{Context, RectangleList};

pub use paths::{Path, PathSegment, PathSegments};

pub use device::Device;

pub use enums::*;

pub use error::{BorrowError, IoError};

pub use patterns::{
    Gradient, LinearGradient, Mesh, Pattern, RadialGradient, SolidPattern, SurfacePattern
};

pub use font::{
    FontExtents, FontFace, FontOptions, FontSlant, FontType, FontWeight, Glyph, ScaledFont,
    TextCluster, TextExtents,
};

pub use matrices::Matrix;

pub use recording_surface::RecordingSurface;
pub use rectangle::Rectangle;
pub use rectangle_int::RectangleInt;

pub use region::Region;

pub use surface::{MappedImageSurface, Surface};

pub use image_surface::{ImageSurface, ImageSurfaceData};

#[cfg(any(feature = "pdf", feature = "svg", feature = "ps", feature = "dox"))]
pub use stream::StreamWithError;

#[cfg(any(feature = "pdf", feature = "dox"))]
pub use pdf::PdfSurface;

#[cfg(any(feature = "ps", feature = "dox"))]
pub use ps::PsSurface;

#[cfg(any(feature = "svg", feature = "dox"))]
pub use svg::SvgSurface;

#[cfg(any(feature = "xcb", feature = "dox"))]
pub use xcb::{
    XCBConnection, XCBDrawable, XCBPixmap, XCBRenderPictFormInfo, XCBScreen, XCBSurface,
    XCBVisualType,
};

#[macro_use]
mod user_data;
mod constants;
pub use constants::*;
mod utils;
pub use utils::*;

mod context;
mod device;
mod enums;
mod error;
mod font;
mod image_surface;
#[cfg(any(feature = "png", feature = "dox"))]
mod image_surface_png;
mod matrices;
mod paths;
mod patterns;
mod recording_surface;
mod rectangle;
mod rectangle_int;
mod region;
mod surface;
#[cfg(any(feature = "xcb", feature = "dox"))]
mod xcb;

#[cfg(any(feature = "pdf", feature = "svg", feature = "ps", feature = "dox"))]
#[macro_use]
mod stream;
#[cfg(any(feature = "pdf", feature = "dox"))]
mod pdf;
#[cfg(any(feature = "ps", feature = "dox"))]
mod ps;
#[cfg(any(feature = "svg", feature = "dox"))]
mod svg;

#[cfg(any(target_os = "macos", target_os = "ios", feature = "dox"))]
mod quartz_surface;
#[cfg(any(target_os = "macos", target_os = "ios", feature = "dox"))]
pub use quartz_surface::QuartzSurface;

#[cfg(any(windows, feature = "dox"))]
mod win32_surface;

#[cfg(any(windows, feature = "dox"))]
pub use win32_surface::Win32Surface;
<|endoftext|>