<commit_before>//! Serenity is a Rust library for the Discord API.
//!
//! View the [examples] on how to make and structure a bot.
//!
//! Serenity supports bot user authentication via the use of [`Client::builder`].
//!
//! Once logged in, you may add handlers to your client to dispatch [`Event`]s,
//! such as [`Client::on_message`]. This will cause your handler to be called
//! when a [`Event::MessageCreate`] is received. Each handler is given a
//! [`Context`], giving information about the event. See the
//! [client's module-level documentation].
//!
//! The [`Shard`] is transparently handled by the library, removing
//! unnecessary complexity. Sharded connections are automatically handled for
//! you. See the [gateway's documentation][gateway docs] for more information.
//!
//! A [`Cache`] is also provided for you. This will be updated automatically for
//! you as data is received from the Discord API via events. When calling a
//! method on a [`Context`], the cache will first be searched for relevant data
//! to avoid unnecessary HTTP requests to the Discord API. For more information,
//! see the [cache's module-level documentation][cache docs].
//!
//! Note that, although this documentation will try to be as up-to-date and
//! accurate as possible, Discord hosts [official documentation][docs]. If you
//! need to be sure that some information piece is sanctioned by Discord, refer
//! to their own documentation.
//!
//! ### Full Examples
//!
//! Full examples, detailing and explaining usage of the basic functionality of the
//! library, can be found in the [`examples`] directory.
//!
//! # Installation
//!
//! Add the following to your `Cargo.toml` file:
//!
//! ```toml
//! [dependencies]
//! serenity = "0.9.0"
//! ```
//!
//! [`Cache`]: cache/struct.Cache.html
//! [`Client::builder`]: client/struct.Client.html#method.builder
//! [`Client::on_message`]: client/struct.Client.html#method.on_message
//! [`Context`]: client/struct.Context.html
//! [`Event`]: model/event/enum.Event.html
//! [`Event::MessageCreate`]: model/event/enum.Event.html#variant.MessageCreate
//! [`Shard`]: gateway/struct.Shard.html
//! [`examples`]: https://github.com/serenity-rs/serenity/blob/current/examples
//! [cache docs]: cache/index.html
//! [client's module-level documentation]: client/index.html
//! [docs]: https://discord.com/developers/docs/intro
//! [examples]: https://github.com/serenity-rs/serenity/tree/current/examples
//! [gateway docs]: gateway/index.html
#![doc(html_root_url = "https://docs.rs/serenity/*")]
#![deny(rust_2018_idioms)]
#![type_length_limit="3294819"] // needed so ShardRunner::run compiles with instrument.

#[macro_use]
extern crate serde;

#[macro_use]
mod internal;

pub mod constants;
pub mod model;
pub mod prelude;

#[cfg(feature = "builder")]
pub mod builder;
#[cfg(feature = "cache")]
pub mod cache;
#[cfg(feature = "client")]
pub mod client;
#[cfg(feature = "framework")]
pub mod framework;
#[cfg(feature = "gateway")]
pub mod gateway;
#[cfg(feature = "http")]
pub mod http;
#[cfg(feature = "utils")]
pub mod utils;
#[cfg(feature = "collector")]
pub mod collector;

mod error;

pub use crate::error::{Error, Result};

#[cfg(all(feature = "client", feature = "gateway"))]
pub use crate::client::Client;

#[cfg(all(feature = "client", feature = "cache"))]
use crate::cache::Cache;
#[cfg(all(feature = "client", feature = "cache"))]
use std::time::Duration;
#[cfg(feature = "client")]
use std::sync::Arc;
#[cfg(feature = "client")]
use crate::http::Http;

#[cfg(feature = "client")]
#[derive(Clone, Default)]
#[non_exhaustive]
pub struct CacheAndHttp {
    #[cfg(feature = "cache")]
    pub cache: Arc<Cache>,
    #[cfg(feature = "cache")]
    pub update_cache_timeout: Option<Duration>,
    pub http: Arc<Http>,
}

// For the procedural macros in `command_attr`.
#[cfg(feature = "standard_framework")]
#[doc(hidden)]
pub use static_assertions;

pub use async_trait::async_trait;
pub use futures;
pub use futures::future::FutureExt;
<commit_msg>Update the version in lib.rs to rely on any patch version in 0.9<commit_after>//! Serenity is a Rust library for the Discord API.
//!
//! View the [examples] on how to make and structure a bot.
//!
//! Serenity supports bot user authentication via the use of [`Client::builder`].
//!
//! Once logged in, you may add handlers to your client to dispatch [`Event`]s,
//! such as [`Client::on_message`]. This will cause your handler to be called
//! when a [`Event::MessageCreate`] is received. Each handler is given a
//! [`Context`], giving information about the event. See the
//! [client's module-level documentation].
//!
//! The [`Shard`] is transparently handled by the library, removing
//! unnecessary complexity. Sharded connections are automatically handled for
//! you. See the [gateway's documentation][gateway docs] for more information.
//!
//! A [`Cache`] is also provided for you. This will be updated automatically for
//! you as data is received from the Discord API via events. When calling a
//! method on a [`Context`], the cache will first be searched for relevant data
//! to avoid unnecessary HTTP requests to the Discord API. For more information,
//! see the [cache's module-level documentation][cache docs].
//!
//! Note that, although this documentation will try to be as up-to-date and
//! accurate as possible, Discord hosts [official documentation][docs]. If you
//! need to be sure that some information piece is sanctioned by Discord, refer
//! to their own documentation.
//!
//! ### Full Examples
//!
//! Full examples, detailing and explaining usage of the basic functionality of the
//! library, can be found in the [`examples`] directory.
//!
//! # Installation
//!
//! Add the following to your `Cargo.toml` file:
//!
//! ```toml
//! [dependencies]
//! serenity = "0.9"
//! ```
//!
//! [`Cache`]: cache/struct.Cache.html
//! [`Client::builder`]: client/struct.Client.html#method.builder
//! [`Client::on_message`]: client/struct.Client.html#method.on_message
//! [`Context`]: client/struct.Context.html
//! [`Event`]: model/event/enum.Event.html
//! [`Event::MessageCreate`]: model/event/enum.Event.html#variant.MessageCreate
//! [`Shard`]: gateway/struct.Shard.html
//! [`examples`]: https://github.com/serenity-rs/serenity/blob/current/examples
//! [cache docs]: cache/index.html
//! [client's module-level documentation]: client/index.html
//! [docs]: https://discord.com/developers/docs/intro
//! [examples]: https://github.com/serenity-rs/serenity/tree/current/examples
//! [gateway docs]: gateway/index.html
#![doc(html_root_url = "https://docs.rs/serenity/*")]
#![deny(rust_2018_idioms)]
#![type_length_limit="3294819"] // needed so ShardRunner::run compiles with instrument.

#[macro_use]
extern crate serde;

#[macro_use]
mod internal;

pub mod constants;
pub mod model;
pub mod prelude;

#[cfg(feature = "builder")]
pub mod builder;
#[cfg(feature = "cache")]
pub mod cache;
#[cfg(feature = "client")]
pub mod client;
#[cfg(feature = "framework")]
pub mod framework;
#[cfg(feature = "gateway")]
pub mod gateway;
#[cfg(feature = "http")]
pub mod http;
#[cfg(feature = "utils")]
pub mod utils;
#[cfg(feature = "collector")]
pub mod collector;

mod error;

pub use crate::error::{Error, Result};

#[cfg(all(feature = "client", feature = "gateway"))]
pub use crate::client::Client;

#[cfg(all(feature = "client", feature = "cache"))]
use crate::cache::Cache;
#[cfg(all(feature = "client", feature = "cache"))]
use std::time::Duration;
#[cfg(feature = "client")]
use std::sync::Arc;
#[cfg(feature = "client")]
use crate::http::Http;

#[cfg(feature = "client")]
#[derive(Clone, Default)]
#[non_exhaustive]
pub struct CacheAndHttp {
    #[cfg(feature = "cache")]
    pub cache: Arc<Cache>,
    #[cfg(feature = "cache")]
    pub update_cache_timeout: Option<Duration>,
    pub http: Arc<Http>,
}

// For the procedural macros in `command_attr`.
#[cfg(feature = "standard_framework")]
#[doc(hidden)]
pub use static_assertions;

pub use async_trait::async_trait;
pub use futures;
pub use futures::future::FutureExt;
<|endoftext|>


<commit_before>extern crate rand;

use std::collections::BTreeMap;
use std::cmp;
use innovation::Innovation;
use rand::{Rng, Closed01};

mod innovation;

trait Gene: Clone {
    fn weight_distance(&self, other: &Self) -> f64;
}

enum NodeType {
    Input,
    Output,
    Hidden,
}

struct NodeGene {
    node_type: NodeType,
}

// To avoid node collisions we use Innovation numbers instead of node
// ids.
#[derive(Clone)]
struct LinkGene {
    // This points to the NodeGene of that innovation
    source_node_gene: Innovation,
    // This points to the NodeGene of that innovation
    target_node_gene: Innovation,
    weight: f64,
    active: bool,
}

impl Gene for LinkGene {
    fn weight_distance(&self, other: &LinkGene) -> f64 {
        self.weight - other.weight
    }
}

struct Genome {
    link_genes: BTreeMap<Innovation, LinkGene>,
    node_genes: BTreeMap<Innovation, NodeGene>,
}

enum Correlation<'a, T: 'a> {
    Matching(&'a T, &'a T),
    DisjointLeft(&'a T),
    DisjointRight(&'a T),
    ExcessLeft(&'a T),
    ExcessRight(&'a T),
}

/// Correlate the genes in ```genes_left``` with those in ```genes_right```.
fn correlate<'a, T, F>(genes_left: &'a BTreeMap<Innovation, T>,
                       genes_right: &'a BTreeMap<Innovation, T>,
                       f: &mut F)
    where F: FnMut(Innovation, Correlation<'a, T>)
{
    let range_left = innovation::innovation_range(genes_left);
    let range_right = innovation::innovation_range(genes_right);

    for (innov_left, gene_left) in genes_left.iter() {
        if innov_left.is_within(&range_right) {
            match genes_right.get(innov_left) {
                Some(gene_right) => f(*innov_left, Correlation::Matching(gene_left, gene_right)),
                None => f(*innov_left, Correlation::DisjointLeft(gene_left)),
            }
        } else {
            f(*innov_left, Correlation::ExcessLeft(gene_left))
        }
    }

    for (innov_right, gene_right) in genes_right.iter() {
        if innov_right.is_within(&range_left) {
            if !genes_left.contains_key(innov_right) {
                f(*innov_right, Correlation::DisjointRight(gene_right))
            }
        } else {
            f(*innov_right, Correlation::ExcessRight(gene_right))
        }
    }
}

struct CompatibilityCoefficients {
    excess: f64,
    disjoint: f64,
    weight: f64,
}

/// Calculates the compatibility between two gene lists.
fn compatibility<T: Gene>(genes1: &BTreeMap<Innovation, T>,
                              genes2: &BTreeMap<Innovation, T>,
                              coeff: &CompatibilityCoefficients)
                              -> f64 {
    let max_len = cmp::max(genes1.len(), genes2.len());
    assert!(max_len > 0);

    let mut matching = 0;
    let mut disjoint = 0;
    let mut excess = 0;
    let mut weight_dist = 0.0;

    correlate(genes1,
              genes2,
              &mut |_, correlation| {
                  match correlation {
                      Correlation::Matching(gene_left, gene_right) => {
                          matching += 1;
                          weight_dist += gene_left.weight_distance(gene_right).abs();
                      }
                      Correlation::DisjointLeft(_) | Correlation::DisjointRight(_) => {
                          disjoint += 1;
                      }
                      Correlation::ExcessLeft(_) | Correlation::ExcessRight(_) => {
                          excess += 1;
                      }
                  }
              });

    assert!(2 * matching + disjoint + excess == genes1.len() + genes2.len());

    coeff.excess * (excess as f64) / (max_len as f64) +
    coeff.disjoint * (disjoint as f64) / (max_len as f64) +
    coeff.weight *
    if matching > 0 {
        weight_dist / (matching as f64)
    } else {
        0.0
    }
}

/// These describe the probabilities to take a gene from one of the
/// parents during crossover.
/// XXX: It's probably faster to not use floats here.
struct CrossoverProbabilities {
    /// Probability to take a matching gene from the fitter (first) parent.
    prob_match1: Closed01<f32>,

    /// Probability to take a disjoint gene from the fitter (first) parent.
    prob_disjoint1: Closed01<f32>,

    /// Probability to take an excess gene from the fitter (first) parent.
    prob_excess1: Closed01<f32>,

    /// Probability to take a disjoint gene from the less fit parent.
    prob_disjoint2: Closed01<f32>,

    /// Probability to take an excess gene from the less fit parent.
    prob_excess2: Closed01<f32>,
}

fn is_probable<R: Rng>(prob: &Closed01<f32>, rng: &mut R) -> bool {
    if prob.0 < 1.0 {
        let v: f32 = rng.gen(); // half open [0, 1)
        v < prob.0
    } else {
        true
    }
}

/// We assume ```parent1``` to be the fitter parent. Takes gene
/// either from ```parent1``` or ```parent2``` according to
/// the probabilities specified and the relative fitness of the parents.
fn crossover<T: Gene, R: Rng>(parent1: &BTreeMap<Innovation, T>,
                              parent2: &BTreeMap<Innovation, T>,
                              p: &CrossoverProbabilities,
                              rng: &mut R)
                              -> BTreeMap<Innovation, T> {

    let mut offspring = BTreeMap::new();

    correlate(parent1,
              parent2,
              &mut |innov, correlation| {
                  match correlation {
                      Correlation::Matching(gene_left, gene_right) => {
                          if is_probable(&p.prob_match1, rng) {
                              offspring.insert(innov, gene_left.clone());
                          } else {
                              offspring.insert(innov, gene_right.clone());
                          }
                      }

                      Correlation::DisjointLeft(gene_left) => {
                          if is_probable(&p.prob_disjoint1, rng) {
                              offspring.insert(innov, gene_left.clone());
                          }
                      }

                      Correlation::DisjointRight(gene_right) => {
                          if is_probable(&p.prob_disjoint2, rng) {
                              offspring.insert(innov, gene_right.clone());
                          }
                      }

                      Correlation::ExcessLeft(gene_left) => {
                          if is_probable(&p.prob_excess1, rng) {
                              offspring.insert(innov, gene_left.clone());
                          }
                      }

                      Correlation::ExcessRight(gene_right) => {
                          if is_probable(&p.prob_excess2, rng) {
                              offspring.insert(innov, gene_right.clone());
                          }
                      }
                  }
              });

    offspring
}
<commit_msg>Rustfmt<commit_after>extern crate rand;

use std::collections::BTreeMap;
use std::cmp;
use innovation::Innovation;
use rand::{Rng, Closed01};

mod innovation;

trait Gene: Clone {
    fn weight_distance(&self, other: &Self) -> f64;
}

enum NodeType {
    Input,
    Output,
    Hidden,
}

struct NodeGene {
    node_type: NodeType,
}

// To avoid node collisions we use Innovation numbers instead of node
// ids.
#[derive(Clone)]
struct LinkGene {
    // This points to the NodeGene of that innovation
    source_node_gene: Innovation,
    // This points to the NodeGene of that innovation
    target_node_gene: Innovation,
    weight: f64,
    active: bool,
}

impl Gene for LinkGene {
    fn weight_distance(&self, other: &LinkGene) -> f64 {
        self.weight - other.weight
    }
}

struct Genome {
    link_genes: BTreeMap<Innovation, LinkGene>,
    node_genes: BTreeMap<Innovation, NodeGene>,
}

enum Correlation<'a, T: 'a> {
    Matching(&'a T, &'a T),
    DisjointLeft(&'a T),
    DisjointRight(&'a T),
    ExcessLeft(&'a T),
    ExcessRight(&'a T),
}

/// Correlate the genes in ```genes_left``` with those in ```genes_right```.
fn correlate<'a, T, F>(genes_left: &'a BTreeMap<Innovation, T>,
                       genes_right: &'a BTreeMap<Innovation, T>,
                       f: &mut F)
    where F: FnMut(Innovation, Correlation<'a, T>)
{
    let range_left = innovation::innovation_range(genes_left);
    let range_right = innovation::innovation_range(genes_right);

    for (innov_left, gene_left) in genes_left.iter() {
        if innov_left.is_within(&range_right) {
            match genes_right.get(innov_left) {
                Some(gene_right) => f(*innov_left, Correlation::Matching(gene_left, gene_right)),
                None => f(*innov_left, Correlation::DisjointLeft(gene_left)),
            }
        } else {
            f(*innov_left, Correlation::ExcessLeft(gene_left))
        }
    }

    for (innov_right, gene_right) in genes_right.iter() {
        if innov_right.is_within(&range_left) {
            if !genes_left.contains_key(innov_right) {
                f(*innov_right, Correlation::DisjointRight(gene_right))
            }
        } else {
            f(*innov_right, Correlation::ExcessRight(gene_right))
        }
    }
}

struct CompatibilityCoefficients {
    excess: f64,
    disjoint: f64,
    weight: f64,
}

/// Calculates the compatibility between two gene lists.
fn compatibility<T: Gene>(genes1: &BTreeMap<Innovation, T>,
                          genes2: &BTreeMap<Innovation, T>,
                          coeff: &CompatibilityCoefficients)
                          -> f64 {
    let max_len = cmp::max(genes1.len(), genes2.len());
    assert!(max_len > 0);

    let mut matching = 0;
    let mut disjoint = 0;
    let mut excess = 0;
    let mut weight_dist = 0.0;

    correlate(genes1,
              genes2,
              &mut |_, correlation| {
                  match correlation {
                      Correlation::Matching(gene_left, gene_right) => {
                          matching += 1;
                          weight_dist += gene_left.weight_distance(gene_right).abs();
                      }
                      Correlation::DisjointLeft(_) | Correlation::DisjointRight(_) => {
                          disjoint += 1;
                      }
                      Correlation::ExcessLeft(_) | Correlation::ExcessRight(_) => {
                          excess += 1;
                      }
                  }
              });

    assert!(2 * matching + disjoint + excess == genes1.len() + genes2.len());

    coeff.excess * (excess as f64) / (max_len as f64) +
    coeff.disjoint * (disjoint as f64) / (max_len as f64) +
    coeff.weight *
    if matching > 0 {
        weight_dist / (matching as f64)
    } else {
        0.0
    }
}

/// These describe the probabilities to take a gene from one of the
/// parents during crossover.
/// XXX: It's probably faster to not use floats here.
struct CrossoverProbabilities {
    /// Probability to take a matching gene from the fitter (first) parent.
    prob_match1: Closed01<f32>,

    /// Probability to take a disjoint gene from the fitter (first) parent.
    prob_disjoint1: Closed01<f32>,

    /// Probability to take an excess gene from the fitter (first) parent.
    prob_excess1: Closed01<f32>,

    /// Probability to take a disjoint gene from the less fit parent.
    prob_disjoint2: Closed01<f32>,

    /// Probability to take an excess gene from the less fit parent.
    prob_excess2: Closed01<f32>,
}

fn is_probable<R: Rng>(prob: &Closed01<f32>, rng: &mut R) -> bool {
    if prob.0 < 1.0 {
        let v: f32 = rng.gen(); // half open [0, 1)
        v < prob.0
    } else {
        true
    }
}

/// We assume ```parent1``` to be the fitter parent. Takes gene
/// either from ```parent1``` or ```parent2``` according to
/// the probabilities specified and the relative fitness of the parents.
fn crossover<T: Gene, R: Rng>(parent1: &BTreeMap<Innovation, T>,
                              parent2: &BTreeMap<Innovation, T>,
                              p: &CrossoverProbabilities,
                              rng: &mut R)
                              -> BTreeMap<Innovation, T> {

    let mut offspring = BTreeMap::new();

    correlate(parent1,
              parent2,
              &mut |innov, correlation| {
                  match correlation {
                      Correlation::Matching(gene_left, gene_right) => {
                          if is_probable(&p.prob_match1, rng) {
                              offspring.insert(innov, gene_left.clone());
                          } else {
                              offspring.insert(innov, gene_right.clone());
                          }
                      }

                      Correlation::DisjointLeft(gene_left) => {
                          if is_probable(&p.prob_disjoint1, rng) {
                              offspring.insert(innov, gene_left.clone());
                          }
                      }

                      Correlation::DisjointRight(gene_right) => {
                          if is_probable(&p.prob_disjoint2, rng) {
                              offspring.insert(innov, gene_right.clone());
                          }
                      }

                      Correlation::ExcessLeft(gene_left) => {
                          if is_probable(&p.prob_excess1, rng) {
                              offspring.insert(innov, gene_left.clone());
                          }
                      }

                      Correlation::ExcessRight(gene_right) => {
                          if is_probable(&p.prob_excess2, rng) {
                              offspring.insert(innov, gene_right.clone());
                          }
                      }
                  }
              });

    offspring
}
<|endoftext|>


<commit_before>#![crate_type = "lib"]

extern crate core;
extern crate getopts;
extern crate "rustc-serialize" as rustc_serialize;

use getopts::Matches;
use core::str::FromStr;
//use std::str::StrSlice;
use rustc_serialize::Decodable;
use std::num;
use std::result::Result;

#[derive(PartialEq, Eq, Show)]
pub enum ErrorType {
  UnimplementedDecoder,
  MissingField(String),
  ExpectedType(String, String, String),
  GenericError(String)
}

#[derive(PartialEq, Eq, Show)]
pub struct Error {
  e: ErrorType
}

pub type DecodeResult<T> =  Result<T, ErrorType>;

pub struct Decoder {
  matches: Matches,
  cur: String,
  current_type: String
}

impl Decoder {
  pub fn new(matches: Matches) -> Decoder {
    Decoder {
      matches: matches,
      cur: "".to_string(),
      current_type: "".to_string()
    }
  }

  fn expected(&self, expected_type: String) -> ErrorType {
    ErrorType::ExpectedType(self.cur.to_string(),
                       expected_type.to_string(),
                       self.matches.opt_str(self.cur.as_slice()).unwrap())
  }

}

pub fn decode<T:Send+Decodable>(matches: Matches) -> DecodeResult<T> {
  let mut decoder = Decoder::new(matches);
  Decodable::decode(&mut decoder)
}

impl ErrorType {
  pub fn to_err_msg(self) -> String {
    match self {
      ErrorType::UnimplementedDecoder => format!("this function is not implemented"),
      ErrorType::MissingField(ref s)  => format!("the required field '{}' is not present", s),
      ErrorType::ExpectedType(ref field, ref expected, ref value) => {
        format!("Expected type '{}' for field '{}' but got value '{}'", expected, field, value)
      },
      ErrorType::GenericError(ref s)  => format!("generic error: {}", s)
    }
  }
}
impl Decoder {
  fn get_field<T:FromStr>(&self, field: &str) -> Option<T> {
    match self.matches.opt_str(self.cur.as_slice()) {
      None    => None,
      Some(s) => FromStr::from_str(s.as_slice()).ok()
    }
  }
}

macro_rules! read_primitive {
    ($name:ident, $ty:ty) => {
        fn $name(&mut self) -> DecodeResult<$ty> {
          match self.matches.opt_str(self.cur.as_slice()) {
            None    => Err(ErrorType::MissingField(self.cur.clone())),
            Some(s) => match FromStr::from_str(s.as_slice()).ok() {
              None     => Err(self.expected("u64".to_string())),
              Some(nb) => Ok(nb)
            }
          }
        }
    }
}

impl rustc_serialize::Decoder for Decoder {
  type Error = ErrorType;

  fn read_nil(&mut self) -> DecodeResult<()> {
    Err(ErrorType::UnimplementedDecoder)
  }

  read_primitive! { read_usize, usize }
  read_primitive! { read_u8, u8 }
  read_primitive! { read_u16, u16 }
  read_primitive! { read_u32, u32 }
  read_primitive! { read_u64, u64 }
  read_primitive! { read_isize, isize }
  read_primitive! { read_i8, i8 }
  read_primitive! { read_i16, i16 }
  read_primitive! { read_i32, i32 }
  read_primitive! { read_i64, i64 }

  fn read_f32(&mut self) -> DecodeResult<f32> { self.read_f64().map(|x| x as f32) }
  fn read_f64(&mut self) -> DecodeResult<f64> {
    match self.matches.opt_str(self.cur.as_slice()) {
      None    => Err(ErrorType::MissingField(self.cur.clone())),
      Some(s) => match FromStr::from_str(s.as_slice()).ok() {
        None     => Err(self.expected("f64".to_string())),
        Some(nb) => Ok(nb)
      }
    }
  }

  fn read_bool(&mut self) -> DecodeResult<bool> {
    match self.matches.opt_str(self.cur.as_slice()) {
      None    => Err(ErrorType::MissingField(self.cur.clone())),
      Some(s) => match FromStr::from_str(s.as_slice()).ok() {
        None     => Err(self.expected("boolean".to_string())),
        Some(b) => Ok(b)
      }
    }
  }

  fn read_char(&mut self) -> DecodeResult<char> {
    match self.matches.opt_str(self.cur.as_slice()) {
      None    => Err(ErrorType::MissingField(self.cur.clone())),
      Some(s) => if s.as_slice().chars().count() == 1 { Ok(s.as_slice().char_at(0)) } else { Err(self.expected("char".to_string())) }
    }
  }

  fn read_str(&mut self) -> DecodeResult<String> {
    match self.matches.opt_str(self.cur.as_slice()) {
      None    => Err(ErrorType::MissingField(self.cur.clone())),
      Some(s) => Ok(s)
    }
  }

  fn read_enum<T, F>(&mut self, name: &str, f: F) -> DecodeResult<T> where F: FnOnce(&mut Decoder) -> DecodeResult<T> {
    //println!("reading enum: {}", name);
    self.current_type = name.to_string();
    f(self)
  }

  fn read_enum_variant<T, F>(&mut self, names: &[&str], f: F) -> DecodeResult<T> where F: FnOnce(&mut Decoder, usize) -> DecodeResult<T> {
    //println!("reading enum variant({}): {}", self.cur, names);
    match self.matches.opt_str(self.cur.as_slice()) {
      None    => Err(ErrorType::MissingField(self.cur.clone())),
      Some(s) => match names.iter().position(|&e| e == s.as_slice()) {
        None    => {
          let mut s = self.current_type.clone();
          s.push_str(" enum");
          Err(self.expected(s))
        },
        Some(i) => f(self, i)
      }
    }
  }

  fn read_enum_variant_arg<T, F>(&mut self, a_idx: usize, f: F) -> DecodeResult<T> where F: FnOnce(&mut Decoder) -> DecodeResult<T> {
    //println!("reading enum variant_arg: {}", a_idx);
    f(self);
    Err(ErrorType::UnimplementedDecoder)
  }

  fn read_enum_struct_variant<T, F>(&mut self, names: &[&str], f: F) -> DecodeResult<T> where F: FnOnce(&mut Decoder, usize) -> DecodeResult<T> {
    //println!("reading enum struct variant: {}", names);
    f(self, 0);
    Err(ErrorType::UnimplementedDecoder)
  }

  fn read_enum_struct_variant_field<T, F>(&mut self, f_name: &str, f_idx: usize, f: F) -> DecodeResult<T> where F: FnOnce(&mut Decoder) -> DecodeResult<T> {
    //println!("reading enum struct variant field: {}, {}", f_name, f_idx);
    f(self);
    Err(ErrorType::UnimplementedDecoder)
  }

  fn read_struct<T, F>(&mut self, s_name: &str, len: usize, f: F) -> DecodeResult<T> where F: FnOnce(&mut Decoder) -> DecodeResult<T> {
    //println!("reading struct: {} | len = {}", s_name, len);
    self.cur = s_name.to_string();
    f(self)
  }

  fn read_struct_field<T, F>(&mut self, f_name: &str, f_idx: usize, f: F) -> DecodeResult<T> where F: FnOnce(&mut Decoder) -> DecodeResult<T> {
    //println!("reading struct field: {} | idx = {}", f_name, f_idx);
    self.cur = f_name.to_string();
    let data = f(self);
    //println!("got struct field data: {}", data);
    data
  }

  fn read_option<T, F>(&mut self, mut f: F) -> DecodeResult<T> where F: FnMut(&mut Decoder, bool) -> DecodeResult<T>
   {
    //println!("read_option");
    match self.matches.opt_str(self.cur.as_slice()) {
      None    => {
        //println!("option not there");
        f(self, false)
      },
      Some(_) => {
        //println!("option is there");
        f(self, true)
      }
    }
  }

  fn read_tuple<T, F>(&mut self,  tuple_len: usize, f: F) -> DecodeResult<T> where F: FnOnce(&mut Decoder) -> DecodeResult<T> {
    f(self);
    Err(ErrorType::UnimplementedDecoder)
  }

  fn read_tuple_arg<T, F>(&mut self, a_idx: usize, f: F) -> DecodeResult<T> where F: FnOnce(&mut Decoder) -> DecodeResult<T> {
    f(self);
    Err(ErrorType::UnimplementedDecoder)
  }

  fn read_tuple_struct<T, F>(&mut self, s_name: &str, len:usize, f: F) -> DecodeResult<T> where F: FnOnce(&mut Decoder) -> DecodeResult<T> {
    f(self);
    Err(ErrorType::UnimplementedDecoder)
  }

  fn read_tuple_struct_arg<T, F>(&mut self, a_idx: usize, f: F) -> DecodeResult<T> where F: FnOnce(&mut Decoder) -> DecodeResult<T> {
    f(self);
    Err(ErrorType::UnimplementedDecoder)
  }

  fn read_seq<T, F>(&mut self, f: F) -> DecodeResult<T> where F: FnOnce(&mut Decoder, usize) -> DecodeResult<T>
   {
    f(self, 0);
    Err(ErrorType::UnimplementedDecoder)
  }

  fn read_seq_elt<T, F>(&mut self, idx: usize, f: F) -> DecodeResult<T> where F: FnOnce(&mut Decoder) -> DecodeResult<T> {
    f(self);
    Err(ErrorType::UnimplementedDecoder)
  }

  fn read_map<T, F>(&mut self, f: F) -> DecodeResult<T> where F: FnOnce(&mut Decoder, usize) -> DecodeResult<T> {
    f(self, 0);
    Err(ErrorType::UnimplementedDecoder)
  }

  fn read_map_elt_key<T, F>(&mut self, idx: usize, f: F) -> DecodeResult<T> where F:FnOnce(&mut Decoder) -> DecodeResult<T> {
    f(self);
    Err(ErrorType::UnimplementedDecoder)
  }

  fn read_map_elt_val<T, F>(&mut self, idx: usize, f: F) -> DecodeResult<T> where F:FnOnce(&mut Decoder) -> DecodeResult<T> {
    f(self);
    Err(ErrorType::UnimplementedDecoder)
  }

  fn error(&mut self, err: &str) -> ErrorType {
    ErrorType::GenericError(err.to_string())
  }
}
<commit_msg>fix rustc-serialize import<commit_after>#![crate_type = "lib"]

extern crate core;
extern crate getopts;
extern crate rustc_serialize;

use getopts::Matches;
use core::str::FromStr;
//use std::str::StrSlice;
use rustc_serialize::Decodable;
use std::num;
use std::result::Result;

#[derive(PartialEq, Eq, Show)]
pub enum ErrorType {
  UnimplementedDecoder,
  MissingField(String),
  ExpectedType(String, String, String),
  GenericError(String)
}

#[derive(PartialEq, Eq, Show)]
pub struct Error {
  e: ErrorType
}

pub type DecodeResult<T> =  Result<T, ErrorType>;

pub struct Decoder {
  matches: Matches,
  cur: String,
  current_type: String
}

impl Decoder {
  pub fn new(matches: Matches) -> Decoder {
    Decoder {
      matches: matches,
      cur: "".to_string(),
      current_type: "".to_string()
    }
  }

  fn expected(&self, expected_type: String) -> ErrorType {
    ErrorType::ExpectedType(self.cur.to_string(),
                       expected_type.to_string(),
                       self.matches.opt_str(self.cur.as_slice()).unwrap())
  }

}

pub fn decode<T:Send+Decodable>(matches: Matches) -> DecodeResult<T> {
  let mut decoder = Decoder::new(matches);
  Decodable::decode(&mut decoder)
}

impl ErrorType {
  pub fn to_err_msg(self) -> String {
    match self {
      ErrorType::UnimplementedDecoder => format!("this function is not implemented"),
      ErrorType::MissingField(ref s)  => format!("the required field '{}' is not present", s),
      ErrorType::ExpectedType(ref field, ref expected, ref value) => {
        format!("Expected type '{}' for field '{}' but got value '{}'", expected, field, value)
      },
      ErrorType::GenericError(ref s)  => format!("generic error: {}", s)
    }
  }
}
impl Decoder {
  fn get_field<T:FromStr>(&self, field: &str) -> Option<T> {
    match self.matches.opt_str(self.cur.as_slice()) {
      None    => None,
      Some(s) => FromStr::from_str(s.as_slice()).ok()
    }
  }
}

macro_rules! read_primitive {
    ($name:ident, $ty:ty) => {
        fn $name(&mut self) -> DecodeResult<$ty> {
          match self.matches.opt_str(self.cur.as_slice()) {
            None    => Err(ErrorType::MissingField(self.cur.clone())),
            Some(s) => match FromStr::from_str(s.as_slice()).ok() {
              None     => Err(self.expected("u64".to_string())),
              Some(nb) => Ok(nb)
            }
          }
        }
    }
}

impl rustc_serialize::Decoder for Decoder {
  type Error = ErrorType;

  fn read_nil(&mut self) -> DecodeResult<()> {
    Err(ErrorType::UnimplementedDecoder)
  }

  read_primitive! { read_usize, usize }
  read_primitive! { read_u8, u8 }
  read_primitive! { read_u16, u16 }
  read_primitive! { read_u32, u32 }
  read_primitive! { read_u64, u64 }
  read_primitive! { read_isize, isize }
  read_primitive! { read_i8, i8 }
  read_primitive! { read_i16, i16 }
  read_primitive! { read_i32, i32 }
  read_primitive! { read_i64, i64 }

  fn read_f32(&mut self) -> DecodeResult<f32> { self.read_f64().map(|x| x as f32) }
  fn read_f64(&mut self) -> DecodeResult<f64> {
    match self.matches.opt_str(self.cur.as_slice()) {
      None    => Err(ErrorType::MissingField(self.cur.clone())),
      Some(s) => match FromStr::from_str(s.as_slice()).ok() {
        None     => Err(self.expected("f64".to_string())),
        Some(nb) => Ok(nb)
      }
    }
  }

  fn read_bool(&mut self) -> DecodeResult<bool> {
    match self.matches.opt_str(self.cur.as_slice()) {
      None    => Err(ErrorType::MissingField(self.cur.clone())),
      Some(s) => match FromStr::from_str(s.as_slice()).ok() {
        None     => Err(self.expected("boolean".to_string())),
        Some(b) => Ok(b)
      }
    }
  }

  fn read_char(&mut self) -> DecodeResult<char> {
    match self.matches.opt_str(self.cur.as_slice()) {
      None    => Err(ErrorType::MissingField(self.cur.clone())),
      Some(s) => if s.as_slice().chars().count() == 1 { Ok(s.as_slice().char_at(0)) } else { Err(self.expected("char".to_string())) }
    }
  }

  fn read_str(&mut self) -> DecodeResult<String> {
    match self.matches.opt_str(self.cur.as_slice()) {
      None    => Err(ErrorType::MissingField(self.cur.clone())),
      Some(s) => Ok(s)
    }
  }

  fn read_enum<T, F>(&mut self, name: &str, f: F) -> DecodeResult<T> where F: FnOnce(&mut Decoder) -> DecodeResult<T> {
    //println!("reading enum: {}", name);
    self.current_type = name.to_string();
    f(self)
  }

  fn read_enum_variant<T, F>(&mut self, names: &[&str], f: F) -> DecodeResult<T> where F: FnOnce(&mut Decoder, usize) -> DecodeResult<T> {
    //println!("reading enum variant({}): {}", self.cur, names);
    match self.matches.opt_str(self.cur.as_slice()) {
      None    => Err(ErrorType::MissingField(self.cur.clone())),
      Some(s) => match names.iter().position(|&e| e == s.as_slice()) {
        None    => {
          let mut s = self.current_type.clone();
          s.push_str(" enum");
          Err(self.expected(s))
        },
        Some(i) => f(self, i)
      }
    }
  }

  fn read_enum_variant_arg<T, F>(&mut self, a_idx: usize, f: F) -> DecodeResult<T> where F: FnOnce(&mut Decoder) -> DecodeResult<T> {
    //println!("reading enum variant_arg: {}", a_idx);
    f(self);
    Err(ErrorType::UnimplementedDecoder)
  }

  fn read_enum_struct_variant<T, F>(&mut self, names: &[&str], f: F) -> DecodeResult<T> where F: FnOnce(&mut Decoder, usize) -> DecodeResult<T> {
    //println!("reading enum struct variant: {}", names);
    f(self, 0);
    Err(ErrorType::UnimplementedDecoder)
  }

  fn read_enum_struct_variant_field<T, F>(&mut self, f_name: &str, f_idx: usize, f: F) -> DecodeResult<T> where F: FnOnce(&mut Decoder) -> DecodeResult<T> {
    //println!("reading enum struct variant field: {}, {}", f_name, f_idx);
    f(self);
    Err(ErrorType::UnimplementedDecoder)
  }

  fn read_struct<T, F>(&mut self, s_name: &str, len: usize, f: F) -> DecodeResult<T> where F: FnOnce(&mut Decoder) -> DecodeResult<T> {
    //println!("reading struct: {} | len = {}", s_name, len);
    self.cur = s_name.to_string();
    f(self)
  }

  fn read_struct_field<T, F>(&mut self, f_name: &str, f_idx: usize, f: F) -> DecodeResult<T> where F: FnOnce(&mut Decoder) -> DecodeResult<T> {
    //println!("reading struct field: {} | idx = {}", f_name, f_idx);
    self.cur = f_name.to_string();
    let data = f(self);
    //println!("got struct field data: {}", data);
    data
  }

  fn read_option<T, F>(&mut self, mut f: F) -> DecodeResult<T> where F: FnMut(&mut Decoder, bool) -> DecodeResult<T>
   {
    //println!("read_option");
    match self.matches.opt_str(self.cur.as_slice()) {
      None    => {
        //println!("option not there");
        f(self, false)
      },
      Some(_) => {
        //println!("option is there");
        f(self, true)
      }
    }
  }

  fn read_tuple<T, F>(&mut self,  tuple_len: usize, f: F) -> DecodeResult<T> where F: FnOnce(&mut Decoder) -> DecodeResult<T> {
    f(self);
    Err(ErrorType::UnimplementedDecoder)
  }

  fn read_tuple_arg<T, F>(&mut self, a_idx: usize, f: F) -> DecodeResult<T> where F: FnOnce(&mut Decoder) -> DecodeResult<T> {
    f(self);
    Err(ErrorType::UnimplementedDecoder)
  }

  fn read_tuple_struct<T, F>(&mut self, s_name: &str, len:usize, f: F) -> DecodeResult<T> where F: FnOnce(&mut Decoder) -> DecodeResult<T> {
    f(self);
    Err(ErrorType::UnimplementedDecoder)
  }

  fn read_tuple_struct_arg<T, F>(&mut self, a_idx: usize, f: F) -> DecodeResult<T> where F: FnOnce(&mut Decoder) -> DecodeResult<T> {
    f(self);
    Err(ErrorType::UnimplementedDecoder)
  }

  fn read_seq<T, F>(&mut self, f: F) -> DecodeResult<T> where F: FnOnce(&mut Decoder, usize) -> DecodeResult<T>
   {
    f(self, 0);
    Err(ErrorType::UnimplementedDecoder)
  }

  fn read_seq_elt<T, F>(&mut self, idx: usize, f: F) -> DecodeResult<T> where F: FnOnce(&mut Decoder) -> DecodeResult<T> {
    f(self);
    Err(ErrorType::UnimplementedDecoder)
  }

  fn read_map<T, F>(&mut self, f: F) -> DecodeResult<T> where F: FnOnce(&mut Decoder, usize) -> DecodeResult<T> {
    f(self, 0);
    Err(ErrorType::UnimplementedDecoder)
  }

  fn read_map_elt_key<T, F>(&mut self, idx: usize, f: F) -> DecodeResult<T> where F:FnOnce(&mut Decoder) -> DecodeResult<T> {
    f(self);
    Err(ErrorType::UnimplementedDecoder)
  }

  fn read_map_elt_val<T, F>(&mut self, idx: usize, f: F) -> DecodeResult<T> where F:FnOnce(&mut Decoder) -> DecodeResult<T> {
    f(self);
    Err(ErrorType::UnimplementedDecoder)
  }

  fn error(&mut self, err: &str) -> ErrorType {
    ErrorType::GenericError(err.to_string())
  }
}
<|endoftext|>


<commit_before>//! A crate that implements [editorconfig](http://editorconfig.org/).
extern crate regex;

extern crate ordermap;

mod ini;
use regex::{Regex, Captures};

use ordermap::OrderMap;

use std::path::{Path, PathBuf};
use std::error::Error;

/// Finds all possible `conffile`s starting from `path` until root.
fn crawl_paths(path: &Path, conffile: &str) -> Result<Vec<PathBuf>, Box<Error>> {
    let mut path = if path.exists() {
        path.canonicalize()?
    } else {
        path.to_path_buf()
    };

    let mut result = vec![];
    while path.parent().is_some() {
        let mut adjacent_file = path.clone();
        adjacent_file.set_file_name(conffile);
        path.pop();
        if !adjacent_file.exists() {
            continue;
        }
        result.push(adjacent_file);
    }
    return Ok(result);
}

fn has_imbalanced_braces(text: &str) -> bool {
    let mut depth = 0i32;
    let escaped_brace_regex = Regex::new(r"\\(\{|\})").unwrap();
    let text = escaped_brace_regex.replace_all(text, "");
    for c in text.chars() {
        if c == '{' {
            depth += 1;
        } else if c == '}' {
            depth -= 1;
            if depth < 0 {
                return true;
            }
        }
    }
    return depth != 0;
}

fn translate_alternation(caps: &Captures) -> String {
    if has_imbalanced_braces(&caps[1]) {
        // println!("\"{}\" has imbalanced braces", &caps[1]);
        return format!("{{{}}}", &caps[1].replace("{", r"\{").replace("}", r"\}"));
    }
    let padded_cases = format!(",{},", &caps[1]);
    let quantifier = if padded_cases.contains(",,") { "?" } else { "" };
    let cases = caps[1].replace(",", "|");
    // println!(" 1 {}", cases);
    let escaped_comma_regex = Regex::new(r"(^|[^\\])\\\|").unwrap();
    let cases = escaped_comma_regex.replace(&cases, "$1,");
    // println!(" 2 {}", cases);
    format!("({}){}", cases, quantifier)
}

fn glob_match(pattern: &String, candidate: &String) -> bool {
    // Step 1. Escape the crap out of the existing pattern
    // println!("B: {}", pattern);
    let pattern = pattern.replace(".", r"\.");
    let unmatched_open_bracket_regex = Regex::new(r"\[([^\]]*)$").unwrap();
    let pattern = unmatched_open_bracket_regex.replace_all(&pattern, r"\[$1")
        .to_string();
    // Step 2. Convert sh globs to regexes
    let pattern = pattern.replace("?", ".");
    let bracketed_slash_regex = Regex::new(r"\[(.*/.*)\]").unwrap();
    let pattern = bracketed_slash_regex.replace_all(&pattern, r"\[$1\]");
    // Handling * and ** is weird but this actually works
    let pattern = pattern.replace("*", "[^/]*");
    let pattern = pattern.replace("[^/]*[^/]*", ".*");
    // If we had /**/, make the directory and leading / optional
    let pattern = pattern.replace("/.*/", "(/.*)?/");
    let pattern = pattern.replace("[!", "[^");
    // Handle single-option "alternation" manually earlier
    let fake_alternation_regex = Regex::new(r"\{([^,]+)\}").unwrap();
    let pattern = fake_alternation_regex.replace_all(&pattern, r"\{$1\}").to_string();
    let mut pattern = pattern;
    // Can use , or | between cases, no } directly after opening {, no backslash before final }
    let alternation_regex = Regex::new(r"\{(([^\}].*)?(,|\|)(.*[^\\])?)\}").unwrap();
    // Since nesting can be infinite, run until there is no more alternation
    while alternation_regex.is_match(&pattern) {
        // println!("{} matches {} at {}", pattern, alternation_regex, alternation_regex.captures(&pattern).unwrap().get(0).unwrap().as_str());
        pattern = alternation_regex.replace_all(&pattern, translate_alternation).to_string();
        // println!("D: {}", pattern);
    }
    let leading_slash_regex = Regex::new(r"^/").unwrap();
    let pattern = leading_slash_regex.replace(&pattern, "^");
    // Yes, this is a bit complex, but I don't want "\{" to become "\\{"
    let unescaped_brace_regex = Regex::new(r"(^|[^\\])(\{|\})").unwrap();
    let pattern = unescaped_brace_regex.replace_all(&pattern, r"$1\$2");
    // Run it again to catch overlaps ({{)
    let pattern = unescaped_brace_regex.replace_all(&pattern, r"$1\$2");
    let pattern = pattern.replace("||", "|");
    let pattern = pattern.replace("(|", "(");
    let pattern = pattern.replace("|)", ")");
    let pattern = format!("^(.*?/)?{}$", pattern);
    // println!("A: {} / {}", pattern, candidate);
    // Step 3. Actually do the testing
    let final_regex = Regex::new(&pattern).unwrap();
    return final_regex.is_match(candidate);
}

fn parse_config(target: &Path, conf_file: &Path) -> Result<OrderMap<String, String>, Box<Error>> {
    let context = conf_file.parent().unwrap();
    let ini_data = ini::Ini::load_from_file(conf_file)?;
    let mut result = OrderMap::new();
    if let Some(general) = ini_data.section::<String>(None) {
        if let Some(root) = general.get("root") {
            if root.to_lowercase() == "true" {
                result.insert("root".to_string(), "true".to_string());
            }
        }
    }
    let target = target.strip_prefix(&context)?;
    let target = target.as_os_str().to_os_string().into_string().unwrap();
    #[cfg(windows)]
    let target = target.replace("\\", "/");
    for (label, data) in ini_data.iter() {
        if let Some(ref label) = *label {
            if label.len() > 4096 {
                continue;
            }
            if glob_match(label, &target) {
                for (k, v) in data.iter() {
                    result.insert(k.clone(), v.clone());
                }
            }
        }
    }

    // Preprocessing may or may not actually be part of the spec
    // so I'm stealing this from editorconfig-core-py
    if let Some(indent_style) = result.clone().get("indent_style") {
        if indent_style == "tab" {
            if result.get("indent_size").is_none() {
                result.insert("indent_size".to_string(), "tab".to_string());
            }
        }
    }
    if let Some(indent_size) = result.clone().get("indent_size") {
        if indent_size != "tab" {
            if result.get("tab_width").is_none() {
                result.insert("tab_width".to_string(), indent_size.clone());
            }
        } else {
            if let Some(tab_width) = result.clone().get("tab_width") {
                result.insert("indent_size".to_string(), tab_width.clone());
            }
        }
    }
    Ok(result)
}

fn is_known_key(key: &str) -> bool {
    let known_keys = ["indent_style",
                      "indent_size",
                      "tab_width",
                      "end_of_line",
                      "charset",
                      "trim_trailing_whitespace",
                      "insert_final_newline"];
    known_keys.contains(&key)
}

/// Finds the configuration that applies to the file passed in `file_path`.
///
/// The `file_path` argument is the path to a file.
///
/// It looks for a file named `.editorconfig` in the file's directory and in every parent directory.
/// A search for `.editorconfig` files will stop if root (`/`) is reached or an `.editorconfig` file with `root=true` is found.
///
/// # Example
/// ```
/// use std::path::Path;
///
/// let path = Path::new("./test_files/simple/file.txt");
/// let path = path.canonicalize().unwrap();
/// let res = editorconfig::get_config(&path).unwrap();
/// for (k, v) in res.iter() {
///     println!("{}={}", *k, *v);
/// }
/// ```
/// # Errors
///  It returns an error:
///
/// - when it can't parse it
///
/// - when the `file_path` is malformed (check `std::fs::canonicalize` docs) or is a directory.
///
pub fn get_config(file_path: &Path) -> Result<OrderMap<String, String>, Box<Error>> {
    get_config_conffile(file_path, ".editorconfig")
}

/// Finds actual configuration that applies to file with given path.
/// # MAINLY USED FOR TESTING AND INTERNAL USE, CHECK `get_config`.
///
/// Looks for config data in given filename; in normal operation this will be ".editorconfig".
pub fn get_config_conffile(file_path: &Path,
                           conffile: &str)
                           -> Result<OrderMap<String, String>, Box<Error>> {
    let paths = crawl_paths(file_path, conffile)?;

    let mut result = OrderMap::new();
    for conf_path in paths {
        let options = parse_config(file_path, &conf_path)?;
        let old_result = result;
        result = OrderMap::new();
        for (k, v) in options.iter() {
            let k = k.to_lowercase();
            let v = if is_known_key(&k) {
                v.to_lowercase()
            } else {
                v.clone()
            };
            if k.len() > 50 || v.len() > 255 {
                continue;
            }
            if !result.contains_key(&k) && k != "root" {
                result.insert(k, v);
            }
        }
        for (k, v) in old_result.iter() {
            result.insert(k.clone(), v.clone());
        }
        if let Some(root) = options.get("root") {
            if root.to_lowercase() == "true" {
                break;
            }
        }
    }
    return Ok(result);
}

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

    #[test]
    fn works_with_multi_level_directories() {
        let path = Path::new("./test_files/multi_level/foo/bar/file.txt");
        let path = path.canonicalize().unwrap();
        let cfg = get_config(&path).unwrap();
        let mut map = OrderMap::new();
        map.insert("end_of_line".to_owned(), "lf".to_owned());
        map.insert("insert_final_newline".to_owned(), "true".to_owned());
        assert_eq!(cfg, map);
    }

    #[test]
    fn get_editorconfig_for_non_existing_file() {
        assert!(get_config(Path::new("./test_files/diocano")).is_ok());
    }
}
<commit_msg>Fix subdirectories in pattern and in filesystem<commit_after>//! A crate that implements [editorconfig](http://editorconfig.org/).
extern crate regex;

extern crate ordermap;

mod ini;
use regex::{Regex, Captures};

use ordermap::OrderMap;

use std::path::{Path, PathBuf};
use std::error::Error;

/// Finds all possible `conffile`s starting from `path` until root.
fn crawl_paths(path: &Path, conffile: &str) -> Result<Vec<PathBuf>, Box<Error>> {
    let mut path = if path.exists() {
        path.canonicalize()?
    } else {
        path.to_path_buf()
    };

    let mut result = vec![];
    while path.parent().is_some() {
        let mut adjacent_file = path.clone();
        adjacent_file.set_file_name(conffile);
        path.pop();
        if !adjacent_file.exists() {
            continue;
        }
        result.push(adjacent_file);
    }
    return Ok(result);
}

fn has_imbalanced_braces(text: &str) -> bool {
    let mut depth = 0i32;
    let escaped_brace_regex = Regex::new(r"\\(\{|\})").unwrap();
    let text = escaped_brace_regex.replace_all(text, "");
    for c in text.chars() {
        if c == '{' {
            depth += 1;
        } else if c == '}' {
            depth -= 1;
            if depth < 0 {
                return true;
            }
        }
    }
    return depth != 0;
}

fn translate_alternation(caps: &Captures) -> String {
    if has_imbalanced_braces(&caps[1]) {
        // println!("\"{}\" has imbalanced braces", &caps[1]);
        return format!("{{{}}}", &caps[1].replace("{", r"\{").replace("}", r"\}"));
    }
    let padded_cases = format!(",{},", &caps[1]);
    let quantifier = if padded_cases.contains(",,") { "?" } else { "" };
    let cases = caps[1].replace(",", "|");
    // println!(" 1 {}", cases);
    let escaped_comma_regex = Regex::new(r"(^|[^\\])\\\|").unwrap();
    let cases = escaped_comma_regex.replace(&cases, "$1,");
    // println!(" 2 {}", cases);
    format!("({}){}", cases, quantifier)
}

fn glob_match(pattern: &String, candidate: &String) -> bool {
    let orig_had_slash = pattern.contains('/');
    // Step 1. Escape the crap out of the existing pattern
    // println!("B: {}", pattern);
    let pattern = pattern.replace(".", r"\.");
    let unmatched_open_bracket_regex = Regex::new(r"\[([^\]]*)$").unwrap();
    let pattern = unmatched_open_bracket_regex.replace_all(&pattern, r"\[$1")
        .to_string();
    // Step 2. Convert sh globs to regexes
    let pattern = pattern.replace("?", ".");
    let bracketed_slash_regex = Regex::new(r"\[(.*/.*)\]").unwrap();
    let pattern = bracketed_slash_regex.replace_all(&pattern, r"\[$1\]");
    // Handling * and ** is weird but this actually works
    let pattern = pattern.replace("*", "[^/]*");
    let pattern = pattern.replace("[^/]*[^/]*", ".*");
    // If we had /**/, make the directory and leading / optional
    let pattern = pattern.replace("/.*/", "(/.*)?/");
    let pattern = pattern.replace("[!", "[^");
    // Handle single-option "alternation" manually earlier
    let fake_alternation_regex = Regex::new(r"\{([^,]+)\}").unwrap();
    let pattern = fake_alternation_regex.replace_all(&pattern, r"\{$1\}").to_string();
    let mut pattern = pattern;
    // Can use , or | between cases, no } directly after opening {, no backslash before final }
    let alternation_regex = Regex::new(r"\{(([^\}].*)?(,|\|)(.*[^\\])?)\}").unwrap();
    // Since nesting can be infinite, run until there is no more alternation
    while alternation_regex.is_match(&pattern) {
        // println!("{} matches {} at {}", pattern, alternation_regex, alternation_regex.captures(&pattern).unwrap().get(0).unwrap().as_str());
        pattern = alternation_regex.replace_all(&pattern, translate_alternation).to_string();
        // println!("D: {}", pattern);
    }
    let leading_slash_regex = Regex::new(r"^/").unwrap();
    let pattern = leading_slash_regex.replace(&pattern, "^");
    // Yes, this is a bit complex, but I don't want "\{" to become "\\{"
    let unescaped_brace_regex = Regex::new(r"(^|[^\\])(\{|\})").unwrap();
    let pattern = unescaped_brace_regex.replace_all(&pattern, r"$1\$2");
    // Run it again to catch overlaps ({{)
    let pattern = unescaped_brace_regex.replace_all(&pattern, r"$1\$2");
    let pattern = pattern.replace("||", "|");
    let pattern = pattern.replace("(|", "(");
    let pattern = pattern.replace("|)", ")");
    // Only allow subdirectories if no directory was specified to begin with
    let leading_expr = if orig_had_slash {
        ""
    } else {
        "(.*?/)?"
    };
    let pattern = format!("^{}{}$", leading_expr, pattern);
    // println!("A: {} / {}", pattern, candidate);
    // Step 3. Actually do the testing
    let final_regex = Regex::new(&pattern).unwrap();
    return final_regex.is_match(candidate);
}

fn parse_config(target: &Path, conf_file: &Path) -> Result<OrderMap<String, String>, Box<Error>> {
    let context = conf_file.parent().unwrap();
    let ini_data = ini::Ini::load_from_file(conf_file)?;
    let mut result = OrderMap::new();
    if let Some(general) = ini_data.section::<String>(None) {
        if let Some(root) = general.get("root") {
            if root.to_lowercase() == "true" {
                result.insert("root".to_string(), "true".to_string());
            }
        }
    }
    let target = target.strip_prefix(&context)?;
    let target = target.as_os_str().to_os_string().into_string().unwrap();
    #[cfg(windows)]
    let target = target.replace("\\", "/");
    for (label, data) in ini_data.iter() {
        if let Some(ref label) = *label {
            if label.len() > 4096 {
                continue;
            }
            if glob_match(label, &target) {
                for (k, v) in data.iter() {
                    result.insert(k.clone(), v.clone());
                }
            }
        }
    }

    // Preprocessing may or may not actually be part of the spec
    // so I'm stealing this from editorconfig-core-py
    if let Some(indent_style) = result.clone().get("indent_style") {
        if indent_style == "tab" {
            if result.get("indent_size").is_none() {
                result.insert("indent_size".to_string(), "tab".to_string());
            }
        }
    }
    if let Some(indent_size) = result.clone().get("indent_size") {
        if indent_size != "tab" {
            if result.get("tab_width").is_none() {
                result.insert("tab_width".to_string(), indent_size.clone());
            }
        } else {
            if let Some(tab_width) = result.clone().get("tab_width") {
                result.insert("indent_size".to_string(), tab_width.clone());
            }
        }
    }
    Ok(result)
}

fn is_known_key(key: &str) -> bool {
    let known_keys = ["indent_style",
                      "indent_size",
                      "tab_width",
                      "end_of_line",
                      "charset",
                      "trim_trailing_whitespace",
                      "insert_final_newline"];
    known_keys.contains(&key)
}

/// Finds the configuration that applies to the file passed in `file_path`.
///
/// The `file_path` argument is the path to a file.
///
/// It looks for a file named `.editorconfig` in the file's directory and in every parent directory.
/// A search for `.editorconfig` files will stop if root (`/`) is reached or an `.editorconfig` file with `root=true` is found.
///
/// # Example
/// ```
/// use std::path::Path;
///
/// let path = Path::new("./test_files/simple/file.txt");
/// let path = path.canonicalize().unwrap();
/// let res = editorconfig::get_config(&path).unwrap();
/// for (k, v) in res.iter() {
///     println!("{}={}", *k, *v);
/// }
/// ```
/// # Errors
///  It returns an error:
///
/// - when it can't parse it
///
/// - when the `file_path` is malformed (check `std::fs::canonicalize` docs) or is a directory.
///
pub fn get_config(file_path: &Path) -> Result<OrderMap<String, String>, Box<Error>> {
    get_config_conffile(file_path, ".editorconfig")
}

/// Finds actual configuration that applies to file with given path.
/// # MAINLY USED FOR TESTING AND INTERNAL USE, CHECK `get_config`.
///
/// Looks for config data in given filename; in normal operation this will be ".editorconfig".
pub fn get_config_conffile(file_path: &Path,
                           conffile: &str)
                           -> Result<OrderMap<String, String>, Box<Error>> {
    let paths = crawl_paths(file_path, conffile)?;

    let mut result = OrderMap::new();
    for conf_path in paths {
        let options = parse_config(file_path, &conf_path)?;
        let old_result = result;
        result = OrderMap::new();
        for (k, v) in options.iter() {
            let k = k.to_lowercase();
            let v = if is_known_key(&k) {
                v.to_lowercase()
            } else {
                v.clone()
            };
            if k.len() > 50 || v.len() > 255 {
                continue;
            }
            if !result.contains_key(&k) && k != "root" {
                result.insert(k, v);
            }
        }
        for (k, v) in old_result.iter() {
            result.insert(k.clone(), v.clone());
        }
        if let Some(root) = options.get("root") {
            if root.to_lowercase() == "true" {
                break;
            }
        }
    }
    return Ok(result);
}

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

    #[test]
    fn works_with_multi_level_directories() {
        let path = Path::new("./test_files/multi_level/foo/bar/file.txt");
        let path = path.canonicalize().unwrap();
        let cfg = get_config(&path).unwrap();
        let mut map = OrderMap::new();
        map.insert("end_of_line".to_owned(), "lf".to_owned());
        map.insert("insert_final_newline".to_owned(), "true".to_owned());
        assert_eq!(cfg, map);
    }

    #[test]
    fn get_editorconfig_for_non_existing_file() {
        assert!(get_config(Path::new("./test_files/diocano")).is_ok());
    }
}
<|endoftext|>


<commit_before>#![deny(missing_docs, dead_code)]
//! Marid
//!
//! A process orchestration library
#[macro_use]
extern crate chan;
extern crate chan_signal;
extern crate crossbeam;

mod traits;
pub use traits::{Signal, Sender, Receiver, Process, Runner};

mod composer;
pub use composer::Composer;

mod process;
pub use process::{MaridProcess, ProcessError};

use std::error::Error;
/// Error type for Marid Runners.
pub type MaridError = Box<Error + Send>;

/// This function will start the specified runner as well as listen on the specified
/// signals.
///
/// # Warnings
///
/// This must be called before any threads are spawned in the process to
/// ensure appropriate signal handling behavior.
pub fn launch<R>(runner: R, signals: Vec<Signal>) -> MaridProcess
where R: Runner + Send + 'static {
    let (signal_send, signal_recv) = chan::sync(1024);
    for sig in signals {
        chan_signal::notify_on(&signal_send, sig);
    }

    MaridProcess::new(Box::new(runner), signal_send, signal_recv)
}

// TODO: Make this module more useable and document behavior.
pub mod test_helpers;
<commit_msg>More info on crate documentation<commit_after>#![deny(missing_docs, dead_code)]
//! Marid
//!
//! A process orchestration library.
//!
//! This library is influenced by [tedsuo's ifrit](https://github.com/tedsuo/ifrit), a similar
//! library for Golang.
//!
//! The foundation of the library is built on the idea of a `Runner` trait, which
//! encapsulates a singular unit of work, e.g. a thread, which is has a long lifetime, potential
//! forever. The `Process` is a trait that defines the actual running of one or more `Runner`
//! objects. Importantly, a `Process` defines the ability to setup, wait for, and signal a
//! `Runner`.
#[macro_use]
extern crate chan;
extern crate chan_signal;
extern crate crossbeam;

mod traits;
pub use traits::{Signal, Sender, Receiver, Process, Runner};

mod composer;
pub use composer::Composer;

mod process;
pub use process::{MaridProcess, ProcessError};

use std::error::Error;
/// Error type for Marid Runners.
pub type MaridError = Box<Error + Send>;

/// This function will start the specified runner as well as listen on the specified
/// signals.
///
/// # Warnings
///
/// This must be called before any threads are spawned in the process to
/// ensure appropriate signal handling behavior.
pub fn launch<R>(runner: R, signals: Vec<Signal>) -> MaridProcess
where R: Runner + Send + 'static {
    let (signal_send, signal_recv) = chan::sync(1024);
    for sig in signals {
        chan_signal::notify_on(&signal_send, sig);
    }

    MaridProcess::new(Box::new(runner), signal_send, signal_recv)
}

// TODO: Make this module more useable and document behavior.
pub mod test_helpers;
<|endoftext|>


<commit_before>// Copyright 2015-2017 Intecture Developers. See the COPYRIGHT file at the
// top-level directory of this distribution and at
// https://intecture.io/COPYRIGHT.
//
// Licensed under the Mozilla Public License 2.0 <LICENSE or
// https://www.tldrlegal.com/l/mpl-2.0>. This file may not be copied,
// modified, or distributed except according to those terms.

//! # Intecture API
//!
//! The Intecture API is the interface between your code and your managed
//! hosts. The library is organised into a set of 'primitives', which
//! are the building blocks used to configure your systems.
//! Developers should implement these primitives in their source code
//! in order to configure and maintain their managed hosts.
//!
//! ## Communication
//!
//! The API communicates with the Agent service on your managed hosts
//! via a ZeroMQ "REQ" (REQuest) socket. No other socket types are
//! supported by the Intecture Agent.
//!
//! **Note:** The Agent service must be running when you call any
//! Intecture API primitives, or the program will hang while it
//! attempts to connect to a non-existent socket.

#[cfg(feature = "remote-run")]
extern crate czmq;
#[macro_use]
extern crate lazy_static;
extern crate libc;
extern crate mustache;
extern crate regex;
extern crate rustc_serialize;
extern crate serde;
#[macro_use]
extern crate serde_json;
#[cfg(test)]
extern crate tempdir;
extern crate tempfile;
extern crate zdaemon;
extern crate zfilexfer;
extern crate hostname;

#[macro_use]
mod ffi_helpers;
pub mod command;
mod project;
pub mod directory;
pub mod error;
pub mod file;
#[macro_use]
mod host;
#[cfg(all(test, feature = "remote-run"))]
mod mock_env;
pub mod package;
#[cfg(feature = "remote-run")]
mod payload;
pub mod service;
mod target;
pub mod template;

pub use command::{Command, CommandResult};
pub use directory::{Directory, DirectoryOpts};
pub use error::Error;
pub use file::{File, FileOwner};
pub use host::Host;
pub use host::data::open as data_open;
pub use host::ffi::{host_data, get_value, get_value_keys, get_value_type};
#[cfg(feature = "local-run")]
pub use host::ffi::host_local;
#[cfg(feature = "remote-run")]
pub use host::ffi::{host_connect, host_connect_endpoint, host_connect_payload, host_close};
pub use mustache::{MapBuilder, VecBuilder};
pub use package::Package;
pub use package::providers::{Provider, ProviderFactory, Providers};
#[cfg(feature = "remote-run")]
pub use payload::Payload;
#[cfg(feature = "remote-run")]
pub use payload::config::Config as PayloadConfig;
#[cfg(feature = "remote-run")]
pub use payload::ffi::{payload_new, payload_build, payload_run, payload_free};
pub use project::{Language, ProjectConfig};
pub use serde_json::Value;
pub use service::{Service, ServiceRunnable};
pub use template::Template;
pub use zfilexfer::FileOptions;

#[cfg(feature = "remote-run")]
use zdaemon::ConfigFile;

#[cfg(all(test, feature = "remote-run"))]
lazy_static! {
    static ref _MOCK_ENV: mock_env::MockEnv = mock_env::MockEnv::new();
}

#[cfg(feature = "remote-run")]
lazy_static! {
    static ref PROJECT_CONFIG: project::ProjectConfig = project::ProjectConfig::load("project.json")
                                                                               .expect("Could not load project.json");
}
<commit_msg>Stop modules from rudely exposing their guts publicly: intecture/api#5<commit_after>// Copyright 2015-2017 Intecture Developers. See the COPYRIGHT file at the
// top-level directory of this distribution and at
// https://intecture.io/COPYRIGHT.
//
// Licensed under the Mozilla Public License 2.0 <LICENSE or
// https://www.tldrlegal.com/l/mpl-2.0>. This file may not be copied,
// modified, or distributed except according to those terms.

//! # Intecture API
//!
//! The Intecture API is the interface between your code and your managed
//! hosts. The library is organised into a set of 'primitives', which
//! are the building blocks used to configure your systems.
//! Developers should implement these primitives in their source code
//! in order to configure and maintain their managed hosts.
//!
//! ## Communication
//!
//! The API communicates with the Agent service on your managed hosts
//! via a ZeroMQ "REQ" (REQuest) socket. No other socket types are
//! supported by the Intecture Agent.
//!
//! **Note:** The Agent service must be running when you call any
//! Intecture API primitives, or the program will hang while it
//! attempts to connect to a non-existent socket.

#[cfg(feature = "remote-run")]
extern crate czmq;
#[macro_use]
extern crate lazy_static;
extern crate libc;
extern crate mustache;
extern crate regex;
extern crate rustc_serialize;
extern crate serde;
#[macro_use]
extern crate serde_json;
#[cfg(test)]
extern crate tempdir;
extern crate tempfile;
extern crate zdaemon;
extern crate zfilexfer;
extern crate hostname;

#[macro_use]
mod ffi_helpers;
mod command;
mod project;
mod directory;
mod error;
mod file;
#[macro_use]
mod host;
#[cfg(all(test, feature = "remote-run"))]
mod mock_env;
mod package;
#[cfg(feature = "remote-run")]
mod payload;
mod service;
mod target;
mod template;

pub use command::{Command, CommandResult, ffi as command_ffi};
pub use directory::{Directory, DirectoryOpts, ffi as directory_ffi};
pub use error::{Error, geterr};
pub use file::{File, FileOwner, ffi as file_ffi};
pub use host::{Host, ffi as host_ffi};
pub use host::data::open as data_open;
pub use mustache::{MapBuilder, VecBuilder};
pub use package::{Package, ffi as package_ffi};
pub use package::providers::Providers;
#[cfg(feature = "remote-run")]
pub use payload::{Payload, ffi as payload_ffi};
#[cfg(feature = "remote-run")]
#[doc(hidden)]
pub use payload::config::Config as PayloadConfig;
#[doc(hidden)]
pub use project::{Language, ProjectConfig};
pub use serde_json::Value;
pub use service::{Service, ServiceRunnable, ffi as service_ffi};
pub use template::{Template, ffi as template_ffi};
pub use zfilexfer::FileOptions;

#[cfg(feature = "remote-run")]
use zdaemon::ConfigFile;

#[cfg(all(test, feature = "remote-run"))]
lazy_static! {
    static ref _MOCK_ENV: mock_env::MockEnv = mock_env::MockEnv::new();
}

#[cfg(feature = "remote-run")]
lazy_static! {
    static ref PROJECT_CONFIG: project::ProjectConfig = project::ProjectConfig::load("project.json")
                                                                               .expect("Could not load project.json");
}
<|endoftext|>


<commit_before>// Copyright 2017 Dmitry Tantsur <divius.inside@gmail.com>
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//! Session structure definition.
//!
//! The Session object serves as a wrapper around an HTTP(s) client, handling
//! authentication, accessing the service catalog and token refresh.

use std::cell::RefCell;

use hyper::{Client, Url};
use hyper::client::{Body, IntoUrl, RequestBuilder, Response};
use hyper::header::{Header, Headers, HeaderFormat};
use hyper::method::Method;

use super::ApiError;
use super::auth::base::{AuthMethod, AuthToken, AuthTokenHeader};
use super::utils;

/// Request builder with authentication.
///
/// Essentially copies the interface of hyper::client::RequestBuilder.
#[allow(missing_debug_implementations)]
pub struct AuthenticatedRequestBuilder<'a, A: AuthMethod + 'a> {
    parent: &'a Session<A>,
    inner: RequestBuilder<'a>
}

/// An OpenStack API session.
///
/// Owns a token and an underlying client.
#[derive(Debug)]
pub struct Session<A: AuthMethod> {
    auth_method: A,
    client: Client,
    cached_token: RefCell<Option<AuthToken>>
}

impl<'a, A: AuthMethod> AuthenticatedRequestBuilder<'a, A> {
    /// Send this request.
    pub fn send(self) -> Result<Response, ApiError> {
        let token_value = try!(self.parent.token_value());
        let hdr = AuthTokenHeader(token_value);
        self.inner.header(hdr).send().map_err(From::from)
    }

    /// Add body to the request.
    pub fn body<B: Into<Body<'a>>>(self, body: B)
            -> AuthenticatedRequestBuilder<'a, A> {
        AuthenticatedRequestBuilder {
            inner: self.inner.body(body),
            .. self
        }
    }

    /// Add additional headers to the request.
    pub fn headers(self, headers: Headers)
            -> AuthenticatedRequestBuilder<'a, A> {
        AuthenticatedRequestBuilder {
            inner: self.inner.headers(headers),
            .. self
        }
    }

    /// Add an individual header to the request.
    ///
    /// Note that X-Auth-Token is always overwritten with a token in use.
    pub fn header<H: Header + HeaderFormat>(self, header: H)
            -> AuthenticatedRequestBuilder<'a, A> {
        AuthenticatedRequestBuilder {
            inner: self.inner.header(header),
            .. self
        }
    }
}

impl<'a, A: AuthMethod + 'a> Session<A> {
    /// Create a new session with a given authentication plugin.
    pub fn new(auth_method: A) -> Session<A> {
        Session {
            auth_method: auth_method,
            client: utils::http_client(),
            cached_token: RefCell::new(None)
        }
    }

    /// Get a clone of the authentication token.
    pub fn auth_token(&self) -> Result<AuthToken, ApiError> {
        try!(self.refresh_token());
        Ok(self.cached_token.borrow().clone().unwrap())
    }

    /// Get an endpoint URL.
    pub fn get_endpoint(&self, service_type: &str,
                        endpoint_interface: Option<&str>,
                        region: Option<&str>) -> Result<Url, ApiError> {
        self.auth_method.get_endpoint(service_type, endpoint_interface,
                                      region, &self)
    }

    /// A wrapper for HTTP request.
    pub fn request<U: IntoUrl>(&'a self, method: Method, url: U)
            -> AuthenticatedRequestBuilder<'a, A> {
        AuthenticatedRequestBuilder {
            parent: self,
            inner: self.client.request(method, url)
        }
    }

    // Private and test-only

    #[cfg(test)]
    pub fn new_with_params(auth_method: A, client: Client,
                       token: AuthToken) -> Session<A> {
        Session {
            auth_method: auth_method,
            client: client,
            cached_token: RefCell::new(Some(token))
        }
    }

    fn refresh_token(&self) -> Result<(), ApiError> {
        let mut cached_token = self.cached_token.borrow_mut();
        if cached_token.is_some() {
            return Ok(())
        }

        // TODO: check expires_at

        let new_token = try!(self.auth_method.get_token(&self.client));
        *cached_token = Some(new_token);
        Ok(())
    }

    fn token_value(&self) -> Result<String, ApiError> {
        try!(self.refresh_token());
        Ok(self.cached_token.borrow().clone().unwrap().token)
    }
}

#[cfg(test)]
pub mod test {
    use super::super::auth::base::{AuthToken, NoAuth};
    use super::super::utils;

    use super::Session;

    pub fn new_session(token: &str) -> Session<NoAuth> {
        let token = AuthToken {
            token: String::from(token),
            expires_at: None
        };

        Session::new_with_params(NoAuth::new("http://127.0.0.1/").unwrap(),
                                 utils::http_client(), token)
    }

    #[test]
    fn test_session_new() {
        let s = new_session("foo");
        assert_eq!(&s.token_value().unwrap(), "foo");
    }
}
<commit_msg>Write more tests for Session<commit_after>// Copyright 2017 Dmitry Tantsur <divius.inside@gmail.com>
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//! Session structure definition.
//!
//! The Session object serves as a wrapper around an HTTP(s) client, handling
//! authentication, accessing the service catalog and token refresh.

use std::cell::RefCell;

use hyper::{Client, Url};
use hyper::client::{Body, IntoUrl, RequestBuilder, Response};
use hyper::header::{Header, Headers, HeaderFormat};
use hyper::method::Method;

use super::ApiError;
use super::auth::base::{AuthMethod, AuthToken, AuthTokenHeader};
use super::utils;

/// Request builder with authentication.
///
/// Essentially copies the interface of hyper::client::RequestBuilder.
#[allow(missing_debug_implementations)]
pub struct AuthenticatedRequestBuilder<'a, A: AuthMethod + 'a> {
    parent: &'a Session<A>,
    inner: RequestBuilder<'a>
}

/// An OpenStack API session.
///
/// Owns a token and an underlying client.
#[derive(Debug)]
pub struct Session<A: AuthMethod> {
    auth_method: A,
    client: Client,
    cached_token: RefCell<Option<AuthToken>>
}

impl<'a, A: AuthMethod> AuthenticatedRequestBuilder<'a, A> {
    /// Send this request.
    pub fn send(self) -> Result<Response, ApiError> {
        let token_value = try!(self.parent.token_value());
        let hdr = AuthTokenHeader(token_value);
        self.inner.header(hdr).send().map_err(From::from)
    }

    /// Add body to the request.
    pub fn body<B: Into<Body<'a>>>(self, body: B)
            -> AuthenticatedRequestBuilder<'a, A> {
        AuthenticatedRequestBuilder {
            inner: self.inner.body(body),
            .. self
        }
    }

    /// Add additional headers to the request.
    pub fn headers(self, headers: Headers)
            -> AuthenticatedRequestBuilder<'a, A> {
        AuthenticatedRequestBuilder {
            inner: self.inner.headers(headers),
            .. self
        }
    }

    /// Add an individual header to the request.
    ///
    /// Note that X-Auth-Token is always overwritten with a token in use.
    pub fn header<H: Header + HeaderFormat>(self, header: H)
            -> AuthenticatedRequestBuilder<'a, A> {
        AuthenticatedRequestBuilder {
            inner: self.inner.header(header),
            .. self
        }
    }
}

impl<'a, A: AuthMethod + 'a> Session<A> {
    /// Create a new session with a given authentication plugin.
    pub fn new(auth_method: A) -> Session<A> {
        Session {
            auth_method: auth_method,
            client: utils::http_client(),
            cached_token: RefCell::new(None)
        }
    }

    /// Get a clone of the authentication token.
    pub fn auth_token(&self) -> Result<AuthToken, ApiError> {
        try!(self.refresh_token());
        Ok(self.cached_token.borrow().clone().unwrap())
    }

    /// Get an endpoint URL.
    pub fn get_endpoint(&self, service_type: &str,
                        endpoint_interface: Option<&str>,
                        region: Option<&str>) -> Result<Url, ApiError> {
        self.auth_method.get_endpoint(service_type, endpoint_interface,
                                      region, &self)
    }

    /// A wrapper for HTTP request.
    pub fn request<U: IntoUrl>(&'a self, method: Method, url: U)
            -> AuthenticatedRequestBuilder<'a, A> {
        AuthenticatedRequestBuilder {
            parent: self,
            inner: self.client.request(method, url)
        }
    }

    // Private and test-only

    #[cfg(test)]
    pub fn new_with_params(auth_method: A, client: Client,
                       token: AuthToken) -> Session<A> {
        Session {
            auth_method: auth_method,
            client: client,
            cached_token: RefCell::new(Some(token))
        }
    }

    fn refresh_token(&self) -> Result<(), ApiError> {
        let mut cached_token = self.cached_token.borrow_mut();
        if cached_token.is_some() {
            return Ok(())
        }

        // TODO: check expires_at

        let new_token = try!(self.auth_method.get_token(&self.client));
        *cached_token = Some(new_token);
        Ok(())
    }

    fn token_value(&self) -> Result<String, ApiError> {
        try!(self.refresh_token());
        Ok(self.cached_token.borrow().clone().unwrap().token)
    }
}

#[cfg(test)]
pub mod test {
    #![allow(missing_debug_implementations)]

    use std::cell::RefCell;
    use std::io::Read;

    use hyper;
    use hyper::header::ContentLength;

    use super::Session;
    use super::super::auth::base::{AuthToken, NoAuth};
    use super::super::utils;

    pub fn new_session(token: &str) -> Session<NoAuth> {
        let token = AuthToken {
            token: String::from(token),
            expires_at: None
        };

        Session::new_with_params(NoAuth::new("http://127.0.0.1/").unwrap(),
                                 utils::http_client(), token)
    }

    mock_connector!(MockHttp {
        "http://127.0.0.1" => "HTTP/1.1 200 OK\r\n\
                               Server: Mock.Mock\r\n\
                               \r\n\
                               {}"
    });

    #[test]
    fn test_session_new() {
        let s = new_session("foo");
        assert_eq!(&s.token_value().unwrap(), "foo");
        let token = s.auth_token().unwrap();
        assert_eq!(&token.token, "foo");
        assert!(token.expires_at.is_none());
    }

    #[test]
    fn test_session_get_endpoint() {
        let s = new_session("foo");
        let e = s.get_endpoint("foo", None, None).unwrap();
        assert_eq!(&e.to_string(), "http://127.0.0.1/");
    }

    #[test]
    fn test_session_request() {
        let cli = hyper::Client::with_connector(MockHttp::default());
        let s = Session {
            auth_method: NoAuth::new("http://127.0.0.1/").unwrap(),
            client: cli,
            cached_token: RefCell::new(None)
        };

        let mut resp = s.request(hyper::Post, "http://127.0.0.1/")
            .body("body").header(ContentLength(4u64)).send().unwrap();

        let mut s = String::new();
        resp.read_to_string(&mut s).unwrap();
        assert_eq!(&s, "{}");
    }
}
<|endoftext|>


<commit_before>// Copyright 2014 Christopher Schröder, Johannes Köster.
// Licensed under the MIT license (http://opensource.org/licenses/MIT)
// This file may not be copied, modified, or distributed
// except according to those terms.

//! Rust-HTSlib provides a high level BAM API.
//! Reading and writing BAM files is as easy as
//!
//! ```
//! use rust_htslib::bam;
//! use rust_htslib::bam::Read;
//! use std::path::Path;
//!
//! let bam = bam::Reader::from_path(&Path::new("test/test.bam")).ok().expect("Error opening bam.");
//! let mut out = bam::Writer::from_path_with_template(&Path::new("test/test.bam"), &Path::new("test/out.bam")).ok().expect("Error opening bam.");
//!
//! // copy reverse reads to new BAM file
//! for r in bam.records() {
//!     let record = r.ok().expect("Error reading BAM file.");
//!     if record.is_reverse() {
//!         out.write(&record).ok().expect("Error writing BAM file.");
//!     }
//! }
//! ```
//!
//! Pileups can be performed with
//!
//! ```
//! use rust_htslib::bam;
//! use rust_htslib::bam::Read;
//! use std::path::Path;
//!
//! let bam = bam::Reader::from_path(&Path::new("test/test.bam")).ok().expect("Error opening bam.");
//!
//! // pileup over all covered sites
//! for p in bam.pileup() {
//!     let pileup = p.ok().expect("Error reading BAM file.");
//!     println!("{}:{} depth {}", pileup.tid(), pileup.pos(), pileup.depth());
//!
//!     for alignment in pileup.alignments() {
//!         match alignment.indel() {
//!             bam::pileup::Indel::Ins(len) => println!("Insertion of length {}", len),
//!             bam::pileup::Indel::Del(len) => println!("Deletion of length {}", len),
//!             _ => println!("Base {}", alignment.record().seq()[alignment.qpos()])
//!         }
//!     }
//! }
//! ```
//!
//! In both cases, indexed BAM files can be seeked for specific regions, constraining either the record iterator or the pileups:
//!
//! ```
//! use rust_htslib::bam;
//! use std::path::Path;
//!
//! let mut bam = bam::IndexedReader::from_path(&Path::new("test/test.bam")).ok().expect("Error opening indexed BAM.");
//!
//! // seek to chr1:50000-100000
//! let tid = bam.header.tid(b"CHROMOSOME_I").unwrap();
//! bam.seek(tid, 0, 20).ok().expect("Error seeking BAM file.");
//! // afterwards, read or pileup in this region
//! ```

extern crate libc;
extern crate itertools;
#[macro_use]
extern crate quick_error;
#[macro_use]
extern crate newtype_derive;
#[macro_use]
extern crate custom_derive;
extern crate url;

pub mod htslib;
pub mod bam;
pub mod bcf;
pub mod utils;
<commit_msg>Replace expect with unwrap.<commit_after>// Copyright 2014 Christopher Schröder, Johannes Köster.
// Licensed under the MIT license (http://opensource.org/licenses/MIT)
// This file may not be copied, modified, or distributed
// except according to those terms.

//! Rust-HTSlib provides a high level BAM API.
//! Reading and writing BAM files is as easy as
//!
//! ```
//! use rust_htslib::bam;
//! use rust_htslib::bam::Read;
//! use std::path::Path;
//!
//! let bam = bam::Reader::from_path(&Path::new("test/test.bam")).unwrap();
//! let mut out = bam::Writer::from_path_with_template(&Path::new("test/test.bam"), &Path::new("test/out.bam")).unwrap();
//!
//! // copy reverse reads to new BAM file
//! for r in bam.records() {
//!     let record = r.unwrap();
//!     if record.is_reverse() {
//!         out.write(&record).unwrap();
//!     }
//! }
//! ```
//!
//! Pileups can be performed with
//!
//! ```
//! use rust_htslib::bam;
//! use rust_htslib::bam::Read;
//! use std::path::Path;
//!
//! let bam = bam::Reader::from_path(&Path::new("test/test.bam")).unwrap();
//!
//! // pileup over all covered sites
//! for p in bam.pileup() {
//!     let pileup = p.unwrap();
//!     println!("{}:{} depth {}", pileup.tid(), pileup.pos(), pileup.depth());
//!
//!     for alignment in pileup.alignments() {
//!         match alignment.indel() {
//!             bam::pileup::Indel::Ins(len) => println!("Insertion of length {}", len),
//!             bam::pileup::Indel::Del(len) => println!("Deletion of length {}", len),
//!             _ => println!("Base {}", alignment.record().seq()[alignment.qpos()])
//!         }
//!     }
//! }
//! ```
//!
//! In both cases, indexed BAM files can be seeked for specific regions, constraining either the record iterator or the pileups:
//!
//! ```
//! use rust_htslib::bam;
//! use std::path::Path;
//!
//! let mut bam = bam::IndexedReader::from_path(&Path::new("test/test.bam")).unwrap();
//!
//! // seek to chr1:50000-100000
//! let tid = bam.header.tid(b"CHROMOSOME_I").unwrap();
//! bam.seek(tid, 0, 20).unwrap();
//! // afterwards, read or pileup in this region
//! ```

extern crate libc;
extern crate itertools;
#[macro_use]
extern crate quick_error;
#[macro_use]
extern crate newtype_derive;
#[macro_use]
extern crate custom_derive;
extern crate url;

pub mod htslib;
pub mod bam;
pub mod bcf;
pub mod utils;
<|endoftext|>