<commit_before>use std::ptr::null;
use libc::{c_int, c_char, c_double};
use rustrt::mutex::{StaticNativeMutex, NATIVE_MUTEX_INIT};
use utils::_string;

#[link(name="gdal")]
extern {
    fn OGRRegisterAll();
    fn OGROpen(pszName: *const c_char, bUpdate: c_int, pahDriverList: *const ()) -> *const ();
    fn OGR_DS_GetLayerCount(hDS: *const ()) -> c_int;
    fn OGR_DS_Destroy(hDataSource: *const ());
    fn OGR_DS_GetLayer(hDS: *const (), iLayer: c_int) -> *const ();
    fn OGR_L_GetLayerDefn(hLayer: *const ()) -> *const ();
    fn OGR_L_GetNextFeature(hLayer: *const ()) -> *const ();
    fn OGR_FD_GetFieldCount(hDefn: *const ()) -> c_int;
    fn OGR_FD_GetFieldDefn(hDefn: *const (), iField: c_int) -> *const ();
    fn OGR_F_GetFieldIndex(hFeat: *const (), pszName: *const c_char) -> c_int;
    fn OGR_F_GetFieldDefnRef(hFeat: *const (), i: c_int) -> *const ();
    fn OGR_F_GetFieldAsString(hFeat: *const (), iField: c_int) -> *const c_char;
    fn OGR_F_GetFieldAsDouble(hFeat: *const (), iField: c_int) -> c_double;
    fn OGR_F_GetGeometryRef(hFeat: *const ()) -> *const ();
    fn OGR_F_Destroy(hFeat: *const ());
    fn OGR_G_ExportToWkt(hGeom: *const (), ppszSrcText: *const *const c_char) -> c_int;
    fn OGR_G_ExportToJson(hGeometry: *const ()) -> *const c_char;
    fn OGR_Fld_GetNameRef(hDefn: *const ()) -> *const c_char;
    fn OGR_Fld_GetType(hDefn: *const ()) -> c_int;
    fn OGRFree(ptr: *const ());
    fn VSIFree(ptr: *const ());
}

const OFT_REAL:             c_int = 2;
const OFT_STRING:           c_int = 4;

static mut LOCK: StaticNativeMutex = NATIVE_MUTEX_INIT;
static mut registered_drivers: bool = false;

fn register_drivers() {
    unsafe {
        let _g = LOCK.lock();
        if ! registered_drivers {
            OGRRegisterAll();
            registered_drivers = true;
        }
    }
}

pub struct VectorDataset {
    c_dataset: *const (),
}

impl VectorDataset {
    pub fn count(&self) -> int {
        return unsafe { OGR_DS_GetLayerCount(self.c_dataset) } as int;
    }

    pub fn layer<'a>(&'a self, idx: int) -> Option<Layer<'a>> {
        let c_layer = unsafe { OGR_DS_GetLayer(self.c_dataset, idx as c_int) };
        return match c_layer.is_null() {
            true  => None,
            false => Some(Layer{vector_dataset: self, c_layer: c_layer}),
        };
    }
}

impl Drop for VectorDataset {
    fn drop(&mut self) {
        unsafe { OGR_DS_Destroy(self.c_dataset); }
    }
}

pub struct Layer<'a> {
    vector_dataset: &'a VectorDataset,
    c_layer: *const (),
}

impl<'a> Layer<'a> {
    pub fn fields<'a>(&'a self) -> FieldIterator<'a> {
        let c_feature_defn = unsafe { OGR_L_GetLayerDefn(self.c_layer) };
        let total = unsafe { OGR_FD_GetFieldCount(c_feature_defn) } as int;
        return FieldIterator{
            layer: self,
            c_feature_defn: c_feature_defn,
            next_id: 0,
            total: total
        };
    }

    pub fn features<'a>(&'a self) -> FeatureIterator<'a> {
        return FeatureIterator{layer: self};
    }
}

pub struct FieldIterator<'a> {
    layer: &'a Layer<'a>,
    c_feature_defn: *const (),
    next_id: int,
    total: int,
}

impl<'a> Iterator<Field<'a>> for FieldIterator<'a> {
    #[inline]
    fn next(&mut self) -> Option<Field<'a>> {
        if self.next_id == self.total {
            return None;
        }
        let field = Field{
            layer: self.layer,
            c_field_defn: unsafe { OGR_FD_GetFieldDefn(
                self.c_feature_defn,
                self.next_id as c_int
            ) }
        };
        self.next_id += 1;
        return Some(field);
    }
}

pub struct Field<'a> {
    layer: &'a Layer<'a>,
    c_field_defn: *const (),
}

impl<'a> Field<'a> {
    pub fn name(&'a self) -> String {
        let rv = unsafe { OGR_Fld_GetNameRef(self.c_field_defn) };
        return _string(rv);
    }
}

pub struct FeatureIterator<'a> {
    layer: &'a Layer<'a>,
}

impl<'a> Iterator<Feature<'a>> for FeatureIterator<'a> {
    #[inline]
    fn next(&mut self) -> Option<Feature<'a>> {
        let c_feature = unsafe { OGR_L_GetNextFeature(self.layer.c_layer) };
        return match c_feature.is_null() {
            true  => None,
            false => Some(Feature{layer: self.layer, c_feature: c_feature}),
        };
    }
}

pub struct Feature<'a> {
    layer: &'a Layer<'a>,
    c_feature: *const (),
}

impl<'a> Feature<'a> {
    pub fn field(&self, name: &str) -> Option<FieldValue> {
        return name.with_c_str(|c_name| unsafe {
            let field_id = OGR_F_GetFieldIndex(self.c_feature, c_name);
            if field_id == -1 {
                return None;
            }
            let field_defn = OGR_F_GetFieldDefnRef(self.c_feature, field_id);
            let field_type = OGR_Fld_GetType(field_defn);
            return match field_type {
                OFT_STRING => {
                    let rv = OGR_F_GetFieldAsString(self.c_feature, field_id);
                    return Some(FieldValue::StringValue(_string(rv)));
                },
                OFT_REAL => {
                    let rv = OGR_F_GetFieldAsDouble(self.c_feature, field_id);
                    return Some(FieldValue::RealValue(rv as f64));
                },
                _ => panic!("Unknown field type {}", field_type)
            }
        });
    }

    pub fn wkt(&self) -> String {
        unsafe {
            let c_geom = OGR_F_GetGeometryRef(self.c_feature);
            let c_wkt: *const c_char = null();
            OGR_G_ExportToWkt(c_geom, &c_wkt);
            let wkt = _string(c_wkt);
            OGRFree(c_wkt as *const ());
            return wkt;
        }
    }

    pub fn json(&self) -> String {
        unsafe {
            let c_geom = OGR_F_GetGeometryRef(self.c_feature);
            let c_json = OGR_G_ExportToJson(c_geom);
            let json = _string(c_json);
            VSIFree(c_json as *const ());
            return json;
        }
    }
}

#[unsafe_destructor]
impl<'a> Drop for Feature<'a> {
    fn drop(&mut self) {
        unsafe { OGR_F_Destroy(self.c_feature); }
    }
}

pub fn open(path: &Path) -> Option<VectorDataset> {
    register_drivers();
    let filename = path.as_str().unwrap();
    let c_dataset = filename.with_c_str(|c_filename| {
        return unsafe { OGROpen(c_filename, 0, null()) };
    });
    return match c_dataset.is_null() {
        true  => None,
        false => Some(VectorDataset{c_dataset: c_dataset}),
    };
}

pub enum FieldValue {
    StringValue(String),
    RealValue(f64),
}

impl FieldValue {
    pub fn as_string(self) -> String {
        match self {
            FieldValue::StringValue(rv) => rv,
            _ => panic!("not a StringValue")
        }
    }

    pub fn as_real(self) -> f64 {
        match self {
            FieldValue::RealValue(rv) => rv,
            _ => panic!("not a RealValue")
        }
    }
}

#[cfg(test)]
mod test {
    use std::path::Path;
    use super::{Feature, FeatureIterator, open};

    fn fixtures() -> Path {
        return Path::new(file!()).dir_path().dir_path().join("fixtures");
    }

    fn assert_almost_eq(a: f64, b: f64) {
        let f: f64 = a / b;
        assert!(f < 1.00001);
        assert!(f > 0.99999);
    }

    #[test]
    fn test_layer_count() {
        let ds = open(&fixtures().join("roads.geojson")).unwrap();
        assert_eq!(ds.count(), 1);
    }

    fn with_features(fixture: &str, f: |FeatureIterator|) {
        let ds = open(&fixtures().join(fixture)).unwrap();
        let layer = ds.layer(0).unwrap();
        f(layer.features());
    }

    fn with_first_feature(fixture: &str, f: |Feature|) {
        with_features(fixture, |mut features| f(features.next().unwrap()));
    }

    #[test]
    fn test_iterate_features() {
        with_features("roads.geojson", |features| {
            let feature_vec: Vec<Feature> = features.collect();
            assert_eq!(feature_vec.len(), 21);
        });
    }

    #[test]
    fn test_string_field() {
        with_features("roads.geojson", |mut features| {
            let feature = features.next().unwrap();
            assert_eq!(feature.field("highway")
                              .unwrap()
                              .as_string(),
                       "footway".to_string());
            assert_eq!(
                features.filter(|field| {
                    let highway = field.field("highway")
                                       .unwrap()
                                       .as_string();
                    highway == "residential".to_string() })
                    .count(),
                2);
        });
    }

    #[test]
    fn test_float_field() {
        with_first_feature("roads.geojson", |feature| {
            assert_almost_eq(
                feature.field("sort_key")
                       .unwrap()
                       .as_real(),
                -9.0
            );
        });
    }

    #[test]
    fn test_missing_field() {
        with_first_feature("roads.geojson", |feature| {
            assert!(feature.field("no such field").is_none());
        });
    }

    #[test]
    fn test_wkt() {
        with_first_feature("roads.geojson", |feature| {
            let wkt = feature.wkt();
            let wkt_ok = format!("{}{}",
                "LINESTRING (26.1019276 44.4302748,",
                "26.1019382 44.4303191,26.1020002 44.4304202)"
                ).to_string();
            assert_eq!(wkt, wkt_ok);
        });
    }

    #[test]
    fn test_json() {
        with_first_feature("roads.geojson", |feature| {
            let json = feature.json();
            let json_ok = format!("{}{}{}{}",
                "{ \"type\": \"LineString\", \"coordinates\": [ ",
                "[ 26.1019276, 44.4302748 ], ",
                "[ 26.1019382, 44.4303191 ], ",
                "[ 26.1020002, 44.4304202 ] ] }"
                ).to_string();
            assert_eq!(json, json_ok);
        });
    }

    #[test]
    fn test_schema() {
        let ds = open(&fixtures().join("roads.geojson")).unwrap();
        let layer = ds.layer(0).unwrap();
        let name_list: Vec<String> = layer
            .fields()
            .map(|f| f.name())
            .collect();
        let ok_names: Vec<String> = vec!(
            "kind", "sort_key", "is_link", "is_tunnel",
            "is_bridge", "railway", "highway")
            .iter().map(|s| s.to_string()).collect();
        assert_eq!(name_list, ok_names);
    }
}
<commit_msg>fix type shadowing warnings<commit_after>use std::ptr::null;
use libc::{c_int, c_char, c_double};
use rustrt::mutex::{StaticNativeMutex, NATIVE_MUTEX_INIT};
use utils::_string;

#[link(name="gdal")]
extern {
    fn OGRRegisterAll();
    fn OGROpen(pszName: *const c_char, bUpdate: c_int, pahDriverList: *const ()) -> *const ();
    fn OGR_DS_GetLayerCount(hDS: *const ()) -> c_int;
    fn OGR_DS_Destroy(hDataSource: *const ());
    fn OGR_DS_GetLayer(hDS: *const (), iLayer: c_int) -> *const ();
    fn OGR_L_GetLayerDefn(hLayer: *const ()) -> *const ();
    fn OGR_L_GetNextFeature(hLayer: *const ()) -> *const ();
    fn OGR_FD_GetFieldCount(hDefn: *const ()) -> c_int;
    fn OGR_FD_GetFieldDefn(hDefn: *const (), iField: c_int) -> *const ();
    fn OGR_F_GetFieldIndex(hFeat: *const (), pszName: *const c_char) -> c_int;
    fn OGR_F_GetFieldDefnRef(hFeat: *const (), i: c_int) -> *const ();
    fn OGR_F_GetFieldAsString(hFeat: *const (), iField: c_int) -> *const c_char;
    fn OGR_F_GetFieldAsDouble(hFeat: *const (), iField: c_int) -> c_double;
    fn OGR_F_GetGeometryRef(hFeat: *const ()) -> *const ();
    fn OGR_F_Destroy(hFeat: *const ());
    fn OGR_G_ExportToWkt(hGeom: *const (), ppszSrcText: *const *const c_char) -> c_int;
    fn OGR_G_ExportToJson(hGeometry: *const ()) -> *const c_char;
    fn OGR_Fld_GetNameRef(hDefn: *const ()) -> *const c_char;
    fn OGR_Fld_GetType(hDefn: *const ()) -> c_int;
    fn OGRFree(ptr: *const ());
    fn VSIFree(ptr: *const ());
}

const OFT_REAL:             c_int = 2;
const OFT_STRING:           c_int = 4;

static mut LOCK: StaticNativeMutex = NATIVE_MUTEX_INIT;
static mut registered_drivers: bool = false;

fn register_drivers() {
    unsafe {
        let _g = LOCK.lock();
        if ! registered_drivers {
            OGRRegisterAll();
            registered_drivers = true;
        }
    }
}

pub struct VectorDataset {
    c_dataset: *const (),
}

impl VectorDataset {
    pub fn count(&self) -> int {
        return unsafe { OGR_DS_GetLayerCount(self.c_dataset) } as int;
    }

    pub fn layer<'a>(&'a self, idx: int) -> Option<Layer<'a>> {
        let c_layer = unsafe { OGR_DS_GetLayer(self.c_dataset, idx as c_int) };
        return match c_layer.is_null() {
            true  => None,
            false => Some(Layer{vector_dataset: self, c_layer: c_layer}),
        };
    }
}

impl Drop for VectorDataset {
    fn drop(&mut self) {
        unsafe { OGR_DS_Destroy(self.c_dataset); }
    }
}

pub struct Layer<'a> {
    vector_dataset: &'a VectorDataset,
    c_layer: *const (),
}

impl<'a> Layer<'a> {
    pub fn fields(&'a self) -> FieldIterator<'a> {
        let c_feature_defn = unsafe { OGR_L_GetLayerDefn(self.c_layer) };
        let total = unsafe { OGR_FD_GetFieldCount(c_feature_defn) } as int;
        return FieldIterator{
            layer: self,
            c_feature_defn: c_feature_defn,
            next_id: 0,
            total: total
        };
    }

    pub fn features(&'a self) -> FeatureIterator<'a> {
        return FeatureIterator{layer: self};
    }
}

pub struct FieldIterator<'a> {
    layer: &'a Layer<'a>,
    c_feature_defn: *const (),
    next_id: int,
    total: int,
}

impl<'a> Iterator<Field<'a>> for FieldIterator<'a> {
    #[inline]
    fn next(&mut self) -> Option<Field<'a>> {
        if self.next_id == self.total {
            return None;
        }
        let field = Field{
            layer: self.layer,
            c_field_defn: unsafe { OGR_FD_GetFieldDefn(
                self.c_feature_defn,
                self.next_id as c_int
            ) }
        };
        self.next_id += 1;
        return Some(field);
    }
}

pub struct Field<'a> {
    layer: &'a Layer<'a>,
    c_field_defn: *const (),
}

impl<'a> Field<'a> {
    pub fn name(&'a self) -> String {
        let rv = unsafe { OGR_Fld_GetNameRef(self.c_field_defn) };
        return _string(rv);
    }
}

pub struct FeatureIterator<'a> {
    layer: &'a Layer<'a>,
}

impl<'a> Iterator<Feature<'a>> for FeatureIterator<'a> {
    #[inline]
    fn next(&mut self) -> Option<Feature<'a>> {
        let c_feature = unsafe { OGR_L_GetNextFeature(self.layer.c_layer) };
        return match c_feature.is_null() {
            true  => None,
            false => Some(Feature{layer: self.layer, c_feature: c_feature}),
        };
    }
}

pub struct Feature<'a> {
    layer: &'a Layer<'a>,
    c_feature: *const (),
}

impl<'a> Feature<'a> {
    pub fn field(&self, name: &str) -> Option<FieldValue> {
        return name.with_c_str(|c_name| unsafe {
            let field_id = OGR_F_GetFieldIndex(self.c_feature, c_name);
            if field_id == -1 {
                return None;
            }
            let field_defn = OGR_F_GetFieldDefnRef(self.c_feature, field_id);
            let field_type = OGR_Fld_GetType(field_defn);
            return match field_type {
                OFT_STRING => {
                    let rv = OGR_F_GetFieldAsString(self.c_feature, field_id);
                    return Some(FieldValue::StringValue(_string(rv)));
                },
                OFT_REAL => {
                    let rv = OGR_F_GetFieldAsDouble(self.c_feature, field_id);
                    return Some(FieldValue::RealValue(rv as f64));
                },
                _ => panic!("Unknown field type {}", field_type)
            }
        });
    }

    pub fn wkt(&self) -> String {
        unsafe {
            let c_geom = OGR_F_GetGeometryRef(self.c_feature);
            let c_wkt: *const c_char = null();
            OGR_G_ExportToWkt(c_geom, &c_wkt);
            let wkt = _string(c_wkt);
            OGRFree(c_wkt as *const ());
            return wkt;
        }
    }

    pub fn json(&self) -> String {
        unsafe {
            let c_geom = OGR_F_GetGeometryRef(self.c_feature);
            let c_json = OGR_G_ExportToJson(c_geom);
            let json = _string(c_json);
            VSIFree(c_json as *const ());
            return json;
        }
    }
}

#[unsafe_destructor]
impl<'a> Drop for Feature<'a> {
    fn drop(&mut self) {
        unsafe { OGR_F_Destroy(self.c_feature); }
    }
}

pub fn open(path: &Path) -> Option<VectorDataset> {
    register_drivers();
    let filename = path.as_str().unwrap();
    let c_dataset = filename.with_c_str(|c_filename| {
        return unsafe { OGROpen(c_filename, 0, null()) };
    });
    return match c_dataset.is_null() {
        true  => None,
        false => Some(VectorDataset{c_dataset: c_dataset}),
    };
}

pub enum FieldValue {
    StringValue(String),
    RealValue(f64),
}

impl FieldValue {
    pub fn as_string(self) -> String {
        match self {
            FieldValue::StringValue(rv) => rv,
            _ => panic!("not a StringValue")
        }
    }

    pub fn as_real(self) -> f64 {
        match self {
            FieldValue::RealValue(rv) => rv,
            _ => panic!("not a RealValue")
        }
    }
}

#[cfg(test)]
mod test {
    use std::path::Path;
    use super::{Feature, FeatureIterator, open};

    fn fixtures() -> Path {
        return Path::new(file!()).dir_path().dir_path().join("fixtures");
    }

    fn assert_almost_eq(a: f64, b: f64) {
        let f: f64 = a / b;
        assert!(f < 1.00001);
        assert!(f > 0.99999);
    }

    #[test]
    fn test_layer_count() {
        let ds = open(&fixtures().join("roads.geojson")).unwrap();
        assert_eq!(ds.count(), 1);
    }

    fn with_features(fixture: &str, f: |FeatureIterator|) {
        let ds = open(&fixtures().join(fixture)).unwrap();
        let layer = ds.layer(0).unwrap();
        f(layer.features());
    }

    fn with_first_feature(fixture: &str, f: |Feature|) {
        with_features(fixture, |mut features| f(features.next().unwrap()));
    }

    #[test]
    fn test_iterate_features() {
        with_features("roads.geojson", |features| {
            let feature_vec: Vec<Feature> = features.collect();
            assert_eq!(feature_vec.len(), 21);
        });
    }

    #[test]
    fn test_string_field() {
        with_features("roads.geojson", |mut features| {
            let feature = features.next().unwrap();
            assert_eq!(feature.field("highway")
                              .unwrap()
                              .as_string(),
                       "footway".to_string());
            assert_eq!(
                features.filter(|field| {
                    let highway = field.field("highway")
                                       .unwrap()
                                       .as_string();
                    highway == "residential".to_string() })
                    .count(),
                2);
        });
    }

    #[test]
    fn test_float_field() {
        with_first_feature("roads.geojson", |feature| {
            assert_almost_eq(
                feature.field("sort_key")
                       .unwrap()
                       .as_real(),
                -9.0
            );
        });
    }

    #[test]
    fn test_missing_field() {
        with_first_feature("roads.geojson", |feature| {
            assert!(feature.field("no such field").is_none());
        });
    }

    #[test]
    fn test_wkt() {
        with_first_feature("roads.geojson", |feature| {
            let wkt = feature.wkt();
            let wkt_ok = format!("{}{}",
                "LINESTRING (26.1019276 44.4302748,",
                "26.1019382 44.4303191,26.1020002 44.4304202)"
                ).to_string();
            assert_eq!(wkt, wkt_ok);
        });
    }

    #[test]
    fn test_json() {
        with_first_feature("roads.geojson", |feature| {
            let json = feature.json();
            let json_ok = format!("{}{}{}{}",
                "{ \"type\": \"LineString\", \"coordinates\": [ ",
                "[ 26.1019276, 44.4302748 ], ",
                "[ 26.1019382, 44.4303191 ], ",
                "[ 26.1020002, 44.4304202 ] ] }"
                ).to_string();
            assert_eq!(json, json_ok);
        });
    }

    #[test]
    fn test_schema() {
        let ds = open(&fixtures().join("roads.geojson")).unwrap();
        let layer = ds.layer(0).unwrap();
        let name_list: Vec<String> = layer
            .fields()
            .map(|f| f.name())
            .collect();
        let ok_names: Vec<String> = vec!(
            "kind", "sort_key", "is_link", "is_tunnel",
            "is_bridge", "railway", "highway")
            .iter().map(|s| s.to_string()).collect();
        assert_eq!(name_list, ok_names);
    }
}
<|endoftext|>


<commit_before>use std::ops::{Add, AddAssign, Sub};

pub struct Vec3 {
  pub i : f64,
  pub j : f64,
  pub k : f64,
}

impl Vec3 {
  pub fn length(&self) -> f64 {
    (self.i * self.i + self.j * self.j + self.k * self.k).sqrt()
  }
}

impl Add for Vec3 {
  type Output = Vec3;
  fn add(self, other: Vec3) -> Vec3 {
      Vec3 {i: self.i + other.i, j: self.j + other.j, k: self.k + other.k}
  }
}

impl AddAssign for Vec3 {
  fn add_assign(&mut self, other: Vec3) {
    *self = Vec3 {
      i: self.i + other.i,
      j: self.j + other.j,
      k: self.k + other.k,
    };
  }
}

impl Sub for Vec3 {
  type Output = Vec3;
  fn sub(self, other: Vec3) -> Vec3 {
      Vec3 {i: self.i - other.i, j: self.j - other.j, k: self.k - other.k}
  }
}
<commit_msg>Add derived traits for printing and copying<commit_after>use std::ops::{Add, AddAssign, Sub};

#[derive(Debug, Copy, Clone)]
pub struct Vec3 {
  pub i : f64,
  pub j : f64,
  pub k : f64,
}

impl Vec3 {
  pub fn length(&self) -> f64 {
    (self.i * self.i + self.j * self.j + self.k * self.k).sqrt()
  }
}

impl Add for Vec3 {
  type Output = Vec3;
  fn add(self, other: Vec3) -> Vec3 {
      Vec3 {i: self.i + other.i, j: self.j + other.j, k: self.k + other.k}
  }
}

impl AddAssign for Vec3 {
  fn add_assign(&mut self, other: Vec3) {
    *self = Vec3 {
      i: self.i + other.i,
      j: self.j + other.j,
      k: self.k + other.k,
    };
  }
}

impl Sub for Vec3 {
  type Output = Vec3;
  fn sub(self, other: Vec3) -> Vec3 {
      Vec3 {i: self.i - other.i, j: self.j - other.j, k: self.k - other.k}
  }
}
<|endoftext|>


<commit_before>// Copyright 2013 The Servo Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

extern crate png;
extern crate std;
extern crate test;
extern crate regex;

use std::ascii::StrAsciiExt;
use std::io;
use std::io::{File, Reader, Command};
use std::io::process::ExitStatus;
use std::os;
use test::{AutoColor, DynTestName, DynTestFn, TestDesc, TestOpts, TestDescAndFn};
use test::run_tests_console;
use regex::Regex;

enum RenderMode {
  CpuRendering = 1,
  GpuRendering = 2,
}

fn main() {
    let args = os::args();
    let mut parts = args.tail().split(|e| "--" == e.as_slice());

    let harness_args = parts.next().unwrap();  // .split() is never empty
    let servo_args = parts.next().unwrap_or(&[]);

    let (render_mode_string, base_path, testname) = match harness_args {
        [] | [_] => fail!("USAGE: cpu|gpu base_path [testname regex]"),
        [ref render_mode_string, ref base_path] => (render_mode_string, base_path, None),
        [ref render_mode_string, ref base_path, ref testname, ..] => (render_mode_string, base_path, Some(Regex::new(testname.as_slice()).unwrap())),
    };

    let render_mode = match render_mode_string.as_slice() {
        "cpu" => CpuRendering,
        "gpu" => GpuRendering,
        _ => fail!("First argument must specify cpu or gpu as rendering mode")
    };

    let mut all_tests = vec!();
    println!("Scanning {} for manifests\n", base_path);

    for file in io::fs::walk_dir(&Path::new(base_path.as_slice())).unwrap() {
        let maybe_extension = file.extension_str();
        match maybe_extension {
            Some(extension) => {
                if extension.to_ascii_lower().as_slice() == "list" && file.is_file() {
                    let manifest = file.as_str().unwrap();
                    let tests = parse_lists(manifest, servo_args, render_mode);
                    println!("\t{} [{} tests]", manifest, tests.len());
                    all_tests.push_all_move(tests);
                }
            }
            _ => {}
        }
    }

    let test_opts = TestOpts {
        filter: testname,
        run_ignored: false,
        logfile: None,
        run_tests: true,
        run_benchmarks: false,
        ratchet_noise_percent: None,
        ratchet_metrics: None,
        save_metrics: None,
        test_shard: None,
        nocapture: false,
        color: AutoColor
    };

    match run_tests_console(&test_opts, all_tests) {
        Ok(false) => os::set_exit_status(1), // tests failed
        Err(_) => os::set_exit_status(2),    // I/O-related failure
        _ => (),
    }
}

#[deriving(PartialEq)]
enum ReftestKind {
    Same,
    Different,
}

struct Reftest {
    name: String,
    kind: ReftestKind,
    files: [String, ..2],
    id: uint,
    servo_args: Vec<String>,
    render_mode: RenderMode,
    flakiness: uint,
}

struct TestLine<'a> {
    conditions: &'a str,
    kind: &'a str,
    file_left: &'a str,
    file_right: &'a str,
}

fn parse_lists(file: &str, servo_args: &[String], render_mode: RenderMode) -> Vec<TestDescAndFn> {
    let mut tests = Vec::new();
    let mut next_id = 0;
    let file_path = Path::new(file);
    let contents = File::open_mode(&file_path, io::Open, io::Read)
                       .and_then(|mut f| f.read_to_string())
                       .ok().expect("Could not read file");

    for line in contents.as_slice().lines() {
        // ignore comments or empty lines
        if line.starts_with("#") || line.is_empty() {
            continue;
        }

        let parts: Vec<&str> = line.split(' ').filter(|p| !p.is_empty()).collect();

        let test_line = match parts.len() {
            3 => TestLine {
                conditions: "",
                kind: parts[0],
                file_left: parts[1],
                file_right: parts[2],
            },
            4 => TestLine {
                conditions: parts[0],
                kind: parts[1],
                file_left: parts[2],
                file_right: parts[3],
            },
            _ => fail!("reftest line: '{:s}' doesn't match '[CONDITIONS] KIND LEFT RIGHT'", line),
        };

        let kind = match test_line.kind {
            "==" => Same,
            "!=" => Different,
            part => fail!("reftest line: '{:s}' has invalid kind '{:s}'", line, part)
        };
        let src_path = file_path.dir_path();
        let src_dir = src_path.display().to_string();
        let file_left =  src_dir.clone().append("/").append(test_line.file_left);
        let file_right = src_dir.append("/").append(test_line.file_right);

        let mut conditions_list = test_line.conditions.split(',');
        let mut flakiness = 0;
        for condition in conditions_list {
            match condition {
                "flaky_cpu" => flakiness |= CpuRendering as uint,
                "flaky_gpu" => flakiness |= GpuRendering as uint,
                _ => (),
            }
        }

        let reftest = Reftest {
            name: test_line.file_left.to_string().append(" / ").append(test_line.file_right),
            kind: kind,
            files: [file_left, file_right],
            id: next_id,
            render_mode: render_mode,
            servo_args: servo_args.iter().map(|x| x.clone()).collect(),
            flakiness: flakiness,
        };

        next_id += 1;

        tests.push(make_test(reftest));
    }
    tests
}

fn make_test(reftest: Reftest) -> TestDescAndFn {
    let name = reftest.name.clone();
    TestDescAndFn {
        desc: TestDesc {
            name: DynTestName(name),
            ignore: false,
            should_fail: false,
        },
        testfn: DynTestFn(proc() {
            check_reftest(reftest);
        }),
    }
}

fn capture(reftest: &Reftest, side: uint) -> png::Image {
    let filename = format!("/tmp/servo-reftest-{:06u}-{:u}.png", reftest.id, side);
    let mut args = reftest.servo_args.clone();
    match reftest.render_mode {
        CpuRendering => args.push("-c".to_string()),
        _ => {}   // GPU rendering is the default
    }
    args.push_all_move(vec!("-f".to_string(), "-o".to_string(), filename.clone(), reftest.files[side].clone()));

    let retval = match Command::new("./servo").args(args.as_slice()).status() {
        Ok(status) => status,
        Err(e) => fail!("failed to execute process: {}", e),
    };
    assert!(retval == ExitStatus(0));

    png::load_png(&from_str::<Path>(filename.as_slice()).unwrap()).unwrap()
}

fn check_reftest(reftest: Reftest) {
    let left  = capture(&reftest, 0);
    let right = capture(&reftest, 1);

    let pixels = left.pixels.iter().zip(right.pixels.iter()).map(|(&a, &b)| {
        if a as i8 - b as i8 == 0 {
            // White for correct
            0xFF
        } else {
            // "1100" in the RGBA channel with an error for an incorrect value
            // This results in some number of C0 and FFs, which is much more
            // readable (and distinguishable) than the previous difference-wise
            // scaling but does not require reconstructing the actual RGBA pixel.
            0xC0
        }
    }).collect::<Vec<u8>>();

    let test_is_flaky = (reftest.render_mode as uint & reftest.flakiness) != 0;

    if pixels.iter().any(|&a| a < 255) {
        let output_str = format!("/tmp/servo-reftest-{:06u}-diff.png", reftest.id);
        let output = from_str::<Path>(output_str.as_slice()).unwrap();

        let mut img = png::Image {
            width: left.width,
            height: left.height,
            color_type: png::RGBA8,
            pixels: pixels,
        };
        let res = png::store_png(&mut img, &output);
        assert!(res.is_ok());

        match (reftest.kind, test_is_flaky) {
            (Same, true) => println!("flaky test - rendering difference: {}", output_str),
            (Same, false) => fail!("rendering difference: {}", output_str),
            (Different, _) => {}   // Result was different and that's what was expected
        }
    } else {
        assert!(test_is_flaky || reftest.kind == Same);
    }
}
<commit_msg>Allow marking tests as flaky by target OS.<commit_after>// Copyright 2013 The Servo Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

extern crate png;
extern crate std;
extern crate test;
extern crate regex;

use std::ascii::StrAsciiExt;
use std::io;
use std::io::{File, Reader, Command};
use std::io::process::ExitStatus;
use std::os;
use test::{AutoColor, DynTestName, DynTestFn, TestDesc, TestOpts, TestDescAndFn};
use test::run_tests_console;
use regex::Regex;

bitflags!(
    flags RenderMode: u32 {
        static CpuRendering  = 0x00000001,
        static GpuRendering  = 0x00000010,
        static LinuxTarget   = 0x00000100,
        static MacOsTarget   = 0x00001000,
        static AndroidTarget = 0x00010000
    }
)

fn main() {
    let args = os::args();
    let mut parts = args.tail().split(|e| "--" == e.as_slice());

    let harness_args = parts.next().unwrap();  // .split() is never empty
    let servo_args = parts.next().unwrap_or(&[]);

    let (render_mode_string, base_path, testname) = match harness_args {
        [] | [_] => fail!("USAGE: cpu|gpu base_path [testname regex]"),
        [ref render_mode_string, ref base_path] => (render_mode_string, base_path, None),
        [ref render_mode_string, ref base_path, ref testname, ..] => (render_mode_string, base_path, Some(Regex::new(testname.as_slice()).unwrap())),
    };

    let mut render_mode = match render_mode_string.as_slice() {
        "cpu" => CpuRendering,
        "gpu" => GpuRendering,
        _ => fail!("First argument must specify cpu or gpu as rendering mode")
    };
    if cfg!(target_os = "linux") {
        render_mode.insert(LinuxTarget);
    }
    if cfg!(target_os = "macos") {
        render_mode.insert(MacOsTarget);
    }
    if cfg!(target_os = "android") {
        render_mode.insert(AndroidTarget);
    }

    let mut all_tests = vec!();
    println!("Scanning {} for manifests\n", base_path);

    for file in io::fs::walk_dir(&Path::new(base_path.as_slice())).unwrap() {
        let maybe_extension = file.extension_str();
        match maybe_extension {
            Some(extension) => {
                if extension.to_ascii_lower().as_slice() == "list" && file.is_file() {
                    let manifest = file.as_str().unwrap();
                    let tests = parse_lists(manifest, servo_args, render_mode);
                    println!("\t{} [{} tests]", manifest, tests.len());
                    all_tests.push_all_move(tests);
                }
            }
            _ => {}
        }
    }

    let test_opts = TestOpts {
        filter: testname,
        run_ignored: false,
        logfile: None,
        run_tests: true,
        run_benchmarks: false,
        ratchet_noise_percent: None,
        ratchet_metrics: None,
        save_metrics: None,
        test_shard: None,
        nocapture: false,
        color: AutoColor
    };

    match run_tests_console(&test_opts, all_tests) {
        Ok(false) => os::set_exit_status(1), // tests failed
        Err(_) => os::set_exit_status(2),    // I/O-related failure
        _ => (),
    }
}

#[deriving(PartialEq)]
enum ReftestKind {
    Same,
    Different,
}

struct Reftest {
    name: String,
    kind: ReftestKind,
    files: [String, ..2],
    id: uint,
    servo_args: Vec<String>,
    render_mode: RenderMode,
    is_flaky: bool,
}

struct TestLine<'a> {
    conditions: &'a str,
    kind: &'a str,
    file_left: &'a str,
    file_right: &'a str,
}

fn parse_lists(file: &str, servo_args: &[String], render_mode: RenderMode) -> Vec<TestDescAndFn> {
    let mut tests = Vec::new();
    let mut next_id = 0;
    let file_path = Path::new(file);
    let contents = File::open_mode(&file_path, io::Open, io::Read)
                       .and_then(|mut f| f.read_to_string())
                       .ok().expect("Could not read file");

    for line in contents.as_slice().lines() {
        // ignore comments or empty lines
        if line.starts_with("#") || line.is_empty() {
            continue;
        }

        let parts: Vec<&str> = line.split(' ').filter(|p| !p.is_empty()).collect();

        let test_line = match parts.len() {
            3 => TestLine {
                conditions: "",
                kind: parts[0],
                file_left: parts[1],
                file_right: parts[2],
            },
            4 => TestLine {
                conditions: parts[0],
                kind: parts[1],
                file_left: parts[2],
                file_right: parts[3],
            },
            _ => fail!("reftest line: '{:s}' doesn't match '[CONDITIONS] KIND LEFT RIGHT'", line),
        };

        let kind = match test_line.kind {
            "==" => Same,
            "!=" => Different,
            part => fail!("reftest line: '{:s}' has invalid kind '{:s}'", line, part)
        };
        let src_path = file_path.dir_path();
        let src_dir = src_path.display().to_string();
        let file_left =  src_dir.clone().append("/").append(test_line.file_left);
        let file_right = src_dir.append("/").append(test_line.file_right);

        let mut conditions_list = test_line.conditions.split(',');
        let mut flakiness = RenderMode::empty();
        for condition in conditions_list {
            match condition {
                "flaky_cpu" => flakiness.insert(CpuRendering),
                "flaky_gpu" => flakiness.insert(GpuRendering),
                "flaky_linux" => flakiness.insert(LinuxTarget),
                "flaky_macos" => flakiness.insert(MacOsTarget),
                _ => (),
            }
        }

        let reftest = Reftest {
            name: test_line.file_left.to_string().append(" / ").append(test_line.file_right),
            kind: kind,
            files: [file_left, file_right],
            id: next_id,
            render_mode: render_mode,
            servo_args: servo_args.iter().map(|x| x.clone()).collect(),
            is_flaky: render_mode.intersects(flakiness),
        };

        next_id += 1;

        tests.push(make_test(reftest));
    }
    tests
}

fn make_test(reftest: Reftest) -> TestDescAndFn {
    let name = reftest.name.clone();
    TestDescAndFn {
        desc: TestDesc {
            name: DynTestName(name),
            ignore: false,
            should_fail: false,
        },
        testfn: DynTestFn(proc() {
            check_reftest(reftest);
        }),
    }
}

fn capture(reftest: &Reftest, side: uint) -> png::Image {
    let filename = format!("/tmp/servo-reftest-{:06u}-{:u}.png", reftest.id, side);
    let mut args = reftest.servo_args.clone();
    // GPU rendering is the default
    if reftest.render_mode.contains(CpuRendering) {
        args.push("-c".to_string());
    }
    args.push_all_move(vec!("-f".to_string(), "-o".to_string(), filename.clone(), reftest.files[side].clone()));

    let retval = match Command::new("./servo").args(args.as_slice()).status() {
        Ok(status) => status,
        Err(e) => fail!("failed to execute process: {}", e),
    };
    assert!(retval == ExitStatus(0));

    png::load_png(&from_str::<Path>(filename.as_slice()).unwrap()).unwrap()
}

fn check_reftest(reftest: Reftest) {
    let left  = capture(&reftest, 0);
    let right = capture(&reftest, 1);

    let pixels = left.pixels.iter().zip(right.pixels.iter()).map(|(&a, &b)| {
        if a as i8 - b as i8 == 0 {
            // White for correct
            0xFF
        } else {
            // "1100" in the RGBA channel with an error for an incorrect value
            // This results in some number of C0 and FFs, which is much more
            // readable (and distinguishable) than the previous difference-wise
            // scaling but does not require reconstructing the actual RGBA pixel.
            0xC0
        }
    }).collect::<Vec<u8>>();

    if pixels.iter().any(|&a| a < 255) {
        let output_str = format!("/tmp/servo-reftest-{:06u}-diff.png", reftest.id);
        let output = from_str::<Path>(output_str.as_slice()).unwrap();

        let mut img = png::Image {
            width: left.width,
            height: left.height,
            color_type: png::RGBA8,
            pixels: pixels,
        };
        let res = png::store_png(&mut img, &output);
        assert!(res.is_ok());

        match (reftest.kind, reftest.is_flaky) {
            (Same, true) => println!("flaky test - rendering difference: {}", output_str),
            (Same, false) => fail!("rendering difference: {}", output_str),
            (Different, _) => {}   // Result was different and that's what was expected
        }
    } else {
        assert!(reftest.is_flaky || reftest.kind == Same);
    }
}
<|endoftext|>


<commit_before>extern crate atom_syndication as atom;

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

use crate::atom::extension::ExtensionMap;
use crate::atom::Feed;

macro_rules! feed {
    ($f:expr) => {{
        let file = File::open($f).unwrap();
        let reader = BufReader::new(file);
        Feed::read_from(reader).unwrap()
    }};
}

#[test]
fn read_feed() {
    let feed = feed!("tests/data/feed.xml");
    assert_eq!(feed.title(), "Feed Title");
    assert_eq!(feed.id(), "urn:uuid:60a76c80-d399-11d9-b91C-0003939e0af6");
    assert_eq!(feed.updated().to_rfc3339(), "2017-06-03T15:15:44-05:00");
    assert_eq!(feed.icon(), Some("http://example.com/icon.png"));
    assert_eq!(feed.logo(), Some("http://example.com/logo.png"));
    assert_eq!(feed.rights(), Some("© 2017 John Doe"));
    assert_eq!(feed.subtitle(), Some("Feed subtitle"));
    assert_eq!(feed.authors().len(), 2);
    assert_eq!(feed.categories().len(), 2);
    assert_eq!(feed.contributors().len(), 2);
    assert!(feed.generator().is_some());
    assert_eq!(feed.links().len(), 2);
}

#[test]
fn read_entry() {
    let feed = feed!("tests/data/entry.xml");
    assert_eq!(feed.entries().len(), 1);

    let entry = feed.entries().first().unwrap();
    assert_eq!(entry.title(), "Entry Title");
    assert_eq!(entry.id(), "http://example.com/article/1");
    assert_eq!(entry.updated().to_rfc3339(), "2017-06-03T15:15:44-05:00");
    assert_eq!(entry.authors().len(), 2);
    assert_eq!(entry.categories().len(), 2);
    assert_eq!(entry.contributors().len(), 2);
    assert_eq!(entry.links().len(), 2);
    assert_eq!(
        entry.published().map(|x| x.to_rfc3339()),
        Some("2017-06-01T15:15:44-05:00".to_string())
    );
    assert_eq!(entry.summary(), Some("Entry summary"));
    assert_eq!(entry.rights(), Some("© 2017 John Doe"));

    let content = entry.content().unwrap();
    assert_eq!(content.value(), Some("Entry content"));
}

#[test]
fn read_category() {
    let feed = feed!("tests/data/category.xml");
    let category = feed.categories().first().unwrap();
    assert_eq!(category.term(), "technology");
    assert_eq!(category.scheme(), Some("http://example.com/scheme"));
    assert_eq!(category.label(), Some("Technology"));
}

#[test]
fn read_generator() {
    let feed = feed!("tests/data/generator.xml");
    let generator = feed.generator().unwrap();
    assert_eq!(generator.value(), "Example Generator");
    assert_eq!(generator.uri(), Some("http://example.com/generator"));
    assert_eq!(generator.version(), Some("1.0"));
}

#[test]
fn read_link() {
    let feed = feed!("tests/data/link.xml");
    let link = feed.links().first().unwrap();
    assert_eq!(link.rel(), "enclosure");
    assert_eq!(link.href(), "http://example.com/audio.mp3");
    assert_eq!(link.hreflang(), Some("en"));
    assert_eq!(link.mime_type(), Some("audio/mpeg"));
    assert_eq!(link.title(), Some("audio"));
    assert_eq!(link.length(), Some("1000"));
}

#[test]
fn read_person() {
    let feed = feed!("tests/data/person.xml");
    let person = feed.authors().first().unwrap();
    assert_eq!(person.name(), "John Doe");
    assert_eq!(person.email(), Some("johndoe@example.com"));
    assert_eq!(person.uri(), Some("http://example.com"));
}

#[test]
fn read_source() {
    let feed = feed!("tests/data/source.xml");

    let entry = feed.entries().first().unwrap();
    assert!(entry.source().is_some());

    let source = entry.source().unwrap();
    assert_eq!(source.title(), "Feed Title");
    assert_eq!(source.id(), "urn:uuid:60a76c80-d399-11d9-b91C-0003939e0af6");
    assert_eq!(source.updated().to_rfc3339(), "2017-06-03T15:15:44-05:00");
    assert_eq!(source.icon(), Some("http://example.com/icon.png"));
    assert_eq!(source.logo(), Some("http://example.com/logo.png"));
    assert_eq!(source.rights(), Some("© 2017 John Doe"));
    assert_eq!(source.subtitle(), Some("Feed subtitle"));
    assert_eq!(source.authors().len(), 2);
    assert_eq!(source.categories().len(), 2);
    assert_eq!(source.contributors().len(), 2);
    assert!(source.generator().is_some());
}

#[test]
fn read_extension() {
    let feed = feed!("tests/data/extension.xml");
    let entry = feed.entries().first().unwrap();

    assert_eq!(
        feed.namespaces().get("ext").map(|s| s.as_str()),
        Some("http://example.com")
    );

    let check_extensions = |extensions: &ExtensionMap| {
        assert!(extensions.contains_key("ext"));
        let map = extensions.get("ext").unwrap();

        assert!(map.contains_key("title"));
        let title = map.get("title").unwrap().first().unwrap();
        assert_eq!(title.value(), Some("Title"));
        assert_eq!(title.attrs().get("type").map(|s| s.as_str()), Some("text"));

        assert!(map.contains_key("parent"));
        let parent = map.get("parent").unwrap().first().unwrap();

        assert!(parent.children().contains_key("child"));
        let child = parent.children().get("child").unwrap().first().unwrap();
        assert_eq!(child.value(), Some("Child"));
    };

    check_extensions(feed.extensions());
    check_extensions(entry.extensions());
}
<commit_msg>fix clippy warning in test<commit_after>extern crate atom_syndication as atom;

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

use crate::atom::extension::ExtensionMap;
use crate::atom::Feed;

macro_rules! feed {
    ($f:expr) => {{
        let file = File::open($f).unwrap();
        let reader = BufReader::new(file);
        Feed::read_from(reader).unwrap()
    }};
}

#[test]
fn read_feed() {
    let feed = feed!("tests/data/feed.xml");
    assert_eq!(feed.title(), "Feed Title");
    assert_eq!(feed.id(), "urn:uuid:60a76c80-d399-11d9-b91C-0003939e0af6");
    assert_eq!(feed.updated().to_rfc3339(), "2017-06-03T15:15:44-05:00");
    assert_eq!(feed.icon(), Some("http://example.com/icon.png"));
    assert_eq!(feed.logo(), Some("http://example.com/logo.png"));
    assert_eq!(feed.rights(), Some("© 2017 John Doe"));
    assert_eq!(feed.subtitle(), Some("Feed subtitle"));
    assert_eq!(feed.authors().len(), 2);
    assert_eq!(feed.categories().len(), 2);
    assert_eq!(feed.contributors().len(), 2);
    assert!(feed.generator().is_some());
    assert_eq!(feed.links().len(), 2);
}

#[test]
fn read_entry() {
    let feed = feed!("tests/data/entry.xml");
    assert_eq!(feed.entries().len(), 1);

    let entry = feed.entries().first().unwrap();
    assert_eq!(entry.title(), "Entry Title");
    assert_eq!(entry.id(), "http://example.com/article/1");
    assert_eq!(entry.updated().to_rfc3339(), "2017-06-03T15:15:44-05:00");
    assert_eq!(entry.authors().len(), 2);
    assert_eq!(entry.categories().len(), 2);
    assert_eq!(entry.contributors().len(), 2);
    assert_eq!(entry.links().len(), 2);
    assert_eq!(
        entry.published().map(chrono::DateTime::to_rfc3339),
        Some("2017-06-01T15:15:44-05:00".to_string())
    );
    assert_eq!(entry.summary(), Some("Entry summary"));
    assert_eq!(entry.rights(), Some("© 2017 John Doe"));

    let content = entry.content().unwrap();
    assert_eq!(content.value(), Some("Entry content"));
}

#[test]
fn read_category() {
    let feed = feed!("tests/data/category.xml");
    let category = feed.categories().first().unwrap();
    assert_eq!(category.term(), "technology");
    assert_eq!(category.scheme(), Some("http://example.com/scheme"));
    assert_eq!(category.label(), Some("Technology"));
}

#[test]
fn read_generator() {
    let feed = feed!("tests/data/generator.xml");
    let generator = feed.generator().unwrap();
    assert_eq!(generator.value(), "Example Generator");
    assert_eq!(generator.uri(), Some("http://example.com/generator"));
    assert_eq!(generator.version(), Some("1.0"));
}

#[test]
fn read_link() {
    let feed = feed!("tests/data/link.xml");
    let link = feed.links().first().unwrap();
    assert_eq!(link.rel(), "enclosure");
    assert_eq!(link.href(), "http://example.com/audio.mp3");
    assert_eq!(link.hreflang(), Some("en"));
    assert_eq!(link.mime_type(), Some("audio/mpeg"));
    assert_eq!(link.title(), Some("audio"));
    assert_eq!(link.length(), Some("1000"));
}

#[test]
fn read_person() {
    let feed = feed!("tests/data/person.xml");
    let person = feed.authors().first().unwrap();
    assert_eq!(person.name(), "John Doe");
    assert_eq!(person.email(), Some("johndoe@example.com"));
    assert_eq!(person.uri(), Some("http://example.com"));
}

#[test]
fn read_source() {
    let feed = feed!("tests/data/source.xml");

    let entry = feed.entries().first().unwrap();
    assert!(entry.source().is_some());

    let source = entry.source().unwrap();
    assert_eq!(source.title(), "Feed Title");
    assert_eq!(source.id(), "urn:uuid:60a76c80-d399-11d9-b91C-0003939e0af6");
    assert_eq!(source.updated().to_rfc3339(), "2017-06-03T15:15:44-05:00");
    assert_eq!(source.icon(), Some("http://example.com/icon.png"));
    assert_eq!(source.logo(), Some("http://example.com/logo.png"));
    assert_eq!(source.rights(), Some("© 2017 John Doe"));
    assert_eq!(source.subtitle(), Some("Feed subtitle"));
    assert_eq!(source.authors().len(), 2);
    assert_eq!(source.categories().len(), 2);
    assert_eq!(source.contributors().len(), 2);
    assert!(source.generator().is_some());
}

#[test]
fn read_extension() {
    let feed = feed!("tests/data/extension.xml");
    let entry = feed.entries().first().unwrap();

    assert_eq!(
        feed.namespaces().get("ext").map(String::as_str),
        Some("http://example.com")
    );

    let check_extensions = |extensions: &ExtensionMap| {
        assert!(extensions.contains_key("ext"));
        let map = extensions.get("ext").unwrap();

        assert!(map.contains_key("title"));
        let title = map.get("title").unwrap().first().unwrap();
        assert_eq!(title.value(), Some("Title"));
        assert_eq!(title.attrs().get("type").map(String::as_str), Some("text"));

        assert!(map.contains_key("parent"));
        let parent = map.get("parent").unwrap().first().unwrap();

        assert!(parent.children().contains_key("child"));
        let child = parent.children().get("child").unwrap().first().unwrap();
        assert_eq!(child.value(), Some("Child"));
    };

    check_extensions(feed.extensions());
    check_extensions(entry.extensions());
}
<|endoftext|>


<commit_before>// Taken from issue #185

fn read_manifest_output() -> String {
    "\
{\
    \"name\":\"foo\",\
    \"version\":\"0.5.0\",\
    \"dependencies\":[],\
    \"targets\":[{\
        \"kind\":[\"bin\"],\
        \"name\":\"foo\",\
        \"src_path\":\"src[..]foo.rs\"\
    }],\\
    <caret>
    \"manifest_path\":\"[..]Cargo.toml\"\
}".into()
}
<commit_msg>(T): fix broken test<commit_after>// Taken from issue #185

fn read_manifest_output() -> String {
    "\
{\
    \"name\":\"foo\",\
    \"version\":\"0.5.0\",\
    \"dependencies\":[],\
    \"targets\":[{\
        \"kind\":[\"bin\"],\
        \"name\":\"foo\",\
        \"src_path\":\"src[..]foo.rs\"\
    }],\
    <caret>
    \"manifest_path\":\"[..]Cargo.toml\"\
}".into()
}
<|endoftext|>


<commit_before>#[cfg(feature = "arbitrary")]
use base::storage::Owned;
#[cfg(feature = "arbitrary")]
use quickcheck::{Arbitrary, Gen};

use num::{One, Zero};
use rand::distributions::{Distribution, Standard};
use rand::Rng;

use alga::general::ClosedAdd;

use base::allocator::Allocator;
use base::dimension::{DimName, U1, U2, U3, U4, U5, U6};
use base::{DefaultAllocator, Scalar, VectorN};

use geometry::Translation;

impl<N: Scalar + Zero, D: DimName> Translation<N, D>
where DefaultAllocator: Allocator<N, D>
{
    /// Creates a new identity translation.
    ///
    /// # Example
    /// ```
    /// # use nalgebra::{Point2, Point3, Translation2, Translation3};
    /// let t = Translation2::identity();
    /// let p = Point2::new(1.0, 2.0);
    /// assert_eq!(t * p, p);
    ///
    /// // Works in all dimensions.
    /// let t = Translation3::identity();
    /// let p = Point3::new(1.0, 2.0, 3.0);
    /// assert_eq!(t * p, p);
    /// ```
    #[inline]
    pub fn identity() -> Translation<N, D> {
        Self::from(VectorN::<N, D>::from_element(N::zero()))
    }
}

impl<N: Scalar + Zero + ClosedAdd, D: DimName> One for Translation<N, D>
where DefaultAllocator: Allocator<N, D>
{
    #[inline]
    fn one() -> Self {
        Self::identity()
    }
}

impl<N: Scalar, D: DimName> Distribution<Translation<N, D>> for Standard
where
    DefaultAllocator: Allocator<N, D>,
    Standard: Distribution<N>,
{
    #[inline]
    fn sample<'a, G: Rng + ?Sized>(&self, rng: &'a mut G) -> Translation<N, D> {
        Translation::from(rng.gen::<VectorN<N, D>>())
    }
}

#[cfg(feature = "arbitrary")]
impl<N: Scalar + Arbitrary, D: DimName> Arbitrary for Translation<N, D>
where
    DefaultAllocator: Allocator<N, D>,
    Owned<N, D>: Send,
{
    #[inline]
    fn arbitrary<G: Gen>(rng: &mut G) -> Self {
        Self::from(Arbitrary::arbitrary(rng))
    }
}

/*
 *
 * Small translation construction from components.
 *
 */
macro_rules! componentwise_constructors_impl(
    ($($doc: expr; $D: ty, $($args: ident:$irow: expr),*);* $(;)*) => {$(
        impl<N: Scalar> Translation<N, $D>
            where DefaultAllocator: Allocator<N, $D> {
            #[doc = "Initializes this translation from its components."]
            #[doc = "# Example\n```"]
            #[doc = $doc]
            #[doc = "```"]
            #[inline]
            pub fn new($($args: N),*) -> Self {
                Self::from(VectorN::<N, $D>::new($($args),*))
            }
        }
    )*}
);

componentwise_constructors_impl!(
    "# use nalgebra::Translation1;\nlet t = Translation1::new(1.0);\nassert!(t.vector.x == 1.0);";
    U1, x:0;
    "# use nalgebra::Translation2;\nlet t = Translation2::new(1.0, 2.0);\nassert!(t.vector.x == 1.0 && t.vector.y == 2.0);";
    U2, x:0, y:1;
    "# use nalgebra::Translation3;\nlet t = Translation3::new(1.0, 2.0, 3.0);\nassert!(t.vector.x == 1.0 && t.vector.y == 2.0 && t.vector.z == 3.0);";
    U3, x:0, y:1, z:2;
    "# use nalgebra::Translation4;\nlet t = Translation4::new(1.0, 2.0, 3.0, 4.0);\nassert!(t.vector.x == 1.0 && t.vector.y == 2.0 && t.vector.z == 3.0 && t.vector.w == 4.0);";
    U4, x:0, y:1, z:2, w:3;
    "# use nalgebra::Translation5;\nlet t = Translation5::new(1.0, 2.0, 3.0, 4.0, 5.0);\nassert!(t.vector.x == 1.0 && t.vector.y == 2.0 && t.vector.z == 3.0 && t.vector.w == 4.0 && t.vector.a == 5.0);";
    U5, x:0, y:1, z:2, w:3, a:4;
    "# use nalgebra::Translation6;\nlet t = Translation6::new(1.0, 2.0, 3.0, 4.0, 5.0, 6.0);\nassert!(t.vector.x == 1.0 && t.vector.y == 2.0 && t.vector.z == 3.0 && t.vector.w == 4.0 && t.vector.a == 5.0 && t.vector.b == 6.0);";
    U6, x:0, y:1, z:2, w:3, a:4, b:5;
);
<commit_msg>Fix missing type annotation.<commit_after>#[cfg(feature = "arbitrary")]
use base::storage::Owned;
#[cfg(feature = "arbitrary")]
use quickcheck::{Arbitrary, Gen};

use num::{One, Zero};
use rand::distributions::{Distribution, Standard};
use rand::Rng;

use alga::general::ClosedAdd;

use base::allocator::Allocator;
use base::dimension::{DimName, U1, U2, U3, U4, U5, U6};
use base::{DefaultAllocator, Scalar, VectorN};

use geometry::Translation;

impl<N: Scalar + Zero, D: DimName> Translation<N, D>
where DefaultAllocator: Allocator<N, D>
{
    /// Creates a new identity translation.
    ///
    /// # Example
    /// ```
    /// # use nalgebra::{Point2, Point3, Translation2, Translation3};
    /// let t = Translation2::identity();
    /// let p = Point2::new(1.0, 2.0);
    /// assert_eq!(t * p, p);
    ///
    /// // Works in all dimensions.
    /// let t = Translation3::identity();
    /// let p = Point3::new(1.0, 2.0, 3.0);
    /// assert_eq!(t * p, p);
    /// ```
    #[inline]
    pub fn identity() -> Translation<N, D> {
        Self::from(VectorN::<N, D>::from_element(N::zero()))
    }
}

impl<N: Scalar + Zero + ClosedAdd, D: DimName> One for Translation<N, D>
where DefaultAllocator: Allocator<N, D>
{
    #[inline]
    fn one() -> Self {
        Self::identity()
    }
}

impl<N: Scalar, D: DimName> Distribution<Translation<N, D>> for Standard
where
    DefaultAllocator: Allocator<N, D>,
    Standard: Distribution<N>,
{
    #[inline]
    fn sample<'a, G: Rng + ?Sized>(&self, rng: &'a mut G) -> Translation<N, D> {
        Translation::from(rng.gen::<VectorN<N, D>>())
    }
}

#[cfg(feature = "arbitrary")]
impl<N: Scalar + Arbitrary, D: DimName> Arbitrary for Translation<N, D>
where
    DefaultAllocator: Allocator<N, D>,
    Owned<N, D>: Send,
{
    #[inline]
    fn arbitrary<G: Gen>(rng: &mut G) -> Self {
        let v: VectorN<N, D> = Arbitrary::arbitrary(rng);
        Self::from(v)
    }
}

/*
 *
 * Small translation construction from components.
 *
 */
macro_rules! componentwise_constructors_impl(
    ($($doc: expr; $D: ty, $($args: ident:$irow: expr),*);* $(;)*) => {$(
        impl<N: Scalar> Translation<N, $D>
            where DefaultAllocator: Allocator<N, $D> {
            #[doc = "Initializes this translation from its components."]
            #[doc = "# Example\n```"]
            #[doc = $doc]
            #[doc = "```"]
            #[inline]
            pub fn new($($args: N),*) -> Self {
                Self::from(VectorN::<N, $D>::new($($args),*))
            }
        }
    )*}
);

componentwise_constructors_impl!(
    "# use nalgebra::Translation1;\nlet t = Translation1::new(1.0);\nassert!(t.vector.x == 1.0);";
    U1, x:0;
    "# use nalgebra::Translation2;\nlet t = Translation2::new(1.0, 2.0);\nassert!(t.vector.x == 1.0 && t.vector.y == 2.0);";
    U2, x:0, y:1;
    "# use nalgebra::Translation3;\nlet t = Translation3::new(1.0, 2.0, 3.0);\nassert!(t.vector.x == 1.0 && t.vector.y == 2.0 && t.vector.z == 3.0);";
    U3, x:0, y:1, z:2;
    "# use nalgebra::Translation4;\nlet t = Translation4::new(1.0, 2.0, 3.0, 4.0);\nassert!(t.vector.x == 1.0 && t.vector.y == 2.0 && t.vector.z == 3.0 && t.vector.w == 4.0);";
    U4, x:0, y:1, z:2, w:3;
    "# use nalgebra::Translation5;\nlet t = Translation5::new(1.0, 2.0, 3.0, 4.0, 5.0);\nassert!(t.vector.x == 1.0 && t.vector.y == 2.0 && t.vector.z == 3.0 && t.vector.w == 4.0 && t.vector.a == 5.0);";
    U5, x:0, y:1, z:2, w:3, a:4;
    "# use nalgebra::Translation6;\nlet t = Translation6::new(1.0, 2.0, 3.0, 4.0, 5.0, 6.0);\nassert!(t.vector.x == 1.0 && t.vector.y == 2.0 && t.vector.z == 3.0 && t.vector.w == 4.0 && t.vector.a == 5.0 && t.vector.b == 6.0);";
    U6, x:0, y:1, z:2, w:3, a:4, b:5;
);
<|endoftext|>


<commit_before>extern crate syntex;
extern crate rust_swig;
extern crate env_logger;
extern crate bindgen;

use std::env;
use std::path::{Path, PathBuf};

fn main() {
    env_logger::init().unwrap();

    let java_home = env::var("JAVA_HOME").expect("JAVA_HOME env variable not settted");

    let java_include_dir = Path::new(&java_home).join("include");

    let target = env::var("TARGET").expect("target env var not setted");
    let java_sys_include_dir = java_include_dir.join(if target.contains("windows") {
        "win32"
    } else {
        "linux"
    });

    let include_dirs = [java_include_dir, java_sys_include_dir];
    println!("jni include dirs {:?}", include_dirs);
    for dir in &include_dirs {
        println!("cargo:rerun-if-changed={}", dir.display());
    }

    let jni_h_path =
        search_file_in_directory(&include_dirs[..], "jni.h").expect("Can not find jni.h");

    let out_dir = env::var("OUT_DIR").unwrap();

    gen_binding(
        &include_dirs[..],
        &jni_h_path,
        &Path::new(&out_dir).join("jni_c_header.rs"),
    ).unwrap();

    rust_swig_expand(
        Path::new("src/lib.rs.in"),
        &Path::new(&out_dir).join("lib.rs"),
    ).unwrap();
    println!("cargo:rerun-if-changed=src");

    //rebuild if user remove generated code
    println!("cargo:rerun-if-changed={}", out_dir);
}

fn search_file_in_directory<P: AsRef<Path>>(dirs: &[P], file: &str) -> Result<PathBuf, ()> {
    for dir in dirs {
        let dir = dir.as_ref().to_path_buf();
        let file_path = dir.join(file);
        if file_path.exists() && file_path.is_file() {
            return Ok(file_path);
        }
    }
    Err(())
}

fn gen_binding<P: AsRef<Path>>(
    include_dirs: &[P],
    c_file_path: &Path,
    output_rust: &Path,
) -> Result<(), String> {
    let mut bindings: ::bindgen::Builder = bindgen::builder().header(c_file_path.to_str().unwrap());
    bindings = include_dirs.iter().fold(bindings, |acc, x| {
        acc.clang_arg("-I".to_string() + x.as_ref().to_str().unwrap())
    });

    bindings = bindings.unstable_rust(false);

    let generated_bindings = bindings
        .generate()
        .map_err(|_| "Failed to generate bindings".to_string())?;
    generated_bindings
        .write_to_file(output_rust)
        .map_err(|err| err.to_string())?;

    Ok(())
}

fn rust_swig_expand(from: &Path, out: &Path) -> Result<(), String> {
    println!("Run rust_swig_expand");
    let mut registry = syntex::Registry::new();
    let swig_gen = rust_swig::Generator::new(rust_swig::LanguageConfig::Java {
        output_dir: Path::new("java").join("com").join("example").join("rust"),
        package_name: "com.example.rust".into(),
    });
    swig_gen.register(&mut registry);
    registry
        .expand("rust_swig_test_jni", from, out)
        .map_err(|err| format!("rust swig macros expand failed: {}", err))
}
<commit_msg>print rust swig expand time<commit_after>extern crate syntex;
extern crate rust_swig;
extern crate env_logger;
extern crate bindgen;

use std::env;
use std::time::Instant;
use std::path::{Path, PathBuf};

fn main() {
    env_logger::init().unwrap();

    let java_home = env::var("JAVA_HOME").expect("JAVA_HOME env variable not settted");

    let java_include_dir = Path::new(&java_home).join("include");

    let target = env::var("TARGET").expect("target env var not setted");
    let java_sys_include_dir = java_include_dir.join(if target.contains("windows") {
        "win32"
    } else {
        "linux"
    });

    let include_dirs = [java_include_dir, java_sys_include_dir];
    println!("jni include dirs {:?}", include_dirs);
    for dir in &include_dirs {
        println!("cargo:rerun-if-changed={}", dir.display());
    }

    let jni_h_path =
        search_file_in_directory(&include_dirs[..], "jni.h").expect("Can not find jni.h");

    let out_dir = env::var("OUT_DIR").unwrap();

    gen_binding(
        &include_dirs[..],
        &jni_h_path,
        &Path::new(&out_dir).join("jni_c_header.rs"),
    ).unwrap();

    let now = Instant::now();
    rust_swig_expand(
        Path::new("src/lib.rs.in"),
        &Path::new(&out_dir).join("lib.rs"),
    ).unwrap();
    let expand_time = now.elapsed();
    println!(
        "rust swig expand time: {}",
        expand_time.as_secs() as f64 + (expand_time.subsec_nanos() as f64) / 1_000_000_000.
    );
    println!("cargo:rerun-if-changed=src");
    //rebuild if user remove generated code
    println!("cargo:rerun-if-changed={}", out_dir);
}

fn search_file_in_directory<P: AsRef<Path>>(dirs: &[P], file: &str) -> Result<PathBuf, ()> {
    for dir in dirs {
        let dir = dir.as_ref().to_path_buf();
        let file_path = dir.join(file);
        if file_path.exists() && file_path.is_file() {
            return Ok(file_path);
        }
    }
    Err(())
}

fn gen_binding<P: AsRef<Path>>(
    include_dirs: &[P],
    c_file_path: &Path,
    output_rust: &Path,
) -> Result<(), String> {
    let mut bindings: ::bindgen::Builder = bindgen::builder().header(c_file_path.to_str().unwrap());
    bindings = include_dirs.iter().fold(bindings, |acc, x| {
        acc.clang_arg("-I".to_string() + x.as_ref().to_str().unwrap())
    });

    bindings = bindings.unstable_rust(false);

    let generated_bindings = bindings
        .generate()
        .map_err(|_| "Failed to generate bindings".to_string())?;
    generated_bindings
        .write_to_file(output_rust)
        .map_err(|err| err.to_string())?;

    Ok(())
}

fn rust_swig_expand(from: &Path, out: &Path) -> Result<(), String> {
    println!("Run rust_swig_expand");
    let mut registry = syntex::Registry::new();
    let swig_gen = rust_swig::Generator::new(rust_swig::LanguageConfig::Java {
        output_dir: Path::new("java").join("com").join("example").join("rust"),
        package_name: "com.example.rust".into(),
    });
    swig_gen.register(&mut registry);
    registry
        .expand("rust_swig_test_jni", from, out)
        .map_err(|err| format!("rust swig macros expand failed: {}", err))
}
<|endoftext|>


<commit_before>#[cfg(feature = "bigdecimal")]
mod bigdecimal {
    extern crate bigdecimal;
    extern crate num_bigint;
    extern crate num_integer;
    extern crate num_traits;

    use self::bigdecimal::BigDecimal;
    use self::num_bigint::{BigInt, BigUint, Sign};
    use self::num_integer::Integer;
    use self::num_traits::{Signed, ToPrimitive, Zero};
    use std::io::prelude::*;

    use deserialize::{self, FromSql};
    use pg::data_types::PgNumeric;
    use pg::Pg;
    use serialize::{self, Output, ToSql};
    use sql_types::Numeric;

    use std::convert::{TryFrom, TryInto};
    use std::error::Error;

    /// Iterator over the digits of a big uint in base 10k.
    /// The digits will be returned in little endian order.
    struct ToBase10000(Option<BigUint>);

    impl Iterator for ToBase10000 {
        type Item = i16;

        fn next(&mut self) -> Option<Self::Item> {
            self.0.take().map(|v| {
                let (div, rem) = v.div_rem(&BigUint::from(10_000u16));
                if !div.is_zero() {
                    self.0 = Some(div);
                }
                rem.to_i16().expect("10000 always fits in an i16")
            })
        }
    }

    fn pg_decimal_to_bigdecimal(numeric: &PgNumeric) -> deserialize::Result<BigDecimal> {
        let (sign, weight, scale, digits) = match *numeric {
            PgNumeric::Positive {
                weight,
                scale,
                ref digits,
            } => (Sign::Plus, weight, scale, digits),
            PgNumeric::Negative {
                weight,
                scale,
                ref digits,
            } => (Sign::Minus, weight, scale, digits),
            PgNumeric::NaN => return Err(Box::from("NaN is not (yet) supported in BigDecimal")),
        };

        let mut result = BigUint::default();
        let count = digits.len() as i64;
        for digit in digits {
            result *= BigUint::from(10_000u64);
            result += BigUint::from(*digit as u64);
        }
        // First digit got factor 10_000^(digits.len() - 1), but should get 10_000^weight
        let correction_exp = 4 * (i64::from(weight) - count + 1);
        let result = BigDecimal::new(BigInt::from_biguint(sign, result), -correction_exp)
            .with_scale(i64::from(scale));
        Ok(result)
    }

    impl<'a> TryFrom<&'a PgNumeric> for BigDecimal {
        type Error = Box<Error + Send + Sync>;

        fn try_from(numeric: &'a PgNumeric) -> deserialize::Result<Self> {
            pg_decimal_to_bigdecimal(numeric)
        }
    }

    impl TryFrom<PgNumeric> for BigDecimal {
        type Error = Box<Error + Send + Sync>;

        fn try_from(numeric: PgNumeric) -> deserialize::Result<Self> {
            (&numeric).try_into()
        }
    }

    impl<'a> From<&'a BigDecimal> for PgNumeric {
        // NOTE(clippy): No `std::ops::MulAssign` impl for `BigInt`
        #[allow(clippy::assign_op_pattern)]
        fn from(decimal: &'a BigDecimal) -> Self {
            let (mut integer, scale) = decimal.as_bigint_and_exponent();
            let scale = scale as u16;
            integer = integer.abs();

            // Ensure that the decimal will always lie on a digit boundary
            for _ in 0..(4 - scale % 4) {
                integer = integer * 10;
            }
            let integer = integer.to_biguint().expect("integer is always positive");

            let mut digits = ToBase10000(Some(integer)).collect::<Vec<_>>();
            digits.reverse();
            let digits_after_decimal = scale as u16 / 4 + 1;
            let weight = digits.len() as i16 - digits_after_decimal as i16 - 1;

            let unnecessary_zeroes = if weight >= 0 {
                let index_of_decimal = (weight + 1) as usize;
                digits
                    .get(index_of_decimal..)
                    .expect("enough digits exist")
                    .iter()
                    .rev()
                    .take_while(Zero::is_zero)
                    .count()
            } else {
                0
            };

            let relevant_digits = digits.len() - unnecessary_zeroes;
            digits.truncate(relevant_digits);

            match decimal.sign() {
                Sign::Plus => PgNumeric::Positive {
                    digits,
                    scale,
                    weight,
                },
                Sign::Minus => PgNumeric::Negative {
                    digits,
                    scale,
                    weight,
                },
                Sign::NoSign => PgNumeric::Positive {
                    digits: vec![0],
                    scale: 0,
                    weight: 0,
                },
            }
        }
    }

    impl From<BigDecimal> for PgNumeric {
        fn from(bigdecimal: BigDecimal) -> Self {
            (&bigdecimal).into()
        }
    }

    impl ToSql<Numeric, Pg> for BigDecimal {
        fn to_sql<W: Write>(&self, out: &mut Output<W, Pg>) -> serialize::Result {
            let numeric = PgNumeric::from(self);
            ToSql::<Numeric, Pg>::to_sql(&numeric, out)
        }
    }

    impl FromSql<Numeric, Pg> for BigDecimal {
        fn from_sql(numeric: Option<&[u8]>) -> deserialize::Result<Self> {
            let numeric = PgNumeric::from_sql(numeric)?;
            pg_decimal_to_bigdecimal(&numeric)
        }
    }

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

        #[test]
        fn bigdecimal_to_pgnumeric_converts_digits_to_base_10000() {
            let decimal = BigDecimal::from_str("1").unwrap();
            let expected = PgNumeric::Positive {
                weight: 0,
                scale: 0,
                digits: vec![1],
            };
            assert_eq!(expected, decimal.into());

            let decimal = BigDecimal::from_str("10").unwrap();
            let expected = PgNumeric::Positive {
                weight: 0,
                scale: 0,
                digits: vec![10],
            };
            assert_eq!(expected, decimal.into());

            let decimal = BigDecimal::from_str("10000").unwrap();
            let expected = PgNumeric::Positive {
                weight: 1,
                scale: 0,
                digits: vec![1, 0],
            };
            assert_eq!(expected, decimal.into());

            let decimal = BigDecimal::from_str("10001").unwrap();
            let expected = PgNumeric::Positive {
                weight: 1,
                scale: 0,
                digits: vec![1, 1],
            };
            assert_eq!(expected, decimal.into());

            let decimal = BigDecimal::from_str("100000000").unwrap();
            let expected = PgNumeric::Positive {
                weight: 2,
                scale: 0,
                digits: vec![1, 0, 0],
            };
            assert_eq!(expected, decimal.into());
        }

        #[test]
        fn bigdecimal_to_pg_numeric_properly_adjusts_scale() {
            let decimal = BigDecimal::from_str("1").unwrap();
            let expected = PgNumeric::Positive {
                weight: 0,
                scale: 0,
                digits: vec![1],
            };
            assert_eq!(expected, decimal.into());

            let decimal = BigDecimal::from_str("1.0").unwrap();
            let expected = PgNumeric::Positive {
                weight: 0,
                scale: 1,
                digits: vec![1],
            };
            assert_eq!(expected, decimal.into());

            let decimal = BigDecimal::from_str("1.1").unwrap();
            let expected = PgNumeric::Positive {
                weight: 0,
                scale: 1,
                digits: vec![1, 1000],
            };
            assert_eq!(expected, decimal.into());

            let decimal = BigDecimal::from_str("1.10").unwrap();
            let expected = PgNumeric::Positive {
                weight: 0,
                scale: 2,
                digits: vec![1, 1000],
            };
            assert_eq!(expected, decimal.into());

            let decimal = BigDecimal::from_str("100000000.0001").unwrap();
            let expected = PgNumeric::Positive {
                weight: 2,
                scale: 4,
                digits: vec![1, 0, 0, 1],
            };
            assert_eq!(expected, decimal.into());

            let decimal = BigDecimal::from_str("0.1").unwrap();
            let expected = PgNumeric::Positive {
                weight: -1,
                scale: 1,
                digits: vec![1000],
            };
            assert_eq!(expected, decimal.into());
        }

        #[test]
        fn bigdecimal_to_pg_numeric_retains_sign() {
            let decimal = BigDecimal::from_str("123.456").unwrap();
            let expected = PgNumeric::Positive {
                weight: 0,
                scale: 3,
                digits: vec![123, 4560],
            };
            assert_eq!(expected, decimal.into());

            let decimal = BigDecimal::from_str("-123.456").unwrap();
            let expected = PgNumeric::Negative {
                weight: 0,
                scale: 3,
                digits: vec![123, 4560],
            };
            assert_eq!(expected, decimal.into());
        }

        #[test]
        #[cfg(feature = "unstable")]
        fn pg_numeric_to_bigdecimal_works() {
            let expected = BigDecimal::from_str("123.456").unwrap();
            let pg_numeric = PgNumeric::Positive {
                weight: 0,
                scale: 3,
                digits: vec![123, 4560],
            };
            let res: BigDecimal = pg_numeric.try_into().unwrap();
            assert_eq!(res, expected);

            let expected = BigDecimal::from_str("-56.78").unwrap();
            let pg_numeric = PgNumeric::Negative {
                weight: 0,
                scale: 2,
                digits: vec![56, 7800],
            };
            let res: BigDecimal = pg_numeric.try_into().unwrap();
            assert_eq!(res, expected);
        }
    }
}
<commit_msg>Cleanup from_sql implementation of BigDecimal<commit_after>#[cfg(feature = "bigdecimal")]
mod bigdecimal {
    extern crate bigdecimal;
    extern crate num_bigint;
    extern crate num_integer;
    extern crate num_traits;

    use self::bigdecimal::BigDecimal;
    use self::num_bigint::{BigInt, BigUint, Sign};
    use self::num_integer::Integer;
    use self::num_traits::{Signed, ToPrimitive, Zero};
    use std::io::prelude::*;

    use deserialize::{self, FromSql};
    use pg::data_types::PgNumeric;
    use pg::Pg;
    use serialize::{self, Output, ToSql};
    use sql_types::Numeric;

    use std::convert::{TryFrom, TryInto};
    use std::error::Error;

    /// Iterator over the digits of a big uint in base 10k.
    /// The digits will be returned in little endian order.
    struct ToBase10000(Option<BigUint>);

    impl Iterator for ToBase10000 {
        type Item = i16;

        fn next(&mut self) -> Option<Self::Item> {
            self.0.take().map(|v| {
                let (div, rem) = v.div_rem(&BigUint::from(10_000u16));
                if !div.is_zero() {
                    self.0 = Some(div);
                }
                rem.to_i16().expect("10000 always fits in an i16")
            })
        }
    }

    impl<'a> TryFrom<&'a PgNumeric> for BigDecimal {
        type Error = Box<Error + Send + Sync>;

        fn try_from(numeric: &'a PgNumeric) -> deserialize::Result<Self> {
            let (sign, weight, scale, digits) = match *numeric {
                PgNumeric::Positive {
                    weight,
                    scale,
                    ref digits,
                } => (Sign::Plus, weight, scale, digits),
                PgNumeric::Negative {
                    weight,
                    scale,
                    ref digits,
                } => (Sign::Minus, weight, scale, digits),
                PgNumeric::NaN => {
                    return Err(Box::from("NaN is not (yet) supported in BigDecimal"))
                }
            };

            let mut result = BigUint::default();
            let count = digits.len() as i64;
            for digit in digits {
                result *= BigUint::from(10_000u64);
                result += BigUint::from(*digit as u64);
            }
            // First digit got factor 10_000^(digits.len() - 1), but should get 10_000^weight
            let correction_exp = 4 * (i64::from(weight) - count + 1);
            let result = BigDecimal::new(BigInt::from_biguint(sign, result), -correction_exp)
                .with_scale(i64::from(scale));
            Ok(result)
        }
    }

    impl TryFrom<PgNumeric> for BigDecimal {
        type Error = Box<Error + Send + Sync>;

        fn try_from(numeric: PgNumeric) -> deserialize::Result<Self> {
            (&numeric).try_into()
        }
    }

    impl<'a> From<&'a BigDecimal> for PgNumeric {
        // NOTE(clippy): No `std::ops::MulAssign` impl for `BigInt`
        #[allow(clippy::assign_op_pattern)]
        fn from(decimal: &'a BigDecimal) -> Self {
            let (mut integer, scale) = decimal.as_bigint_and_exponent();
            let scale = scale as u16;
            integer = integer.abs();

            // Ensure that the decimal will always lie on a digit boundary
            for _ in 0..(4 - scale % 4) {
                integer = integer * 10;
            }
            let integer = integer.to_biguint().expect("integer is always positive");

            let mut digits = ToBase10000(Some(integer)).collect::<Vec<_>>();
            digits.reverse();
            let digits_after_decimal = scale as u16 / 4 + 1;
            let weight = digits.len() as i16 - digits_after_decimal as i16 - 1;

            let unnecessary_zeroes = if weight >= 0 {
                let index_of_decimal = (weight + 1) as usize;
                digits
                    .get(index_of_decimal..)
                    .expect("enough digits exist")
                    .iter()
                    .rev()
                    .take_while(Zero::is_zero)
                    .count()
            } else {
                0
            };

            let relevant_digits = digits.len() - unnecessary_zeroes;
            digits.truncate(relevant_digits);

            match decimal.sign() {
                Sign::Plus => PgNumeric::Positive {
                    digits,
                    scale,
                    weight,
                },
                Sign::Minus => PgNumeric::Negative {
                    digits,
                    scale,
                    weight,
                },
                Sign::NoSign => PgNumeric::Positive {
                    digits: vec![0],
                    scale: 0,
                    weight: 0,
                },
            }
        }
    }

    impl From<BigDecimal> for PgNumeric {
        fn from(bigdecimal: BigDecimal) -> Self {
            (&bigdecimal).into()
        }
    }

    impl ToSql<Numeric, Pg> for BigDecimal {
        fn to_sql<W: Write>(&self, out: &mut Output<W, Pg>) -> serialize::Result {
            let numeric = PgNumeric::from(self);
            ToSql::<Numeric, Pg>::to_sql(&numeric, out)
        }
    }

    impl FromSql<Numeric, Pg> for BigDecimal {
        fn from_sql(numeric: Option<&[u8]>) -> deserialize::Result<Self> {
            PgNumeric::from_sql(numeric)?.try_into()
        }
    }

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

        #[test]
        fn bigdecimal_to_pgnumeric_converts_digits_to_base_10000() {
            let decimal = BigDecimal::from_str("1").unwrap();
            let expected = PgNumeric::Positive {
                weight: 0,
                scale: 0,
                digits: vec![1],
            };
            assert_eq!(expected, decimal.into());

            let decimal = BigDecimal::from_str("10").unwrap();
            let expected = PgNumeric::Positive {
                weight: 0,
                scale: 0,
                digits: vec![10],
            };
            assert_eq!(expected, decimal.into());

            let decimal = BigDecimal::from_str("10000").unwrap();
            let expected = PgNumeric::Positive {
                weight: 1,
                scale: 0,
                digits: vec![1, 0],
            };
            assert_eq!(expected, decimal.into());

            let decimal = BigDecimal::from_str("10001").unwrap();
            let expected = PgNumeric::Positive {
                weight: 1,
                scale: 0,
                digits: vec![1, 1],
            };
            assert_eq!(expected, decimal.into());

            let decimal = BigDecimal::from_str("100000000").unwrap();
            let expected = PgNumeric::Positive {
                weight: 2,
                scale: 0,
                digits: vec![1, 0, 0],
            };
            assert_eq!(expected, decimal.into());
        }

        #[test]
        fn bigdecimal_to_pg_numeric_properly_adjusts_scale() {
            let decimal = BigDecimal::from_str("1").unwrap();
            let expected = PgNumeric::Positive {
                weight: 0,
                scale: 0,
                digits: vec![1],
            };
            assert_eq!(expected, decimal.into());

            let decimal = BigDecimal::from_str("1.0").unwrap();
            let expected = PgNumeric::Positive {
                weight: 0,
                scale: 1,
                digits: vec![1],
            };
            assert_eq!(expected, decimal.into());

            let decimal = BigDecimal::from_str("1.1").unwrap();
            let expected = PgNumeric::Positive {
                weight: 0,
                scale: 1,
                digits: vec![1, 1000],
            };
            assert_eq!(expected, decimal.into());

            let decimal = BigDecimal::from_str("1.10").unwrap();
            let expected = PgNumeric::Positive {
                weight: 0,
                scale: 2,
                digits: vec![1, 1000],
            };
            assert_eq!(expected, decimal.into());

            let decimal = BigDecimal::from_str("100000000.0001").unwrap();
            let expected = PgNumeric::Positive {
                weight: 2,
                scale: 4,
                digits: vec![1, 0, 0, 1],
            };
            assert_eq!(expected, decimal.into());

            let decimal = BigDecimal::from_str("0.1").unwrap();
            let expected = PgNumeric::Positive {
                weight: -1,
                scale: 1,
                digits: vec![1000],
            };
            assert_eq!(expected, decimal.into());
        }

        #[test]
        fn bigdecimal_to_pg_numeric_retains_sign() {
            let decimal = BigDecimal::from_str("123.456").unwrap();
            let expected = PgNumeric::Positive {
                weight: 0,
                scale: 3,
                digits: vec![123, 4560],
            };
            assert_eq!(expected, decimal.into());

            let decimal = BigDecimal::from_str("-123.456").unwrap();
            let expected = PgNumeric::Negative {
                weight: 0,
                scale: 3,
                digits: vec![123, 4560],
            };
            assert_eq!(expected, decimal.into());
        }

        #[test]
        #[cfg(feature = "unstable")]
        fn pg_numeric_to_bigdecimal_works() {
            let expected = BigDecimal::from_str("123.456").unwrap();
            let pg_numeric = PgNumeric::Positive {
                weight: 0,
                scale: 3,
                digits: vec![123, 4560],
            };
            let res: BigDecimal = pg_numeric.try_into().unwrap();
            assert_eq!(res, expected);

            let expected = BigDecimal::from_str("-56.78").unwrap();
            let pg_numeric = PgNumeric::Negative {
                weight: 0,
                scale: 2,
                digits: vec![56, 7800],
            };
            let res: BigDecimal = pg_numeric.try_into().unwrap();
            assert_eq!(res, expected);
        }
    }
}
<|endoftext|>