<commit_before>use std::num::{One, Zero};

use super::super::matrix::*;
use super::super::util::{alloc_dirty_vec, hypot};

pub struct QRDecomposition<T> {
  qr : Matrix<T>,
  rdiag : ~[T]
}

// Initial version based on JAMA.
// QR Decomposition.
//
// For an m-by-n matrix A, the QR decomposition is an m-by-m orthogonal matrix Q
// and an m-by-n upper triangular (or trapezoidal) matrix R, so that A = Q*R.
//
// The QR decompostion always exists, even if the matrix does not have
// full rank.  The primary use of the QR decomposition is in the least
// squares solution of nonsquare systems of simultaneous linear equations.
// This will fail if is_full_rank() returns false.
impl<T : Add<T, T> + Sub<T, T> + Mul<T, T> + Div<T, T> + Neg<T> + Eq + Ord + ApproxEq<T> + One + Zero + Clone + Algebraic + Orderable + Signed> QRDecomposition<T> {
  pub fn new(m : &Matrix<T>) -> QRDecomposition<T> {
    // NOTE: Currently does not work for m < n.
    assert!(m.noRows >= m.noCols);

    let mut qrdata = m.data.clone();
    let n = m.noCols;
    let m = m.noRows;
    let mut rdiag = alloc_dirty_vec(n);

    for k in range(0u, n) {
      // Compute 2-norm of k-th column without under/overflow.
      let mut nrm : T = Zero::zero();
      for i in range(k, m) {
        nrm = hypot(nrm, qrdata[i * n + k].clone());
      }

      if(nrm != Zero::zero()) {
        // Form k-th Householder vector.
        if(qrdata[k * n + k] < Zero::zero()) {
          nrm = - nrm;
        }
        for i in range(k, m) {
          qrdata[i * n + k] = qrdata[i * n + k] / nrm;
        }
        qrdata[k * n + k] = qrdata[k * n + k] + One::one();

        // Apply transformation to remaining columns.
        for j in range(k + 1, n) {
          let mut s : T = Zero::zero();
          for i in range(k, m) {
            s = s + qrdata[i * n + k] * qrdata[i * n + j];
          }
          s = - s / qrdata[k * n + k];
          for i in range(k, m) {
            qrdata[i * n + j] = qrdata[i * n + j] + s * qrdata[i * n + k];
          }
        }
      }

      rdiag[k] = - nrm;
    }

    QRDecomposition { qr : Matrix { noRows : m, noCols : n, data : qrdata }, rdiag : rdiag }
  }

  pub fn is_full_rank(&self) -> bool {
    for j in range(0u, self.qr.noCols) {
      if(self.rdiag[j] == Zero::zero()) {
        return false;
      }
    }
    return true;
  }

  // Return the Householder vectors
  // Lower trapezoidal matrix whose columns define the reflections
  pub fn get_h(&self) -> Matrix<T> {
    let mut hdata = alloc_dirty_vec(self.qr.noRows * self.qr.noCols);

    let m = self.qr.noRows;
    let n = self.qr.noCols;

    for i in range(0u, m) {
      for j in range(0u, n) {
        hdata[i * self.qr.noCols + j] = if(i >= j) { self.qr.data[i * self.qr.noCols + j].clone() } else { Zero::zero() }
      }
    }

    Matrix { noRows : self.qr.noRows, noCols : self.qr.noCols, data : hdata }
  }

  // Return the upper triangular factor
  pub fn get_r(&self) -> Matrix<T> {
    let n = self.qr.noCols;
    let mut rdata = alloc_dirty_vec(n * n);

    for i in range(0u, n) {
      for j in range(0u, n) {
        if(i < j) {
          rdata[i * n + j] = self.qr.data[i * self.qr.noCols + j].clone();
        } else if(i == j) {
          rdata[i * n + j] = self.rdiag[i].clone();
        } else {
          rdata[i * n + j] = Zero::zero();
        }
      }
    }

    Matrix { noRows : n, noCols : n, data : rdata }
  }

  // Generate and return the (economy-sized) orthogonal factor
  pub fn get_q(&self) -> Matrix<T> {
    let n = self.qr.noCols;
    let m = self.qr.noRows;
    let mut qdata = alloc_dirty_vec(m * n);

    for k in range(0u, n).invert() {
      for i in range(0u, m) {
        qdata[i * n + k] = Zero::zero();
      }
      qdata[k * n + k] = One::one();
      for j in range(k, n) {
        if(self.qr.data[k * n + k] != Zero::zero()) {
          let mut s : T = Zero::zero();
          for i in range(k, m) {
            s = s + self.qr.data[i * n + k] * qdata[i * n + j];
          }
          s = - s / self.qr.data[k * n + k];
          for i in range(k, m) {
            qdata[i * n + j] = qdata[i * n + j] + s * self.qr.data[i * n + k];
          }
        }
      }
    }

    Matrix { noRows : m, noCols : n, data : qdata }
  }

  // Least squares solution of A*X = B
  // B : A Matrix with as many rows as A and any number of columns.
  // returns X that minimizes the two norm of Q*R*X-B.
  pub fn solve(&self, b : &Matrix<T>) -> Option<Matrix<T>> {
    assert!(b.noRows == self.qr.noRows);
    if(!self.is_full_rank()) {
      return None
    }

    let nx = b.noCols;
    let mut xdata = b.data.clone();

    let n = self.qr.noCols;
    let m = self.qr.noRows;

    // Compute Y = transpose(Q)*B
    for k in range(0u, n) {
      for j in range(0u, nx) {
        let mut s : T = Zero::zero();
        for i in range(k, m) {
          s = s + self.qr.data[i * self.qr.noCols + k] * xdata[i * nx + j];
        }
        s = - s / self.qr.data[k * self.qr.noCols + k];
        for i in range(k, m) {
          xdata[i * nx + j] = xdata[i * nx + j] + s * self.qr.data[i * self.qr.noCols + k];
        }
      }
    }

    // Solve R*X = Y;
    for k in range(0u, n).invert() {
      for j in range(0u, nx) {
        xdata[k * nx + j] = xdata[k * nx + j] / self.rdiag[k];
      }
      for i in range(0u, k) {
        for j in range(0u, nx) {
          xdata[i * nx + j] = xdata[i * nx + j] - xdata[k * nx + j] * self.qr.data[i * self.qr.noCols + k];
        }
      }
    }

    Some(Matrix { noRows : self.qr.noCols, noCols : b.noCols, data : xdata })
  }
}

#[test]
fn qr_test() {
  let a = matrix(3, 3, ~[12.0, -51.0, 4.0, 6.0, 167.0, -68.0, -4.0, 24.0, -41.0]);
  let qr = QRDecomposition::new(&a);
  assert!((qr.get_q() * qr.get_r()).approx_eq(&a));
}

#[test]
fn qr_test__m_over_n() {
  let a = matrix(3, 2, ~[1.0, 2.0, 3.0, 4.0, 5.0, 6.0]);
  let qr = QRDecomposition::new(&a);
  assert!((qr.get_q() * qr.get_r()).approx_eq(&a));
}

/*
// FIXME: Add support for n over m case.
#[test]
fn qr_test__n_over_m() {
  let a = matrix(2, 3, ~[1.0, 2.0, 3.0, 4.0, 5.0, 6.0]);
  let qr = QRDecomposition::new(&a);
  assert!((qr.get_q() * qr.get_r()).approx_eq(&a));
}
*/
<commit_msg>Fix QR decomposition for m < n case.<commit_after>use std::num::{One, Zero};

use super::super::matrix::*;
use super::super::util::{alloc_dirty_vec, hypot};

pub struct QRDecomposition<T> {
  qr : Matrix<T>,
  rdiag : ~[T]
}

// Initial version based on JAMA.
// QR Decomposition.
//
// For an m-by-n matrix A, the QR decomposition is an m-by-m orthogonal matrix Q
// and an m-by-n upper triangular (or trapezoidal) matrix R, so that A = Q*R.
//
// The QR decompostion always exists, even if the matrix does not have
// full rank.  The primary use of the QR decomposition is in the least
// squares solution of nonsquare systems of simultaneous linear equations.
// This will fail if is_full_rank() returns false.
impl<T : Add<T, T> + Sub<T, T> + Mul<T, T> + Div<T, T> + Neg<T> + Eq + Ord + ApproxEq<T> + One + Zero + Clone + Algebraic + Orderable + Signed> QRDecomposition<T> {
  pub fn new(m : &Matrix<T>) -> QRDecomposition<T> {
    let mut qrdata = m.data.clone();
    let n = m.noCols;
    let m = m.noRows;
    let mut rdiag = alloc_dirty_vec(n);

    for k in range(0u, n) {
      // Zero out elements below the diagonal for the k:th column of m.

      // Compute 2-norm of k-th column without under/overflow.
      let mut nrm : T = Zero::zero();
      for i in range(k, m) {
        nrm = hypot(nrm, qrdata[i * n + k].clone());
      }

      // If the length of the column vector is zero, the column is already zero and there's nothing to do.
      if(nrm != Zero::zero()) {
        // Form k-th Householder vector.
        if(qrdata[k * n + k] < Zero::zero()) {
          nrm = - nrm;
        }
        for i in range(k, m) {
          qrdata[i * n + k] = qrdata[i * n + k] / nrm;
        }
        qrdata[k * n + k] = qrdata[k * n + k] + One::one();

        // Apply transformation to remaining columns.
        for j in range(k + 1, n) {
          let mut s : T = Zero::zero();
          for i in range(k, m) {
            s = s + qrdata[i * n + k] * qrdata[i * n + j];
          }
          s = - s / qrdata[k * n + k];
          for i in range(k, m) {
            qrdata[i * n + j] = qrdata[i * n + j] + s * qrdata[i * n + k];
          }
        }
      }

      rdiag[k] = - nrm;
    }

    QRDecomposition { qr : Matrix { noRows : m, noCols : n, data : qrdata }, rdiag : rdiag }
  }

  pub fn is_full_rank(&self) -> bool {
    for j in range(0u, self.qr.noCols) {
      if(self.rdiag[j] == Zero::zero()) {
        return false;
      }
    }
    return true;
  }

  // Return the Householder vectors
  // Lower trapezoidal matrix whose columns define the reflections
  pub fn get_h(&self) -> Matrix<T> {
    let mut hdata = alloc_dirty_vec(self.qr.noRows * self.qr.noCols);

    let m = self.qr.noRows;
    let n = self.qr.noCols;

    for i in range(0u, m) {
      for j in range(0u, n) {
        hdata[i * self.qr.noCols + j] = if(i >= j) { self.qr.data[i * self.qr.noCols + j].clone() } else { Zero::zero() }
      }
    }

    Matrix { noRows : self.qr.noRows, noCols : self.qr.noCols, data : hdata }
  }

  // Return the upper triangular factor
  pub fn get_r(&self) -> Matrix<T> {
    let m = self.qr.noRows;
    let n = self.qr.noCols;
    let mut rdata = alloc_dirty_vec(m * n);

    for i in range(0u, m) {
      for j in range(0u, n) {
        rdata[i * n + j] = if(i < j) { self.qr.data[i * n + j].clone() }
                           else if(i == j) { self.rdiag[i].clone() }
                           else { Zero::zero() };
      }
    }

    Matrix { noRows : m, noCols : n, data : rdata }
  }

  // Generate and return the (economy-sized) orthogonal factor
  pub fn get_q(&self) -> Matrix<T> {
    let n = self.qr.noCols;
    let m = self.qr.noRows;
    let mut qdata = alloc_dirty_vec(m * m);

    // Iterate over all columns of Q, from (m-1) to 0.
    for k in range(0u, m).invert() {
      // Zero out k:th column of Q.
      for i in range(0u, m) {
        qdata[i * m + k] = Zero::zero();
      }

      // Set the diagonal element of k:th column to one.
      qdata[k * m + k] = One::one();

      // Iterate over columns k .. (m - 1) of Q.
      for j in range(k, m) {
        if((k < n) && (self.qr.data[k * n + k] != Zero::zero())) {
          let mut s : T = Zero::zero();
          for i in range(k, m) {
            s = s + self.qr.data[i * n + k] * qdata[i * m + j];
          }
          s = - s / self.qr.data[k * n + k];
          for i in range(k, m) {
            qdata[i * m + j] = qdata[i * m + j] + s * self.qr.data[i * n + k];
          }
        }
      }
    }

    Matrix { noRows : m, noCols : m, data : qdata }
  }

  // Least squares solution of A*X = B
  // B : A Matrix with as many rows as A and any number of columns.
  // returns X that minimizes the two norm of Q*R*X-B.
  pub fn solve(&self, b : &Matrix<T>) -> Option<Matrix<T>> {
    assert!(b.noRows == self.qr.noRows);
    if(!self.is_full_rank()) {
      return None
    }

    let nx = b.noCols;
    let mut xdata = b.data.clone();

    let n = self.qr.noCols;
    let m = self.qr.noRows;

    // Compute Y = transpose(Q)*B
    for k in range(0u, n) {
      for j in range(0u, nx) {
        let mut s : T = Zero::zero();
        for i in range(k, m) {
          s = s + self.qr.data[i * self.qr.noCols + k] * xdata[i * nx + j];
        }
        s = - s / self.qr.data[k * self.qr.noCols + k];
        for i in range(k, m) {
          xdata[i * nx + j] = xdata[i * nx + j] + s * self.qr.data[i * self.qr.noCols + k];
        }
      }
    }

    // Solve R*X = Y;
    for k in range(0u, n).invert() {
      for j in range(0u, nx) {
        xdata[k * nx + j] = xdata[k * nx + j] / self.rdiag[k];
      }
      for i in range(0u, k) {
        for j in range(0u, nx) {
          xdata[i * nx + j] = xdata[i * nx + j] - xdata[k * nx + j] * self.qr.data[i * self.qr.noCols + k];
        }
      }
    }

    Some(Matrix { noRows : self.qr.noCols, noCols : b.noCols, data : xdata })
  }
}

#[test]
fn qr_test() {
  let a = matrix(3, 3, ~[12.0, -51.0, 4.0, 6.0, 167.0, -68.0, -4.0, 24.0, -41.0]);
  let qr = QRDecomposition::new(&a);
  assert!((qr.get_q() * qr.get_r()).approx_eq(&a));
}

#[test]
fn qr_test__m_over_n() {
  let a = matrix(3, 2, ~[1.0, 2.0, 3.0, 4.0, 5.0, 6.0]);
  let qr = QRDecomposition::new(&a);
  assert!((qr.get_q() * qr.get_r()).approx_eq(&a));
}

#[test]
fn qr_test__n_over_m() {
  let a = matrix(2, 3, ~[1.0, 2.0, 3.0, 4.0, 5.0, 6.0]);
  let qr = QRDecomposition::new(&a);
  assert!((qr.get_q() * qr.get_r()).approx_eq(&a));
}
<|endoftext|>


<commit_before>use std::ops::{Deref, DerefMut};

use serde_urlencoded;
use serde::de::{self, Deserializer, DeserializeOwned, Visitor, Error as DeError};
use futures::future::{Future, FutureResult, result};

use error::Error;
use httprequest::HttpRequest;

pub trait HttpRequestExtractor<T, S>: Sized where T: DeserializeOwned, S: 'static
{
    type Result: Future<Item=Self, Error=Error>;

    fn extract(req: &HttpRequest<S>) -> Self::Result;
}

/// Extract typed information from the request's path.
///
/// `S` - application state type
///
/// ## Example
///
/// ```rust
/// # extern crate bytes;
/// # extern crate actix_web;
/// # extern crate futures;
/// #[macro_use] extern crate serde_derive;
/// use actix_web::*;
/// use actix_web::Path;
///
/// #[derive(Deserialize)]
/// struct Info {
///     username: String,
/// }
///
/// /// extract path info using serde
/// fn index(info: Path<Info>) -> Result<String> {
///     Ok(format!("Welcome {}!", info.username))
/// }
///
/// fn main() {
///     let app = Application::new().resource(
///        "/{username}/index.html",                // <- define path parameters
///        |r| r.method(Method::GET).with(index));  // <- use `with` extractor
/// }
/// ```
pub struct Path<T, S=()>{
    item: T,
    req: HttpRequest<S>,
}

impl<T, S> Deref for Path<T, S> {
    type Target = T;

    fn deref(&self) -> &T {
        &self.item
    }
}

impl<T, S> DerefMut for Path<T, S> {
    fn deref_mut(&mut self) -> &mut T {
        &mut self.item
    }
}

impl<T, S> Path<T, S> {

    /// Shared application state
    #[inline]
    pub fn state(&self) -> &S {
        self.req.state()
    }

    /// Incoming request
    #[inline]
    pub fn request(&self) -> &HttpRequest<S> {
        &self.req
    }

    /// Deconstruct instance into a parts
    pub fn into(self) -> (T, HttpRequest<S>) {
        (self.item, self.req)
    }

}

impl<T, S> HttpRequestExtractor<T, S> for Path<T, S>
    where T: DeserializeOwned, S: 'static
{
    type Result = FutureResult<Self, Error>;

    #[inline]
    fn extract(req: &HttpRequest<S>) -> Self::Result {
        let req = req.clone();
        result(de::Deserialize::deserialize(PathExtractor{req: &req})
               .map_err(|e| e.into())
               .map(|item| Path{item, req}))
    }
}

/// Extract typed information from from the request's query.
///
/// `S` - application state type
///
/// ## Example
///
/// ```rust
/// # extern crate bytes;
/// # extern crate actix_web;
/// # extern crate futures;
/// #[macro_use] extern crate serde_derive;
/// use actix_web::*;
/// use actix_web::Query;
///
/// #[derive(Deserialize)]
/// struct Info {
///     username: String,
/// }
///
/// // use `with` extractor for query info
/// // this handler get called only if request's query contains `username` field
/// fn index(info: Query<Info>) -> Result<String> {
///     Ok(format!("Welcome {}!", info.username))
/// }
///
/// fn main() {
///     let app = Application::new().resource(
///        "/index.html",
///        |r| r.method(Method::GET).with(index)); // <- use `with` extractor
/// }
/// ```
pub struct Query<T, S=()>{
    item: T,
    req: HttpRequest<S>,
}

impl<T, S> Deref for Query<T, S> {
    type Target = T;

    fn deref(&self) -> &T {
        &self.item
    }
}

impl<T, S> DerefMut for Query<T, S> {
    fn deref_mut(&mut self) -> &mut T {
        &mut self.item
    }
}

impl<T, S> Query<T, S> {

    /// Shared application state
    #[inline]
    pub fn state(&self) -> &S {
        self.req.state()
    }

    /// Incoming request
    #[inline]
    pub fn request(&self) -> &HttpRequest<S> {
        &self.req
    }

    /// Deconstruct instance into a parts
    pub fn into(self) -> (T, HttpRequest<S>) {
        (self.item, self.req)
    }
}

impl<T, S> HttpRequestExtractor<T, S> for Query<T, S>
    where T: de::DeserializeOwned, S: 'static
{
    type Result = FutureResult<Self, Error>;

    #[inline]
    fn extract(req: &HttpRequest<S>) -> Self::Result {
        let req = req.clone();
        result(serde_urlencoded::from_str::<T>(req.query_string())
               .map_err(|e| e.into())
               .map(|item| Query{ item, req}))
    }
}

macro_rules! unsupported_type {
    ($trait_fn:ident, $name:expr) => {
        fn $trait_fn<V>(self, _: V) -> Result<V::Value, Self::Error>
            where V: Visitor<'de>
        {
            Err(de::value::Error::custom(concat!("unsupported type: ", $name)))
        }
    };
}

pub struct PathExtractor<'de, S: 'de> {
    req: &'de HttpRequest<S>
}

impl<'de, S: 'de> Deserializer<'de> for PathExtractor<'de, S>
{
    type Error = de::value::Error;

    fn deserialize_map<V>(self, visitor: V) -> Result<V::Value, Self::Error>
        where V: Visitor<'de>,
    {
        visitor.visit_map(de::value::MapDeserializer::new(
            self.req.match_info().iter().map(|&(ref k, ref v)| (k.as_ref(), v.as_ref()))))
    }

    fn deserialize_struct<V>(self, _: &'static str, _: &'static [&'static str], visitor: V)
                             -> Result<V::Value, Self::Error>
        where V: Visitor<'de>,
    {
        self.deserialize_map(visitor)
    }

    fn deserialize_unit<V>(self, visitor: V) -> Result<V::Value, Self::Error>
        where
        V: Visitor<'de>,
    {
        visitor.visit_unit()
    }

    fn deserialize_unit_struct<V>(self, _: &'static str, visitor: V)
                                  -> Result<V::Value, Self::Error>
        where V: Visitor<'de>
    {
        self.deserialize_unit(visitor)
    }

    fn deserialize_newtype_struct<V>(self, _: &'static str, visitor: V)
                                     -> Result<V::Value, Self::Error>
        where V: Visitor<'de>,
    {
        visitor.visit_newtype_struct(self)
    }

    fn deserialize_tuple<V>(self, len: usize, visitor: V) -> Result<V::Value, Self::Error>
        where V: Visitor<'de>
    {
        if self.req.match_info().len() < len {
            Err(de::value::Error::custom(
                format!("wrong number of parameters: {} expected {}",
                        self.req.match_info().len(), len).as_str()))
        } else {
            visitor.visit_seq(de::value::SeqDeserializer::new(
                self.req.match_info().iter().map(|&(_, ref v)| v.as_ref())))
        }
    }

    fn deserialize_tuple_struct<V>(self, _: &'static str, _: usize, visitor: V)
                                   -> Result<V::Value, Self::Error>
        where V: Visitor<'de>
    {
        visitor.visit_seq(de::value::SeqDeserializer::new(
            self.req.match_info().iter().map(|&(_, ref v)| v.as_ref())))
    }

    fn deserialize_enum<V>(self, _: &'static str, _: &'static [&'static str], _: V)
                           -> Result<V::Value, Self::Error>
        where V: Visitor<'de>
    {
        Err(de::value::Error::custom("unsupported type: enum"))
    }

    unsupported_type!(deserialize_any, "'any'");
    unsupported_type!(deserialize_bool, "bool");
    unsupported_type!(deserialize_i8, "i8");
    unsupported_type!(deserialize_i16, "i16");
    unsupported_type!(deserialize_i32, "i32");
    unsupported_type!(deserialize_i64, "i64");
    unsupported_type!(deserialize_u8, "u8");
    unsupported_type!(deserialize_u16, "u16");
    unsupported_type!(deserialize_u32, "u32");
    unsupported_type!(deserialize_u64, "u64");
    unsupported_type!(deserialize_f32, "f32");
    unsupported_type!(deserialize_f64, "f64");
    unsupported_type!(deserialize_char, "char");
    unsupported_type!(deserialize_str, "str");
    unsupported_type!(deserialize_string, "String");
    unsupported_type!(deserialize_bytes, "bytes");
    unsupported_type!(deserialize_byte_buf, "byte buf");
    unsupported_type!(deserialize_option, "Option<T>");
    unsupported_type!(deserialize_seq, "sequence");
    unsupported_type!(deserialize_identifier, "identifier");
    unsupported_type!(deserialize_ignored_any, "ignored_any");
}

#[cfg(test)]
mod tests {
    use router::{Router, Pattern};
    use resource::Resource;
    use test::TestRequest;
    use server::ServerSettings;

    #[derive(Deserialize)]
    struct MyStruct {
        key: String,
        value: String,
    }

    #[derive(Deserialize)]
    struct Id {
        id: String,
    }

    #[test]
    fn test_request_extract() {
        let mut req = TestRequest::with_uri("/name/user1/?id=test").finish();

        let mut resource = Resource::<()>::default();
        resource.name("index");
        let mut routes = Vec::new();
        routes.push((Pattern::new("index", "/{key}/{value}/"), Some(resource)));
        let (router, _) = Router::new("", ServerSettings::default(), routes);
        assert!(router.recognize(&mut req).is_some());

        let s: MyStruct = req.extract_path().unwrap();
        assert_eq!(s.key, "name");
        assert_eq!(s.value, "user1");

        let s: (String, String) = req.extract_path().unwrap();
        assert_eq!(s.0, "name");
        assert_eq!(s.1, "user1");

        let s: Id = req.extract_query().unwrap();
        assert_eq!(s.id, "test");
    }
}
<commit_msg>update Path and Query doc strings<commit_after>use std::ops::{Deref, DerefMut};

use serde_urlencoded;
use serde::de::{self, Deserializer, DeserializeOwned, Visitor, Error as DeError};
use futures::future::{Future, FutureResult, result};

use error::Error;
use httprequest::HttpRequest;

pub trait HttpRequestExtractor<T, S>: Sized where T: DeserializeOwned, S: 'static
{
    type Result: Future<Item=Self, Error=Error>;

    fn extract(req: &HttpRequest<S>) -> Self::Result;
}

/// Extract typed information from the request's path.
///
/// `S` - application state type
///
/// ## Example
///
/// ```rust
/// # extern crate bytes;
/// # extern crate actix_web;
/// # extern crate futures;
/// #[macro_use] extern crate serde_derive;
/// use actix_web::*;
/// use actix_web::Path;
///
/// /// Application state
/// struct State {}
///
/// #[derive(Deserialize)]
/// struct Info {
///     username: String,
/// }
///
/// /// extract path info using serde
/// fn index(info: Path<Info, State>) -> Result<String> {
///     Ok(format!("Welcome {}!", info.username))
/// }
///
/// fn main() {
///     let app = Application::with_state(State{}).resource(
///        "/{username}/index.html",                // <- define path parameters
///        |r| r.method(Method::GET).with(index));  // <- use `with` extractor
/// }
/// ```
pub struct Path<T, S=()>{
    item: T,
    req: HttpRequest<S>,
}

impl<T, S> Deref for Path<T, S> {
    type Target = T;

    fn deref(&self) -> &T {
        &self.item
    }
}

impl<T, S> DerefMut for Path<T, S> {
    fn deref_mut(&mut self) -> &mut T {
        &mut self.item
    }
}

impl<T, S> Path<T, S> {

    /// Shared application state
    #[inline]
    pub fn state(&self) -> &S {
        self.req.state()
    }

    /// Incoming request
    #[inline]
    pub fn request(&self) -> &HttpRequest<S> {
        &self.req
    }

    /// Deconstruct instance into a parts
    pub fn into(self) -> (T, HttpRequest<S>) {
        (self.item, self.req)
    }

}

impl<T, S> HttpRequestExtractor<T, S> for Path<T, S>
    where T: DeserializeOwned, S: 'static
{
    type Result = FutureResult<Self, Error>;

    #[inline]
    fn extract(req: &HttpRequest<S>) -> Self::Result {
        let req = req.clone();
        result(de::Deserialize::deserialize(PathExtractor{req: &req})
               .map_err(|e| e.into())
               .map(|item| Path{item, req}))
    }
}

/// Extract typed information from from the request's query.
///
/// `S` - application state type
///
/// ## Example
///
/// ```rust
/// # extern crate bytes;
/// # extern crate actix_web;
/// # extern crate futures;
/// #[macro_use] extern crate serde_derive;
/// use actix_web::*;
/// use actix_web::Query;
///
/// /// Application state
/// struct State {}
///
/// #[derive(Deserialize)]
/// struct Info {
///     username: String,
/// }
///
/// // use `with` extractor for query info
/// // this handler get called only if request's query contains `username` field
/// fn index(info: Query<Info, State>) -> Result<String> {
///     Ok(format!("Welcome {}!", info.username))
/// }
///
/// fn main() {
///     let app = Application::with_state(State{}).resource(
///        "/index.html",
///        |r| r.method(Method::GET).with(index)); // <- use `with` extractor
/// }
/// ```
pub struct Query<T, S=()>{
    item: T,
    req: HttpRequest<S>,
}

impl<T, S> Deref for Query<T, S> {
    type Target = T;

    fn deref(&self) -> &T {
        &self.item
    }
}

impl<T, S> DerefMut for Query<T, S> {
    fn deref_mut(&mut self) -> &mut T {
        &mut self.item
    }
}

impl<T, S> Query<T, S> {

    /// Shared application state
    #[inline]
    pub fn state(&self) -> &S {
        self.req.state()
    }

    /// Incoming request
    #[inline]
    pub fn request(&self) -> &HttpRequest<S> {
        &self.req
    }

    /// Deconstruct instance into a parts
    pub fn into(self) -> (T, HttpRequest<S>) {
        (self.item, self.req)
    }
}

impl<T, S> HttpRequestExtractor<T, S> for Query<T, S>
    where T: de::DeserializeOwned, S: 'static
{
    type Result = FutureResult<Self, Error>;

    #[inline]
    fn extract(req: &HttpRequest<S>) -> Self::Result {
        let req = req.clone();
        result(serde_urlencoded::from_str::<T>(req.query_string())
               .map_err(|e| e.into())
               .map(|item| Query{ item, req}))
    }
}

macro_rules! unsupported_type {
    ($trait_fn:ident, $name:expr) => {
        fn $trait_fn<V>(self, _: V) -> Result<V::Value, Self::Error>
            where V: Visitor<'de>
        {
            Err(de::value::Error::custom(concat!("unsupported type: ", $name)))
        }
    };
}

pub struct PathExtractor<'de, S: 'de> {
    req: &'de HttpRequest<S>
}

impl<'de, S: 'de> Deserializer<'de> for PathExtractor<'de, S>
{
    type Error = de::value::Error;

    fn deserialize_map<V>(self, visitor: V) -> Result<V::Value, Self::Error>
        where V: Visitor<'de>,
    {
        visitor.visit_map(de::value::MapDeserializer::new(
            self.req.match_info().iter().map(|&(ref k, ref v)| (k.as_ref(), v.as_ref()))))
    }

    fn deserialize_struct<V>(self, _: &'static str, _: &'static [&'static str], visitor: V)
                             -> Result<V::Value, Self::Error>
        where V: Visitor<'de>,
    {
        self.deserialize_map(visitor)
    }

    fn deserialize_unit<V>(self, visitor: V) -> Result<V::Value, Self::Error>
        where
        V: Visitor<'de>,
    {
        visitor.visit_unit()
    }

    fn deserialize_unit_struct<V>(self, _: &'static str, visitor: V)
                                  -> Result<V::Value, Self::Error>
        where V: Visitor<'de>
    {
        self.deserialize_unit(visitor)
    }

    fn deserialize_newtype_struct<V>(self, _: &'static str, visitor: V)
                                     -> Result<V::Value, Self::Error>
        where V: Visitor<'de>,
    {
        visitor.visit_newtype_struct(self)
    }

    fn deserialize_tuple<V>(self, len: usize, visitor: V) -> Result<V::Value, Self::Error>
        where V: Visitor<'de>
    {
        if self.req.match_info().len() < len {
            Err(de::value::Error::custom(
                format!("wrong number of parameters: {} expected {}",
                        self.req.match_info().len(), len).as_str()))
        } else {
            visitor.visit_seq(de::value::SeqDeserializer::new(
                self.req.match_info().iter().map(|&(_, ref v)| v.as_ref())))
        }
    }

    fn deserialize_tuple_struct<V>(self, _: &'static str, _: usize, visitor: V)
                                   -> Result<V::Value, Self::Error>
        where V: Visitor<'de>
    {
        visitor.visit_seq(de::value::SeqDeserializer::new(
            self.req.match_info().iter().map(|&(_, ref v)| v.as_ref())))
    }

    fn deserialize_enum<V>(self, _: &'static str, _: &'static [&'static str], _: V)
                           -> Result<V::Value, Self::Error>
        where V: Visitor<'de>
    {
        Err(de::value::Error::custom("unsupported type: enum"))
    }

    unsupported_type!(deserialize_any, "'any'");
    unsupported_type!(deserialize_bool, "bool");
    unsupported_type!(deserialize_i8, "i8");
    unsupported_type!(deserialize_i16, "i16");
    unsupported_type!(deserialize_i32, "i32");
    unsupported_type!(deserialize_i64, "i64");
    unsupported_type!(deserialize_u8, "u8");
    unsupported_type!(deserialize_u16, "u16");
    unsupported_type!(deserialize_u32, "u32");
    unsupported_type!(deserialize_u64, "u64");
    unsupported_type!(deserialize_f32, "f32");
    unsupported_type!(deserialize_f64, "f64");
    unsupported_type!(deserialize_char, "char");
    unsupported_type!(deserialize_str, "str");
    unsupported_type!(deserialize_string, "String");
    unsupported_type!(deserialize_bytes, "bytes");
    unsupported_type!(deserialize_byte_buf, "byte buf");
    unsupported_type!(deserialize_option, "Option<T>");
    unsupported_type!(deserialize_seq, "sequence");
    unsupported_type!(deserialize_identifier, "identifier");
    unsupported_type!(deserialize_ignored_any, "ignored_any");
}

#[cfg(test)]
mod tests {
    use router::{Router, Pattern};
    use resource::Resource;
    use test::TestRequest;
    use server::ServerSettings;

    #[derive(Deserialize)]
    struct MyStruct {
        key: String,
        value: String,
    }

    #[derive(Deserialize)]
    struct Id {
        id: String,
    }

    #[test]
    fn test_request_extract() {
        let mut req = TestRequest::with_uri("/name/user1/?id=test").finish();

        let mut resource = Resource::<()>::default();
        resource.name("index");
        let mut routes = Vec::new();
        routes.push((Pattern::new("index", "/{key}/{value}/"), Some(resource)));
        let (router, _) = Router::new("", ServerSettings::default(), routes);
        assert!(router.recognize(&mut req).is_some());

        let s: MyStruct = req.extract_path().unwrap();
        assert_eq!(s.key, "name");
        assert_eq!(s.value, "user1");

        let s: (String, String) = req.extract_path().unwrap();
        assert_eq!(s.0, "name");
        assert_eq!(s.1, "user1");

        let s: Id = req.extract_query().unwrap();
        assert_eq!(s.id, "test");
    }
}
<|endoftext|>


<commit_before>#![crate_name = "fold"]

/*
 * This file is part of the uutils coreutils package.
 *
 * (c) Arcterus <arcterus@mail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

#![feature(macro_rules)]

extern crate getopts;
extern crate libc;

use std::io;
use std::io::fs::File;
use std::io::BufferedReader;
use std::uint;

#[path = "../common/util.rs"]
mod util;

static NAME: &'static str = "fold";
static VERSION: &'static str = "1.0.0";

pub fn uumain(args: Vec<String>) -> int {
    let (args, obs_width) = handle_obsolete(args.as_slice());
    let program = args[0].clone();

    let opts = [
        getopts::optflag("b", "bytes", "count using bytes rather than columns (meaning control characters such as newline are not treated specially)"),
        getopts::optflag("s", "spaces", "break lines at word boundaries rather than a hard cut-off"),
        getopts::optopt("w", "width", "set WIDTH as the maximum line width rather than 80", "WIDTH"),
        getopts::optflag("h", "help", "display this help and exit"),
        getopts::optflag("V", "version", "output version information and exit")
    ];

    let matches = match getopts::getopts(args.tail(), opts) {
        Ok(m) => m,
        Err(f) => crash!(1, "{}", f)
    };

    if matches.opt_present("h") {
        println!("{} v{}", NAME, VERSION);
        println!("");
        println!("Usage:");
        println!("  {} [OPTION]... [FILE]...", program);
        println!("");
        print!("{}", getopts::usage("Writes each file (or standard input if no files are given) to standard output whilst breaking long lines", opts));
    } else if matches.opt_present("V") {
        println!("{} v{}", NAME, VERSION);
    } else {
        let bytes = matches.opt_present("b");
        let spaces = matches.opt_present("s");
        let poss_width =
            if matches.opt_present("w") {
                matches.opt_str("w")
            } else {
                match obs_width {
                    Some(v) => Some(v.to_string()),
                    None => None,
                }
            };
        let width = match poss_width {
            Some(inp_width) => match uint::parse_bytes(inp_width.as_bytes(), 10) {
                Some(width) => width,
                None => crash!(1, "illegal width value (\"{}\")", inp_width)
            },
            None => 80
        };
        let files = if matches.free.is_empty() {
            vec!("-".to_string())
        } else {
            matches.free
        };
        fold(files, bytes, spaces, width);
    }

    0
}

fn handle_obsolete(args: &[String]) -> (Vec<String>, Option<String>) {
    let mut args = args.to_vec();
    let mut i = 0;
    while i < args.len() {
        if args[i].as_slice().char_at(0) == '-' && args[i].len() > 1 && args[i].as_slice().char_at(1).is_digit() {
            return (args.clone(),
                    Some(args.remove(i).unwrap().as_slice().slice_from(1).to_string()));
        }
        i += 1;
    }
    (args, None)
}

fn fold(filenames: Vec<String>, bytes: bool, spaces: bool, width: uint) {
    for filename in filenames.iter() {
        let filename: &str = filename.as_slice();
        let mut stdin_buf;
        let mut file_buf;
        let buffer = BufferedReader::new(
            if filename == "-" {
                stdin_buf = io::stdio::stdin_raw();
                &mut stdin_buf as &mut Reader
            } else {
                file_buf = safe_unwrap!(File::open(&Path::new(filename)));
                &mut file_buf as &mut Reader
            }
        );
        fold_file(buffer, bytes, spaces, width);
    }
}

fn fold_file<T: io::Reader>(file: BufferedReader<T>, bytes: bool, spaces: bool, width: uint) {
    let mut file = file;
    for line in file.lines() {
        let line_string = safe_unwrap!(line);
        let mut line = line_string.as_slice();
        let len = line.len();
        if line.char_at(len - 1) == '\n' {
            if len == 1 {
                println!("");
                continue;
            } else {
                line = line.slice_to(len - 1);
            }
        }
        if bytes {
            let mut i = 0;
            while i < line.len() {
                let width = if line.len() - i >= width { width } else { line.len() - i };
                let slice = {
                    let slice = line.slice(i, i + width);
                    if spaces && i + width < line.len() {
                        match slice.rfind(|ch: char| ch.is_whitespace()) {
                            Some(m) => slice.slice_to(m + 1),
                            None => slice
                        }
                    } else {
                        slice
                    }
                };
                println!("{}", slice);
                i += slice.len();
            }
        } else {
            let mut output = String::new();
            let mut count = 0;
            for (i, ch) in line.chars().enumerate() {
                if count >= width {
                    let (val, ncount) = {
                        let slice = output.as_slice();
                        let (out, val, ncount) =
                            if spaces && i + 1 < line.len() {
                                match slice.rfind(|ch: char| ch.is_whitespace()) {
                                    Some(m) => {
                                        let routput = slice.slice_from(m + 1).to_string();
                                        let ncount = routput.as_slice().chars().fold(0u, |out, ch: char| {
                                            out + match ch {
                                                '\t' => 8,
                                                '\x08' => if out > 0 { -1 } else { 0 },
                                                '\r' => return 0,
                                                _ => 1
                                            }
                                        });
                                        (slice.slice_to(m + 1), routput, ncount)
                                    },
                                    None => (slice, "".to_string(), 0)
                                }
                            } else {
                                (slice, "".to_string(), 0)
                            };
                        println!("{}", out);
                        (val, ncount)
                    };
                    output = val.into_string();
                    count = ncount;
                }
                match ch {
                    '\t' => {
                        count += 8;
                        if count > width {
                            println!("{}", output);
                            output.truncate(0);
                            count = 8;
                        }
                    }
                    '\x08' => {
                        if count > 0 {
                            count -= 1;
                            let len = output.len() - 1;
                            output.truncate(len);
                        }
                        continue;
                    }
                    '\r' => {
                        output.truncate(0);
                        count = 0;
                        continue;
                    }
                    _ => count += 1
                };
                output.push(ch);
            }
            if count > 0 {
                print!("{}", output);
            }
        }
    }
}
<commit_msg>Fix fold for Busybox tests (other than unicode)<commit_after>#![crate_name = "fold"]

/*
 * This file is part of the uutils coreutils package.
 *
 * (c) Arcterus <arcterus@mail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

#![feature(macro_rules)]

extern crate getopts;
extern crate libc;

use std::io;
use std::io::fs::File;
use std::io::BufferedReader;
use std::uint;

#[path = "../common/util.rs"]
mod util;

static NAME: &'static str = "fold";
static VERSION: &'static str = "1.0.0";

pub fn uumain(args: Vec<String>) -> int {
    let (args, obs_width) = handle_obsolete(args.as_slice());
    let program = args[0].clone();

    let opts = [
        getopts::optflag("b", "bytes", "count using bytes rather than columns (meaning control characters such as newline are not treated specially)"),
        getopts::optflag("s", "spaces", "break lines at word boundaries rather than a hard cut-off"),
        getopts::optopt("w", "width", "set WIDTH as the maximum line width rather than 80", "WIDTH"),
        getopts::optflag("h", "help", "display this help and exit"),
        getopts::optflag("V", "version", "output version information and exit")
    ];

    let matches = match getopts::getopts(args.tail(), opts) {
        Ok(m) => m,
        Err(f) => crash!(1, "{}", f)
    };

    if matches.opt_present("h") {
        println!("{} v{}", NAME, VERSION);
        println!("");
        println!("Usage:");
        println!("  {} [OPTION]... [FILE]...", program);
        println!("");
        print!("{}", getopts::usage("Writes each file (or standard input if no files are given) to standard output whilst breaking long lines", opts));
    } else if matches.opt_present("V") {
        println!("{} v{}", NAME, VERSION);
    } else {
        let bytes = matches.opt_present("b");
        let spaces = matches.opt_present("s");
        let poss_width =
            if matches.opt_present("w") {
                matches.opt_str("w")
            } else {
                match obs_width {
                    Some(v) => Some(v.to_string()),
                    None => None,
                }
            };
        let width = match poss_width {
            Some(inp_width) => match uint::parse_bytes(inp_width.as_bytes(), 10) {
                Some(width) => width,
                None => crash!(1, "illegal width value (\"{}\")", inp_width)
            },
            None => 80
        };
        let files = if matches.free.is_empty() {
            vec!("-".to_string())
        } else {
            matches.free
        };
        fold(files, bytes, spaces, width);
    }

    0
}

fn handle_obsolete(args: &[String]) -> (Vec<String>, Option<String>) {
    let mut args = args.to_vec();
    let mut i = 0;
    while i < args.len() {
        if args[i].as_slice().char_at(0) == '-' && args[i].len() > 1 && args[i].as_slice().char_at(1).is_digit() {
            return (args.clone(),
                    Some(args.remove(i).unwrap().as_slice().slice_from(1).to_string()));
        }
        i += 1;
    }
    (args, None)
}

#[inline]
fn fold(filenames: Vec<String>, bytes: bool, spaces: bool, width: uint) {
    for filename in filenames.iter() {
        let filename: &str = filename.as_slice();
        let mut stdin_buf;
        let mut file_buf;
        let buffer = BufferedReader::new(
            if filename == "-" {
                stdin_buf = io::stdio::stdin_raw();
                &mut stdin_buf as &mut Reader
            } else {
                file_buf = safe_unwrap!(File::open(&Path::new(filename)));
                &mut file_buf as &mut Reader
            }
        );
        fold_file(buffer, bytes, spaces, width);
    }
}

#[inline]
fn fold_file<T: io::Reader>(file: BufferedReader<T>, bytes: bool, spaces: bool, width: uint) {
    let mut file = file;
    for line in file.lines() {
        let line_string = safe_unwrap!(line);
        let mut line = line_string.as_slice();
        if bytes {
            let len = line.len();
            let mut i = 0;
            while i < len {
                let width = if len - i >= width { width } else { len - i };
                let slice = {
                    let slice = line.slice(i, i + width);
                    if spaces && i + width < len {
                        match slice.rfind(|ch: char| ch.is_whitespace()) {
                            Some(m) => slice.slice_to(m + 1),
                            None => slice
                        }
                    } else {
                        slice
                    }
                };
                print!("{}", slice);
                i += slice.len();
            }
        } else {
            let mut len = line.char_len();
            let newline =
                if len == 0 {
                    continue;
                } else if len == 1 && line.char_at(len - 1) == '\n' {
                    println!("");
                    continue;
                } else if line.char_at(len - 1) == '\n' {
                    line = line.slice_to(line.len() - 1);
                    len -= 1;
                    true
                } else {
                    false
                };
            let mut output = String::new();
            let mut count = 0;
            for (i, ch) in line.chars().enumerate() {
                if count >= width {
                    let (val, ncount) = {
                        let slice = output.as_slice();
                        let (out, val, ncount) =
                            if spaces && i + 1 < len {
                                match rfind_whitespace(slice) {
                                    Some(m) => {
                                        let routput = slice.slice_chars(m + 1, slice.char_len());
                                        let ncount = routput.chars().fold(0u, |out, ch: char| {
                                            out + match ch {
                                                '\t' => 8,
                                                '\x08' => if out > 0 { -1 } else { 0 },
                                                '\r' => return 0,
                                                _ => 1
                                            }
                                        });
                                        (slice.slice_chars(0, m + 1), routput, ncount)
                                    },
                                    None => (slice, "", 0)
                                }
                            } else {
                                (slice, "", 0)
                            };
                        println!("{}", out);
                        (val.into_string(), ncount)
                    };
                    output = val;
                    count = ncount;
                }
                match ch {
                    '\t' => {
                        count += 8;
                        if count > width {
                            println!("{}", output);
                            output.truncate(0);
                            count = 8;
                        }
                    }
                    '\x08' => {
                        if count > 0 {
                            count -= 1;
                            let len = output.len() - 1;
                            output.truncate(len);
                        }
                        continue;
                    }
                    '\r' => {
                        output.truncate(0);
                        count = 0;
                        continue;
                    }
                    _ => count += 1
                };
                output.push(ch);
            }
            if count > 0 {
                if newline {
                    println!("{}", output);
                } else {
                    print!("{}", output);
                }
            }
        }
    }
}

#[inline]
fn rfind_whitespace(slice: &str) -> Option<uint> {
    for (i, ch) in slice.chars().rev().enumerate() {
        if ch.is_whitespace() {
            return Some(slice.char_len() - (i + 1));
        }
    }
    None
}
<|endoftext|>


<commit_before>
use piston_window::*;
use piston_window::character::CharacterCache;

use game::block::Block;
use game::config::Config;
use game::render_options::RenderOptions;
use game::tetromino::Tetromino;

pub struct Grid {
    pub boxes: [[Option<Block>; 10]; 22],
    overlay: [[Option<Block>; 10]; 22],
    border: rectangle::Rectangle,
    block: rectangle::Rectangle,
}

impl Grid {
    pub fn new() -> Grid {
        Grid {
            boxes: [[None; 10]; 22],
            overlay: [[None; 10]; 22],
            border: rectangle::Rectangle::new([1.0; 4]),
            block: rectangle::Rectangle::new([0.0, 0.0, 0.0, 1.0]),
        }
    }

    pub fn apply_tetromino(&mut self, tetromino: &Tetromino, config: &Config) {
        // Clear the overlay
        self.overlay = [[None; 10]; 22];

        // Iterate over the blocks in the active Tetromino and apply them
        // to the overlay
        for y in 0..4 {
            for x in 0..4 {
                if let Some(ref block) = tetromino.blocks[y][x] {
                    let x = (tetromino.x + x as i32) as usize;
                    let y = (tetromino.y + y as i32) as usize;

                    if x >= 10 || y >= 22 {
                        continue;
                    }

                    self.overlay[y][x] = Some(*block);

                    // If the shadow is enabled, also store that in the overlay
                    if config.shadow_enabled {
                        let (shadow_x, shadow_y) = tetromino.find_landing_xy(&self.boxes);
                        let shadow_x = (shadow_x + x as i32) as usize;
                        let shadow_y = (shadow_y + y as i32) as usize;

                        if let Some(ref shadow) = tetromino.shadow[y][x] {
                            self.overlay[shadow_y][shadow_x] = Some(*shadow);
                        }
                    }
                }
            }
        }
    }

    pub fn store_tetromino(&mut self, tetromino: &Tetromino) {
        for y in 0..4 {
            for x in 0..4 {
                if let Some(ref block) = tetromino.blocks[y][x] {
                    let x = (tetromino.x + x as i32) as usize;
                    let y = (tetromino.y + y as i32) as usize;

                    if x >= 10 || y >= 22 {
                        continue;
                    }

                    self.boxes[y][x] = Some(*block);
                }
            }
        }
    }

    /// Removes complete lines from the grid
    pub fn remove_complete_lines(&mut self, config: &Config) -> u32 {
        // Pointer to where we're currently writing lines
        let mut y_mut = (config.grid_size.1 - 1) as usize;
        let mut cleared_lines = 0;

        for y in (0..config.grid_size.1).rev() {
            // If every column in this row has a block,
            // we consider it "complete"
            let complete = self.boxes[y as usize].iter().all(|block| block.is_some());

            // If this is not a complete line, copy it
            // into the currently pointed line
            if !complete {
                self.boxes[y_mut] = self.boxes[y as usize];
                y_mut -= 1;
                if y_mut == 0 {
                    break;
                }
            } else {
                cleared_lines += 1;
            }
        }

        cleared_lines
    }

    pub fn render<'a, G, C>(&self, options: &mut RenderOptions<'a, G, C>, e: &Event)
        where C: CharacterCache,
              G: Graphics<Texture = <C as CharacterCache>::Texture>
    {
        let grid_offset = options.config.grid_offset;
        let tile_size = options.config.tile_size;

        // Draw the "border" first
        self.border.draw([(grid_offset - 2.0) as f64,
                          (grid_offset - 2.0) as f64,
                          (tile_size * 10.0 + 4.0) as f64,
                          (tile_size * 20.0 + 4.0) as f64],
                         &Default::default(),
                         options.context.transform,
                         options.graphics);

        for y in 2..22 {
            for x in 0..10 {

                let adjusted_y = y - 2;
                if let Some(ref block) = self.overlay[y][x] {
                    block.render(x, y, options.config, options.context, options.graphics, e);
                    continue;
                }

                match self.boxes[y][x] {
                    Some(ref block) => {
                        block.render(x, y, options.config, options.context, options.graphics, e);
                    }
                    None => {
                        self.block.draw([x as f64 * tile_size + grid_offset,
                                         adjusted_y as f64 * tile_size + grid_offset,
                                         tile_size as f64,
                                         tile_size as f64],
                                        &Default::default(),
                                        options.context.transform,
                                        options.graphics);
                    }
                };
            }
        }
    }
}
<commit_msg>Use config options when rendering grid<commit_after>
use piston_window::*;
use piston_window::character::CharacterCache;

use game::block::Block;
use game::config::Config;
use game::render_options::RenderOptions;
use game::tetromino::Tetromino;

pub struct Grid {
    pub boxes: [[Option<Block>; 10]; 22],
    overlay: [[Option<Block>; 10]; 22],
    border: rectangle::Rectangle,
    block: rectangle::Rectangle,
}

impl Grid {
    pub fn new() -> Grid {
        Grid {
            boxes: [[None; 10]; 22],
            overlay: [[None; 10]; 22],
            border: rectangle::Rectangle::new([1.0; 4]),
            block: rectangle::Rectangle::new([0.0, 0.0, 0.0, 1.0]),
        }
    }

    pub fn apply_tetromino(&mut self, tetromino: &Tetromino, config: &Config) {
        // Clear the overlay
        self.overlay = [[None; 10]; 22];

        // Iterate over the blocks in the active Tetromino and apply them
        // to the overlay
        for y in 0..4 {
            for x in 0..4 {
                if let Some(ref block) = tetromino.blocks[y][x] {
                    let x = (tetromino.x + x as i32) as usize;
                    let y = (tetromino.y + y as i32) as usize;

                    if x >= 10 || y >= 22 {
                        continue;
                    }

                    self.overlay[y][x] = Some(*block);

                    // If the shadow is enabled, also store that in the overlay
                    if config.shadow_enabled {
                        let (shadow_x, shadow_y) = tetromino.find_landing_xy(&self.boxes);
                        let shadow_x = (shadow_x + x as i32) as usize;
                        let shadow_y = (shadow_y + y as i32) as usize;

                        if let Some(ref shadow) = tetromino.shadow[y][x] {
                            self.overlay[shadow_y][shadow_x] = Some(*shadow);
                        }
                    }
                }
            }
        }
    }

    /// Permanently stores the Tetromino in the grid
    pub fn store_tetromino(&mut self, tetromino: &Tetromino) {
        for y in 0..4 {
            for x in 0..4 {
                if let Some(ref block) = tetromino.blocks[y][x] {
                    let x = (tetromino.x + x as i32) as usize;
                    let y = (tetromino.y + y as i32) as usize;

                    if x >= 10 || y >= 22 {
                        continue;
                    }

                    self.boxes[y][x] = Some(*block);
                }
            }
        }
    }

    /// Removes complete lines from the grid
    pub fn remove_complete_lines(&mut self, config: &Config) -> u32 {
        // Pointer to where we're currently writing lines
        let mut y_mut = (config.grid_size.1 - 1) as usize;
        let mut cleared_lines = 0;

        for y in (0..config.grid_size.1).rev() {
            // If every column in this row has a block,
            // we consider it "complete"
            let complete = self.boxes[y as usize].iter().all(|block| block.is_some());

            // If this is not a complete line, copy it
            // into the currently pointed line
            if !complete {
                self.boxes[y_mut] = self.boxes[y as usize];
                y_mut -= 1;
                if y_mut == 0 {
                    break;
                }
            } else {
                cleared_lines += 1;
            }
        }

        cleared_lines
    }

    pub fn render<'a, G, C>(&self, options: &mut RenderOptions<'a, G, C>, e: &Event)
        where C: CharacterCache,
              G: Graphics<Texture = <C as CharacterCache>::Texture>
    {
        let grid_offset = options.config.grid_offset;
        let tile_size = options.config.tile_size;

        // Draw the "border" first
        self.border.draw([(grid_offset - 2.0) as f64,
                          (grid_offset - 2.0) as f64,
                          (tile_size * 10.0 + 4.0) as f64,
                          (tile_size * 20.0 + 4.0) as f64],
                         &Default::default(),
                         options.context.transform,
                         options.graphics);

        for y in 2..options.config.grid_size.1 {
            for x in 0..options.config.grid_size.0 {
                let x = x as usize;
                let y = y as usize;

                let adjusted_y = y - 2;
                if let Some(ref block) = self.overlay[y][x] {
                    block.render(x, y, options.config, options.context, options.graphics, e);
                    continue;
                }

                match self.boxes[y as usize][x as usize] {
                    Some(ref block) => {
                        block.render(x, y, options.config, options.context, options.graphics, e);
                    }
                    None => {
                        self.block.draw([x as f64 * tile_size + grid_offset,
                                         adjusted_y as f64 * tile_size + grid_offset,
                                         tile_size as f64,
                                         tile_size as f64],
                                        &Default::default(),
                                        options.context.transform,
                                        options.graphics);
                    }
                };
            }
        }
    }
}
<|endoftext|>


<commit_before>// (C)opyleft 2013 Frank Denis

/*!
 * Bindings for the GeoIP library
 */
#![desc = "Bindings for the GeoIP library."]
#![license = "BSD"]
#![crate_id = "geoip#0.1"]
#![crate_type = "rlib"]

#![warn(non_camel_case_types,
        non_uppercase_statics,
        unnecessary_qualification,
        managed_heap_memory)]

extern crate libc;

use libc::{c_void, c_char, c_int, c_ulong};
use std::c_str::CString;
use std::fmt;
use std::io::net::ip::{IpAddr,Ipv4Addr,Ipv6Addr};

type GeoIP_ = *c_void;
type In6Addr = [u8, ..16];

struct GeoIPLookup {
    netmask: c_int
}

impl GeoIPLookup {
    fn new() -> GeoIPLookup {
        GeoIPLookup {
            netmask: 0
        }
    }
}

#[link(name = "GeoIP")]
extern {
    fn GeoIP_open(dbtype: *c_char, flags: c_int) -> GeoIP_;
    fn GeoIP_delete(db: GeoIP_);
    fn GeoIP_name_by_ipnum_gl(db: GeoIP_, ipnum: c_ulong, gl: &GeoIPLookup) -> *c_char;
    fn GeoIP_name_by_ipnum_v6_gl(db: GeoIP_, ipnum: In6Addr, gl: &GeoIPLookup) -> *c_char;
}

pub enum Options {
    Standard = 0,
    MemoryCache = 1,
    CheckCache = 2,
    IndexCache = 4,
    MmapCache = 8
}

pub enum DBType {
    CountryEdition = 1,
    RegionEditionRev0 = 7,
    CityEditionRev0 = 6,
    ORGEdition = 5,
    ISPEdition = 4,
    CityEditionRev1 = 2,
    RegionEditionRev1 = 3,
    ProxyEdition = 8,
    ASNUMEdition = 9,
    NetSpeedEdition = 10,
    DomainEdition = 11,
    CountryEditionV6 = 12,
    LocationAEdition = 13,
    AccuracyRadiusEdition = 14,
    LargeCountryEdition = 17,
    LargeCountryEditionV6 = 18,
    ASNumEditionV6 = 21,
    ISPEditionV6 = 22,
    ORGEditionV6 = 23,
    DomainEditionV6 = 24,
    LoctionAEditionV6 = 25,
    RegistrarEdition = 26,
    RegistrarEditionV6 = 27,
    UserTypeEdition = 28,
    UserTypeEditionV6 = 29,
    CityEditionRev1V6 = 30,
    CityEditionRev0V6 = 31,
    NetSpeedEditionRev1 = 32,
    NetSpeedEditionRev1V6 = 33,
    CountryConfEdition = 34,
    CityConfEdition = 35,
    RegionConfEdition = 36,
    PostalConfEdition = 37,
    AccuracyRadiusEditionV6 = 38
}

pub struct GeoIP {
    db: GeoIP_
}

pub struct ASInfo {
    pub asn: uint,
    pub name: String,
    pub netmask: uint
}

impl fmt::Show for ASInfo {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}\t{}", self.asn, self.name)
    }
}

impl GeoIP {
    pub fn open(path: &Path, options: Options) -> Result<GeoIP, String> {
        let file = match path.as_str() {
            None => return Err(format!("Invalid path {}", path.display())),
            Some(file) => file
        };
        let db = unsafe {
            GeoIP_open(file.to_c_str().unwrap(), options as c_int)
        };
        match db.is_null() {
            true => Err(format!("Can't open {}", file)),
            false => Ok(GeoIP {
                    db: db
                })
        }
    }

    pub fn as_info_by_ip(&self, ip: IpAddr) -> Option<ASInfo> {
        let gl = GeoIPLookup::new();
        let cres = match ip {
            Ipv4Addr(a, b, c, d) => {
                let ipnum: c_ulong =
                    (a as c_ulong << 24) | (b as c_ulong << 16) |
                    (c as c_ulong << 8)  | (d as c_ulong);
                unsafe {
                    GeoIP_name_by_ipnum_gl(self.db, ipnum, &gl)
                }
            },
            Ipv6Addr(a, b, c, d, e, f, g, h) => {
                let in6_addr: In6Addr = [(a >> 8) as u8, a as u8,
                                         (b >> 8) as u8, b as u8,
                                         (c >> 8) as u8, c as u8,
                                         (d >> 8) as u8, d as u8,
                                         (e >> 8) as u8, e as u8,
                                         (f >> 8) as u8, f as u8,
                                         (g >> 8) as u8, g as u8,
                                         (h >> 8) as u8, h as u8];
                unsafe {
                    GeoIP_name_by_ipnum_v6_gl(self.db, in6_addr, &gl)
                }
            }
        };
        if cres.is_null() {
            return None;
        }
        let description_cstr = unsafe { CString::new(cres, true) };
        let description = match description_cstr.as_str() {
            None => return None,
            Some(description) => description
        };
        let mut di = description.splitn(' ', 1);
        let asn = match di.next() {
            None => return None,
            Some(asn) => {
                if ! asn.starts_with("AS") {
                    return None
                } else {
                    from_str::<uint>(asn.slice_from(2)).unwrap()
                }
            }
        };
        let name = di.next().unwrap_or("(none)");
        let asinfo = ASInfo {
            asn: asn,
            name: name.to_owned(),
            netmask: gl.netmask as uint
        };
        Some(asinfo)
    }
}

impl Drop for GeoIP {
    fn drop(&mut self) {
        unsafe {
            GeoIP_delete(self.db);
        }
    }
}

#[test]
fn geoip_test_basic() {
    let geoip = match GeoIP::open(&from_str("/opt/geoip/GeoIPASNum.dat").unwrap(), MemoryCache) {
        Err(err) => fail!(err),
        Ok(geoip) => geoip
    };
    let ip = from_str("91.203.184.192").unwrap();
    let res = geoip.as_info_by_ip(ip).unwrap();
    assert!(res.asn == 41064);
    assert!(res.name.as_slice().contains("Telefun"));
    assert!(res.netmask == 22);
}
<commit_msg>to_owned() -> to_string()<commit_after>// (C)opyleft 2013 Frank Denis

/*!
 * Bindings for the GeoIP library
 */
#![desc = "Bindings for the GeoIP library."]
#![license = "BSD"]
#![crate_id = "geoip#0.1"]
#![crate_type = "rlib"]

#![warn(non_camel_case_types,
        non_uppercase_statics,
        unnecessary_qualification,
        managed_heap_memory)]

extern crate libc;

use libc::{c_void, c_char, c_int, c_ulong};
use std::c_str::CString;
use std::fmt;
use std::io::net::ip::{IpAddr,Ipv4Addr,Ipv6Addr};

type GeoIP_ = *c_void;
type In6Addr = [u8, ..16];

struct GeoIPLookup {
    netmask: c_int
}

impl GeoIPLookup {
    fn new() -> GeoIPLookup {
        GeoIPLookup {
            netmask: 0
        }
    }
}

#[link(name = "GeoIP")]
extern {
    fn GeoIP_open(dbtype: *c_char, flags: c_int) -> GeoIP_;
    fn GeoIP_delete(db: GeoIP_);
    fn GeoIP_name_by_ipnum_gl(db: GeoIP_, ipnum: c_ulong, gl: &GeoIPLookup) -> *c_char;
    fn GeoIP_name_by_ipnum_v6_gl(db: GeoIP_, ipnum: In6Addr, gl: &GeoIPLookup) -> *c_char;
}

pub enum Options {
    Standard = 0,
    MemoryCache = 1,
    CheckCache = 2,
    IndexCache = 4,
    MmapCache = 8
}

pub enum DBType {
    CountryEdition = 1,
    RegionEditionRev0 = 7,
    CityEditionRev0 = 6,
    ORGEdition = 5,
    ISPEdition = 4,
    CityEditionRev1 = 2,
    RegionEditionRev1 = 3,
    ProxyEdition = 8,
    ASNUMEdition = 9,
    NetSpeedEdition = 10,
    DomainEdition = 11,
    CountryEditionV6 = 12,
    LocationAEdition = 13,
    AccuracyRadiusEdition = 14,
    LargeCountryEdition = 17,
    LargeCountryEditionV6 = 18,
    ASNumEditionV6 = 21,
    ISPEditionV6 = 22,
    ORGEditionV6 = 23,
    DomainEditionV6 = 24,
    LoctionAEditionV6 = 25,
    RegistrarEdition = 26,
    RegistrarEditionV6 = 27,
    UserTypeEdition = 28,
    UserTypeEditionV6 = 29,
    CityEditionRev1V6 = 30,
    CityEditionRev0V6 = 31,
    NetSpeedEditionRev1 = 32,
    NetSpeedEditionRev1V6 = 33,
    CountryConfEdition = 34,
    CityConfEdition = 35,
    RegionConfEdition = 36,
    PostalConfEdition = 37,
    AccuracyRadiusEditionV6 = 38
}

pub struct GeoIP {
    db: GeoIP_
}

pub struct ASInfo {
    pub asn: uint,
    pub name: String,
    pub netmask: uint
}

impl fmt::Show for ASInfo {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}\t{}", self.asn, self.name)
    }
}

impl GeoIP {
    pub fn open(path: &Path, options: Options) -> Result<GeoIP, String> {
        let file = match path.as_str() {
            None => return Err(format!("Invalid path {}", path.display())),
            Some(file) => file
        };
        let db = unsafe {
            GeoIP_open(file.to_c_str().unwrap(), options as c_int)
        };
        match db.is_null() {
            true => Err(format!("Can't open {}", file)),
            false => Ok(GeoIP {
                    db: db
                })
        }
    }

    pub fn as_info_by_ip(&self, ip: IpAddr) -> Option<ASInfo> {
        let gl = GeoIPLookup::new();
        let cres = match ip {
            Ipv4Addr(a, b, c, d) => {
                let ipnum: c_ulong =
                    (a as c_ulong << 24) | (b as c_ulong << 16) |
                    (c as c_ulong << 8)  | (d as c_ulong);
                unsafe {
                    GeoIP_name_by_ipnum_gl(self.db, ipnum, &gl)
                }
            },
            Ipv6Addr(a, b, c, d, e, f, g, h) => {
                let in6_addr: In6Addr = [(a >> 8) as u8, a as u8,
                                         (b >> 8) as u8, b as u8,
                                         (c >> 8) as u8, c as u8,
                                         (d >> 8) as u8, d as u8,
                                         (e >> 8) as u8, e as u8,
                                         (f >> 8) as u8, f as u8,
                                         (g >> 8) as u8, g as u8,
                                         (h >> 8) as u8, h as u8];
                unsafe {
                    GeoIP_name_by_ipnum_v6_gl(self.db, in6_addr, &gl)
                }
            }
        };
        if cres.is_null() {
            return None;
        }
        let description_cstr = unsafe { CString::new(cres, true) };
        let description = match description_cstr.as_str() {
            None => return None,
            Some(description) => description
        };
        let mut di = description.splitn(' ', 1);
        let asn = match di.next() {
            None => return None,
            Some(asn) => {
                if ! asn.starts_with("AS") {
                    return None
                } else {
                    from_str::<uint>(asn.slice_from(2)).unwrap()
                }
            }
        };
        let name = di.next().unwrap_or("(none)");
        let asinfo = ASInfo {
            asn: asn,
            name: name.to_string(),
            netmask: gl.netmask as uint
        };
        Some(asinfo)
    }
}

impl Drop for GeoIP {
    fn drop(&mut self) {
        unsafe {
            GeoIP_delete(self.db);
        }
    }
}

#[test]
fn geoip_test_basic() {
    let geoip = match GeoIP::open(&from_str("/opt/geoip/GeoIPASNum.dat").unwrap(), MemoryCache) {
        Err(err) => fail!(err),
        Ok(geoip) => geoip
    };
    let ip = from_str("91.203.184.192").unwrap();
    let res = geoip.as_info_by_ip(ip).unwrap();
    assert!(res.asn == 41064);
    assert!(res.name.as_slice().contains("Telefun"));
    assert!(res.netmask == 22);
}
<|endoftext|>


<commit_before>
#![crate_name = "geoip"]
#![crate_type = "rlib"]

#![warn(non_camel_case_types,
        non_upper_case_globals,
        unused_qualifications)]
#![feature(libc, std_misc, net, path)]

extern crate libc;
extern crate "rustc-serialize" as rustc_serialize;
extern crate "geoip-sys" as geoip_sys;

use libc::{c_char, c_int, c_ulong};
use std::ffi;
use std::fmt;
use std::net::IpAddr;
use std::path::Path;

#[cfg(test)]
use std::str::FromStr;

enum Charset {
    UTF8 = 1
}

pub enum Options {
    Standard = 0,
    MemoryCache = 1,
    CheckCache = 2,
    IndexCache = 4,
    MmapCache = 8
}

impl Copy for Options { }

pub enum DBType {
    CountryEdition = 1,
    RegionEditionRev0 = 7,
    CityEditionRev0 = 6,
    ORGEdition = 5,
    ISPEdition = 4,
    CityEditionRev1 = 2,
    RegionEditionRev1 = 3,
    ProxyEdition = 8,
    ASNUMEdition = 9,
    NetSpeedEdition = 10,
    DomainEdition = 11,
    CountryEditionV6 = 12,
    LocationAEdition = 13,
    AccuracyRadiusEdition = 14,
    LargeCountryEdition = 17,
    LargeCountryEditionV6 = 18,
    ASNumEditionV6 = 21,
    ISPEditionV6 = 22,
    ORGEditionV6 = 23,
    DomainEditionV6 = 24,
    LoctionAEditionV6 = 25,
    RegistrarEdition = 26,
    RegistrarEditionV6 = 27,
    UserTypeEdition = 28,
    UserTypeEditionV6 = 29,
    CityEditionRev1V6 = 30,
    CityEditionRev0V6 = 31,
    NetSpeedEditionRev1 = 32,
    NetSpeedEditionRev1V6 = 33,
    CountryConfEdition = 34,
    CityConfEdition = 35,
    RegionConfEdition = 36,
    PostalConfEdition = 37,
    AccuracyRadiusEditionV6 = 38
}

impl Copy for DBType { }

pub struct GeoIp {
    db: geoip_sys::RawGeoIp
}

#[derive(RustcDecodable, RustcEncodable)]
pub struct ASInfo {
    pub asn: u32,
    pub name: String,
    pub netmask: u32
}

#[derive(RustcDecodable, RustcEncodable)]
pub struct CityInfo {
    pub country_code: Option<String>,
    pub country_code3: Option<String>,
    pub country_name: Option<String>,
    pub region: Option<String>,
    pub city: Option<String>,
    pub postal_code: Option<String>,
    pub latitude: f32,
    pub longitude: f32,
    pub dma_code: u32,
    pub area_code: u32,
    pub charset: u32,
    pub continent_code: Option<String>,
    pub netmask: u32
}

fn maybe_string(c_str: *const c_char) -> Option<String> {
    if c_str.is_null() {
        None
    } else {
        String::from_utf8(unsafe { ffi::CStr::from_ptr(c_str).to_bytes() }.
                          to_vec()).ok()
    }
}

impl CityInfo {
    unsafe fn from_geoiprecord(res: &geoip_sys::GeoIpRecord) -> CityInfo {
        CityInfo {
            country_code: maybe_string(res.country_code),
            country_code3: maybe_string(res.country_code3),
            country_name: maybe_string(res.country_name),
            region: maybe_string(res.region),
            city: maybe_string(res.city),
            postal_code: maybe_string(res.postal_code),
            latitude: res.latitude as f32,
            longitude: res.longitude as f32,
            dma_code: res.dma_code as u32,
            area_code: res.area_code as u32,
            charset: res.charset as u32,
            continent_code: maybe_string(res.continent_code),
            netmask: res.netmask as u32
        }
    }
}

impl fmt::Debug for ASInfo {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}\t{}", self.asn, self.name)
    }
}

enum CNetworkIp {
    V4(c_ulong),
    V6(geoip_sys::In6Addr)
}

impl CNetworkIp {
    fn new(ip: IpAddr) -> CNetworkIp {
        match ip {
            IpAddr::V4(addr) => {
                let b = addr.octets();
                CNetworkIp::V4(((b[0] as c_ulong) << 24) |
                               ((b[1] as c_ulong) << 16) |
                               ((b[2] as c_ulong) << 8)  |
                               ((b[3] as c_ulong)))
            },
            IpAddr::V6(addr) => {
                let b = addr.segments();
                CNetworkIp::V6([(b[0] >> 8) as u8, b[0] as u8,
                                (b[1] >> 8) as u8, b[1] as u8,
                                (b[2] >> 8) as u8, b[2] as u8,
                                (b[3] >> 8) as u8, b[3] as u8,
                                (b[4] >> 8) as u8, b[4] as u8,
                                (b[5] >> 8) as u8, b[5] as u8,
                                (b[6] >> 8) as u8, b[6] as u8,
                                (b[7] >> 8) as u8, b[7] as u8])
            }
        }
    }
}

impl GeoIp {
    pub fn open(path: &Path, options: Options) -> Result<GeoIp, String> {
        let file = match path.to_str() {
            None => return Err(format!("Invalid path {}", path.display())),
            Some(file) => file
        };
        let db = unsafe {
            geoip_sys::GeoIP_open(ffi::CString::new(file.as_bytes()).
                                  unwrap().as_ptr(), options as c_int)
        };
        if db.is_null() {
            return Err(format!("Can't open {}", file));
        }
        if unsafe { geoip_sys::GeoIP_set_charset(db, Charset::UTF8 as c_int)
        } != 0 {
            return Err("Can't set charset to UTF8".to_string());
        }
        Ok(GeoIp { db: db })
    }

    pub fn city_info_by_ip(&self, ip: IpAddr) -> Option<CityInfo> {
        let cres = match CNetworkIp::new(ip) {
            CNetworkIp::V4(ip) => unsafe {
                geoip_sys::GeoIP_record_by_ipnum(self.db, ip) },
            CNetworkIp::V6(ip) => unsafe {
                geoip_sys::GeoIP_record_by_ipnum_v6(self.db, ip) }
        };

        if cres.is_null() { return None; }

        unsafe {
            let city_info = CityInfo::from_geoiprecord(&*cres);
            geoip_sys::GeoIPRecord_delete(cres);
            std::mem::forget(cres);
            Some(city_info)
        }
    }

    pub fn as_info_by_ip(&self, ip: IpAddr) -> Option<ASInfo> {
        let mut gl = geoip_sys::GeoIpLookup::new();
        let cres = match CNetworkIp::new(ip) {
            CNetworkIp::V4(ip) => unsafe {
                geoip_sys::GeoIP_name_by_ipnum_gl(self.db, ip, &mut gl) },
            CNetworkIp::V6(ip) => unsafe {
                geoip_sys::GeoIP_name_by_ipnum_v6_gl(self.db, ip, &mut gl) }
        };

        if cres.is_null() {
            return None;
        }
        let description = match maybe_string(cres) {
            None => return None,
            Some(description) => description
        };
        let mut di = description.splitn(1, ' ');
        let asn = match di.next() {
            None => return None,
            Some(asn) => {
                if ! asn.starts_with("AS") {
                    return None
                } else {
                    asn[2..].parse::<u32>().unwrap()
                }
            }
        };
        let name = di.next().unwrap_or("(none)");
        let as_info = ASInfo {
            asn: asn,
            name: name.to_string(),
            netmask: gl.netmask as u32
        };
        Some(as_info)
    }
}

impl Drop for GeoIp {
    fn drop(&mut self) {
        unsafe {
            geoip_sys::GeoIP_delete(self.db);
        }
    }
}

#[test]
fn geoip_test_basic() {
    let geoip = match GeoIp::open(&Path::new("/opt/geoip/GeoIPASNum.dat"), Options::MemoryCache) {
        Err(err) => panic!(err),
        Ok(geoip) => geoip
    };
    let ip = FromStr::from_str("91.203.184.192").unwrap();
    let res = geoip.as_info_by_ip(ip).unwrap();
    assert!(res.asn == 41064);
    assert!(res.name.contains("Telefun"));
    assert!(res.netmask == 22);
}

#[test]
fn geoip_test_city() {
    let geoip = match GeoIp::open(&Path::new("/opt/geoip/GeoLiteCity.dat"), Options::MemoryCache) {
        Err(err) => panic!(err),
        Ok(geoip) => geoip
    };
    let ip = FromStr::from_str("8.8.8.8").unwrap();
    let res = geoip.city_info_by_ip(ip).unwrap();
    assert!(res.city.unwrap() == "Mountain View");
}
<commit_msg>std_misc is not required any more<commit_after>
#![crate_name = "geoip"]
#![crate_type = "rlib"]

#![warn(non_camel_case_types,
        non_upper_case_globals,
        unused_qualifications)]
#![feature(libc, net, path)]

extern crate libc;
extern crate "rustc-serialize" as rustc_serialize;
extern crate "geoip-sys" as geoip_sys;

use libc::{c_char, c_int, c_ulong};
use std::ffi;
use std::fmt;
use std::net::IpAddr;
use std::path::Path;

#[cfg(test)]
use std::str::FromStr;

enum Charset {
    UTF8 = 1
}

pub enum Options {
    Standard = 0,
    MemoryCache = 1,
    CheckCache = 2,
    IndexCache = 4,
    MmapCache = 8
}

impl Copy for Options { }

pub enum DBType {
    CountryEdition = 1,
    RegionEditionRev0 = 7,
    CityEditionRev0 = 6,
    ORGEdition = 5,
    ISPEdition = 4,
    CityEditionRev1 = 2,
    RegionEditionRev1 = 3,
    ProxyEdition = 8,
    ASNUMEdition = 9,
    NetSpeedEdition = 10,
    DomainEdition = 11,
    CountryEditionV6 = 12,
    LocationAEdition = 13,
    AccuracyRadiusEdition = 14,
    LargeCountryEdition = 17,
    LargeCountryEditionV6 = 18,
    ASNumEditionV6 = 21,
    ISPEditionV6 = 22,
    ORGEditionV6 = 23,
    DomainEditionV6 = 24,
    LoctionAEditionV6 = 25,
    RegistrarEdition = 26,
    RegistrarEditionV6 = 27,
    UserTypeEdition = 28,
    UserTypeEditionV6 = 29,
    CityEditionRev1V6 = 30,
    CityEditionRev0V6 = 31,
    NetSpeedEditionRev1 = 32,
    NetSpeedEditionRev1V6 = 33,
    CountryConfEdition = 34,
    CityConfEdition = 35,
    RegionConfEdition = 36,
    PostalConfEdition = 37,
    AccuracyRadiusEditionV6 = 38
}

impl Copy for DBType { }

pub struct GeoIp {
    db: geoip_sys::RawGeoIp
}

#[derive(RustcDecodable, RustcEncodable)]
pub struct ASInfo {
    pub asn: u32,
    pub name: String,
    pub netmask: u32
}

#[derive(RustcDecodable, RustcEncodable)]
pub struct CityInfo {
    pub country_code: Option<String>,
    pub country_code3: Option<String>,
    pub country_name: Option<String>,
    pub region: Option<String>,
    pub city: Option<String>,
    pub postal_code: Option<String>,
    pub latitude: f32,
    pub longitude: f32,
    pub dma_code: u32,
    pub area_code: u32,
    pub charset: u32,
    pub continent_code: Option<String>,
    pub netmask: u32
}

fn maybe_string(c_str: *const c_char) -> Option<String> {
    if c_str.is_null() {
        None
    } else {
        String::from_utf8(unsafe { ffi::CStr::from_ptr(c_str).to_bytes() }.
                          to_vec()).ok()
    }
}

impl CityInfo {
    unsafe fn from_geoiprecord(res: &geoip_sys::GeoIpRecord) -> CityInfo {
        CityInfo {
            country_code: maybe_string(res.country_code),
            country_code3: maybe_string(res.country_code3),
            country_name: maybe_string(res.country_name),
            region: maybe_string(res.region),
            city: maybe_string(res.city),
            postal_code: maybe_string(res.postal_code),
            latitude: res.latitude as f32,
            longitude: res.longitude as f32,
            dma_code: res.dma_code as u32,
            area_code: res.area_code as u32,
            charset: res.charset as u32,
            continent_code: maybe_string(res.continent_code),
            netmask: res.netmask as u32
        }
    }
}

impl fmt::Debug for ASInfo {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}\t{}", self.asn, self.name)
    }
}

enum CNetworkIp {
    V4(c_ulong),
    V6(geoip_sys::In6Addr)
}

impl CNetworkIp {
    fn new(ip: IpAddr) -> CNetworkIp {
        match ip {
            IpAddr::V4(addr) => {
                let b = addr.octets();
                CNetworkIp::V4(((b[0] as c_ulong) << 24) |
                               ((b[1] as c_ulong) << 16) |
                               ((b[2] as c_ulong) << 8)  |
                               ((b[3] as c_ulong)))
            },
            IpAddr::V6(addr) => {
                let b = addr.segments();
                CNetworkIp::V6([(b[0] >> 8) as u8, b[0] as u8,
                                (b[1] >> 8) as u8, b[1] as u8,
                                (b[2] >> 8) as u8, b[2] as u8,
                                (b[3] >> 8) as u8, b[3] as u8,
                                (b[4] >> 8) as u8, b[4] as u8,
                                (b[5] >> 8) as u8, b[5] as u8,
                                (b[6] >> 8) as u8, b[6] as u8,
                                (b[7] >> 8) as u8, b[7] as u8])
            }
        }
    }
}

impl GeoIp {
    pub fn open(path: &Path, options: Options) -> Result<GeoIp, String> {
        let file = match path.to_str() {
            None => return Err(format!("Invalid path {}", path.display())),
            Some(file) => file
        };
        let db = unsafe {
            geoip_sys::GeoIP_open(ffi::CString::new(file.as_bytes()).
                                  unwrap().as_ptr(), options as c_int)
        };
        if db.is_null() {
            return Err(format!("Can't open {}", file));
        }
        if unsafe { geoip_sys::GeoIP_set_charset(db, Charset::UTF8 as c_int)
        } != 0 {
            return Err("Can't set charset to UTF8".to_string());
        }
        Ok(GeoIp { db: db })
    }

    pub fn city_info_by_ip(&self, ip: IpAddr) -> Option<CityInfo> {
        let cres = match CNetworkIp::new(ip) {
            CNetworkIp::V4(ip) => unsafe {
                geoip_sys::GeoIP_record_by_ipnum(self.db, ip) },
            CNetworkIp::V6(ip) => unsafe {
                geoip_sys::GeoIP_record_by_ipnum_v6(self.db, ip) }
        };

        if cres.is_null() { return None; }

        unsafe {
            let city_info = CityInfo::from_geoiprecord(&*cres);
            geoip_sys::GeoIPRecord_delete(cres);
            std::mem::forget(cres);
            Some(city_info)
        }
    }

    pub fn as_info_by_ip(&self, ip: IpAddr) -> Option<ASInfo> {
        let mut gl = geoip_sys::GeoIpLookup::new();
        let cres = match CNetworkIp::new(ip) {
            CNetworkIp::V4(ip) => unsafe {
                geoip_sys::GeoIP_name_by_ipnum_gl(self.db, ip, &mut gl) },
            CNetworkIp::V6(ip) => unsafe {
                geoip_sys::GeoIP_name_by_ipnum_v6_gl(self.db, ip, &mut gl) }
        };

        if cres.is_null() {
            return None;
        }
        let description = match maybe_string(cres) {
            None => return None,
            Some(description) => description
        };
        let mut di = description.splitn(1, ' ');
        let asn = match di.next() {
            None => return None,
            Some(asn) => {
                if ! asn.starts_with("AS") {
                    return None
                } else {
                    asn[2..].parse::<u32>().unwrap()
                }
            }
        };
        let name = di.next().unwrap_or("(none)");
        let as_info = ASInfo {
            asn: asn,
            name: name.to_string(),
            netmask: gl.netmask as u32
        };
        Some(as_info)
    }
}

impl Drop for GeoIp {
    fn drop(&mut self) {
        unsafe {
            geoip_sys::GeoIP_delete(self.db);
        }
    }
}

#[test]
fn geoip_test_basic() {
    let geoip = match GeoIp::open(&Path::new("/opt/geoip/GeoIPASNum.dat"), Options::MemoryCache) {
        Err(err) => panic!(err),
        Ok(geoip) => geoip
    };
    let ip = FromStr::from_str("91.203.184.192").unwrap();
    let res = geoip.as_info_by_ip(ip).unwrap();
    assert!(res.asn == 41064);
    assert!(res.name.contains("Telefun"));
    assert!(res.netmask == 22);
}

#[test]
fn geoip_test_city() {
    let geoip = match GeoIp::open(&Path::new("/opt/geoip/GeoLiteCity.dat"), Options::MemoryCache) {
        Err(err) => panic!(err),
        Ok(geoip) => geoip
    };
    let ip = FromStr::from_str("8.8.8.8").unwrap();
    let res = geoip.city_info_by_ip(ip).unwrap();
    assert!(res.city.unwrap() == "Mountain View");
}
<|endoftext|>