<commit_before>extern crate rand;

pub use self::vec3d::Vec3d;
pub use self::ray::Ray;

use rand::Rng;

use std::f64::consts::PI;

mod vec3d;
mod ray;

pub enum Material {
    Diffuse,
    Specular,
    Refractive
}

pub struct Sphere {
    material: Material,
    radius: f64,
    position: Vec3d,
    emission: Vec3d,
    colour: Vec3d
}

impl Sphere {
    pub fn new(material: Material, radius: f64, position: Vec3d, emission: Vec3d, colour: Vec3d) -> Sphere {
        Sphere {
          material: material,
          radius: radius,
          position: position,
          emission: emission,
          colour: colour
        }
    }
    fn intersect(&self, ray: &Ray) -> Option<f64> {
        let op = self.position - ray.origin;
        let b = op.dot(ray.direction);
        let determinant = b * b - op.dot(op) + self.radius * self.radius;
        if determinant < 0.0 { return None; }
        let determinant = determinant.sqrt();
        let t1 = b - determinant;
        let t2 = b + determinant;
        const EPSILON : f64 = 0.0001;
        if t1 > EPSILON {
            Some(t1)
        } else if t2 > EPSILON {
            Some(t2)
        } else {
            None
        }
    }
}

#[test]
fn intersection() {
    let sphere = Sphere::new(
        Material::Diffuse,
        100.0,
        Vec3d::new(0.0, 0.0, 200.0),
        Vec3d::zero(),
        Vec3d::zero());
    let ray = Ray::new(Vec3d::zero(), Vec3d::new(0.0, 0.0, 1.0));
    match sphere.intersect(&ray) {
        Some(x) => assert_eq!(x, 100.0),
        None => panic!("unexpected")
    }
    let ray = Ray::new(Vec3d::zero(), Vec3d::new(0.0, 1.0, 0.0));
    match sphere.intersect(&ray) {
        Some(_) => panic!("unexpected"),
        None => {}
    }
}

pub struct HitRecord<'a> {
    sphere: &'a Sphere,
    dist: f64
}

pub fn intersect<'a>(scene: &'a [Sphere], ray: &Ray) -> Option<HitRecord<'a>> {
    let mut result : Option<HitRecord<'a>> = None;
    for sph in scene {
        if let Some(dist) = sph.intersect(&ray) {
            if result.as_ref().map_or(true, |x| dist < x.dist) {
                result = Some(HitRecord { sphere: &sph, dist: dist });
            }
        }
    }
    result
}

pub fn radiance<R: Rng>(scene: &[Sphere], ray: &Ray, depth: i32, rng: &mut R) -> Vec3d {
    intersect(&scene, &ray).map_or(Vec3d::zero(), |hit| {
        let hit_pos = ray.origin + ray.direction * hit.dist;
        let hit_normal = (hit_pos - hit.sphere.position).normalized();
        let n1 = if hit_normal.dot(ray.direction) < 0.0 { hit_normal } else { hit_normal.neg() };
        let mut colour = hit.sphere.colour;
        let max_reflectance = colour.max_component();
        let depth = depth + 1;
        if depth > 5 {
            let rand = rng.gen::<f64>();
            if rand < max_reflectance && depth < 500 { // Rust's stack blows up ~600 on my machine
               colour = colour * (1.0 / max_reflectance);
            } else {
                return hit.sphere.emission;
            }
        }
        match hit.sphere.material {
            Material::Diffuse => {
                // Get a random polar coordinate
                let r1 = rng.gen::<f64>() * 2.0 * PI;
                let r2 = rng.gen::<f64>();
                let r2s = r2.sqrt();
                // Create a coordinate system u,v,w local to the point, where the w is the normal
                // pointing out of the sphere and the u and v are orthonormal to w.
                let w = n1;
                // Pick an arbitrary non-zero preferred axis for u
                let u = if n1.x.abs() > 0.1 { Vec3d::new(0.0, 1.0, 0.0) } else { Vec3d::new(1.0, 0.0, 0.0) }.cross(w);
                let v = w.cross(u);
                // construct the new direction
                let new_dir = u * r1.cos() * r2s + v * r1.sin() * r2s + w * (1.0 - r2).sqrt();
                colour = colour * radiance(scene, &Ray::new(hit_pos, new_dir.normalized()), depth, rng);
            },
            Material::Specular => {
                let reflection = ray.direction - hit_normal * 2.0 * hit_normal.dot(ray.direction);
                let reflected_ray = Ray::new(hit_pos, reflection);
                colour = colour * radiance(scene, &reflected_ray, depth, rng);
            },
            Material::Refractive => {
                let reflection = ray.direction - hit_normal * 2.0 * hit_normal.dot(ray.direction);
                let reflected_ray = Ray::new(hit_pos, reflection);
                let into = hit_normal.dot(n1) > 0.0;
                let nc = 1.0;
                let nt = 1.5;
                let nnt = if into { nc/nt } else { nt/nc };
                let ddn = ray.direction.dot(n1);
                let cos2t = 1.0 - nnt * nnt * (1.0 - ddn * ddn);
                if cos2t < 0.0 {
                    // Total internal reflection
                    colour = colour * radiance(scene, &reflected_ray, depth, rng);
                } else {
                    let tbd = ddn * nnt + cos2t.sqrt();
                    let tbd = if into { tbd } else { -tbd };
                    let tdir = (ray.direction * nnt - hit_normal * tbd).normalized();
                    let transmitted_ray = Ray::new(hit_pos, tdir);
                    let a = nt - nc;
                    let b = nt + nc;
                    let r0 = (a * a) / (b * b);
                    let c = 1.0 - if into { -ddn } else { tdir.dot(hit_normal) };
                    let re = r0 + (1.0 - r0) * c * c * c * c * c;
                    let tr = 1.0 - re;
                    let p = 0.25 + 0.5 * re;
                    let rp = re / p;
                    let tp = tr / (1.0 - p);
                    colour = colour * if depth > 2 {
                        if rng.gen::<f64>() < p {
                            radiance(scene, &reflected_ray, depth, rng) * rp
                        } else {
                            radiance(scene, &transmitted_ray, depth, rng) * tp
                        }
                    } else {
                        radiance(scene, &reflected_ray, depth, rng) * re +
                            radiance(scene, &transmitted_ray, depth, rng) * tr
                    }
                }
            }
        }
        hit.sphere.emission + colour
    })
}

pub fn random_samp<T: Rng>(rng: &mut T) -> f64 {
    let r = 2.0 * rng.gen::<f64>();
    if r < 1.0 { r.sqrt() - 1.0 } else { 1.0 - (2.0 - r).sqrt() }
}

pub fn to_int(v: f64) -> u8 {
    let ch = (v.powf(1.0/2.2) * 255.0 + 0.5) as i64;
    if ch < 0 { 0 } else if ch > 255 { 255 } else { ch as u8 }
}
<commit_msg>Move to a more general-case hit record.<commit_after>extern crate rand;

pub use self::vec3d::Vec3d;
pub use self::ray::Ray;

use rand::Rng;

use std::f64::consts::PI;

mod vec3d;
mod ray;

pub enum Material {
    Diffuse,
    Specular,
    Refractive
}

pub struct Hit<'a> {
    distance: f64,
    pos: Vec3d,
    normal: Vec3d,
    material: &'a Material,
    emission: Vec3d,
    colour: Vec3d
}

pub trait Renderable {
    fn render(&self, ray: &Ray, nearest_hit: f64) -> Option<Hit>;
}

pub struct Sphere {
    material: Material,
    radius: f64,
    position: Vec3d,
    emission: Vec3d,
    colour: Vec3d
}

impl Sphere {
    pub fn new(material: Material, radius: f64, position: Vec3d, emission: Vec3d, colour: Vec3d) -> Sphere {
        Sphere {
          material: material,
          radius: radius,
          position: position,
          emission: emission,
          colour: colour
        }
    }
    fn intersect(&self, ray: &Ray) -> Option<f64> {
        let op = self.position - ray.origin;
        let b = op.dot(ray.direction);
        let determinant = b * b - op.dot(op) + self.radius * self.radius;
        if determinant < 0.0 { return None; }
        let determinant = determinant.sqrt();
        let t1 = b - determinant;
        let t2 = b + determinant;
        const EPSILON : f64 = 0.0001;
        if t1 > EPSILON {
            Some(t1)
        } else if t2 > EPSILON {
            Some(t2)
        } else {
            None
        }
    }
}

impl Renderable for Sphere {
    fn render(&self, ray: &Ray, nearest_hit: f64) -> Option<Hit> {
        if let Some(dist) = self.intersect(ray) {
            if dist < nearest_hit { // todo: can I combine with the if let?
                let pos = ray.origin + ray.direction * dist;
                let normal = (pos - self.position).normalized();
                return Some(Hit{
                    distance: dist,
                    pos: pos,
                    normal: normal,
                    material: &self.material,
                    colour: self.colour,
                    emission: self.emission
                });
            }
        }
        None
    }
}

#[test]
fn intersection() {
    let sphere = Sphere::new(
        Material::Diffuse,
        100.0,
        Vec3d::new(0.0, 0.0, 200.0),
        Vec3d::zero(),
        Vec3d::zero());
    let ray = Ray::new(Vec3d::zero(), Vec3d::new(0.0, 0.0, 1.0));
    match sphere.intersect(&ray) {
        Some(x) => assert_eq!(x, 100.0),
        None => panic!("unexpected")
    }
    let ray = Ray::new(Vec3d::zero(), Vec3d::new(0.0, 1.0, 0.0));
    match sphere.intersect(&ray) {
        Some(_) => panic!("unexpected"),
        None => {}
    }
}

fn intersect<'a>(scene: &'a [Sphere], ray: &Ray) -> Option<Hit<'a>> {
    let mut result : Option<Hit<'a>> = None;
    for sph in scene {
        if let Some(hit) = sph.render(&ray, result.as_ref().map_or(std::f64::INFINITY, |x| x.distance)) {
            result = Some(hit);
        }
    }
    result
}

pub fn radiance<R: Rng>(scene: &[Sphere], ray: &Ray, depth: i32, rng: &mut R) -> Vec3d {
    intersect(&scene, &ray).map_or(Vec3d::zero(), |hit| {
        let n1 = if hit.normal.dot(ray.direction) < 0.0 { hit.normal } else { hit.normal.neg() };
        let mut colour = hit.colour;
        let max_reflectance = colour.max_component();
        let depth = depth + 1;
        if depth > 5 {
            let rand = rng.gen::<f64>();
            if rand < max_reflectance && depth < 500 { // Rust's stack blows up ~600 on my machine
               colour = colour * (1.0 / max_reflectance);
            } else {
                return hit.emission;
            }
        }
        match *hit.material {
            Material::Diffuse => {
                // Get a random polar coordinate.
                let r1 = rng.gen::<f64>() * 2.0 * PI;
                let r2 = rng.gen::<f64>();
                let r2s = r2.sqrt();
                // Create a coordinate system u,v,w local to the point, where the w is the normal
                // pointing out of the sphere and the u and v are orthonormal to w.
                let w = n1;
                // Pick an arbitrary non-zero preferred axis for u
                let u = if n1.x.abs() > 0.1 { Vec3d::new(0.0, 1.0, 0.0) } else { Vec3d::new(1.0, 0.0, 0.0) }.cross(w);
                let v = w.cross(u);
                // construct the new direction
                let new_dir = u * r1.cos() * r2s + v * r1.sin() * r2s + w * (1.0 - r2).sqrt();
                colour = colour * radiance(scene, &Ray::new(hit.pos, new_dir.normalized()), depth, rng);
            },
            Material::Specular => {
                let reflection = ray.direction - hit.normal * 2.0 * hit.normal.dot(ray.direction);
                let reflected_ray = Ray::new(hit.pos, reflection);
                colour = colour * radiance(scene, &reflected_ray, depth, rng);
            },
            Material::Refractive => {
                let reflection = ray.direction - hit.normal * 2.0 * hit.normal.dot(ray.direction);
                let reflected_ray = Ray::new(hit.pos, reflection);
                let into = hit.normal.dot(n1) > 0.0;
                let nc = 1.0;
                let nt = 1.5;
                let nnt = if into { nc/nt } else { nt/nc };
                let ddn = ray.direction.dot(n1);
                let cos2t = 1.0 - nnt * nnt * (1.0 - ddn * ddn);
                if cos2t < 0.0 {
                    // Total internal reflection
                    colour = colour * radiance(scene, &reflected_ray, depth, rng);
                } else {
                    let tbd = ddn * nnt + cos2t.sqrt();
                    let tbd = if into { tbd } else { -tbd };
                    let tdir = (ray.direction * nnt - hit.normal * tbd).normalized();
                    let transmitted_ray = Ray::new(hit.pos, tdir);
                    let a = nt - nc;
                    let b = nt + nc;
                    let r0 = (a * a) / (b * b);
                    let c = 1.0 - if into { -ddn } else { tdir.dot(hit.normal) };
                    let re = r0 + (1.0 - r0) * c * c * c * c * c;
                    let tr = 1.0 - re;
                    let p = 0.25 + 0.5 * re;
                    let rp = re / p;
                    let tp = tr / (1.0 - p);
                    colour = colour * if depth > 2 {
                        if rng.gen::<f64>() < p {
                            radiance(scene, &reflected_ray, depth, rng) * rp
                        } else {
                            radiance(scene, &transmitted_ray, depth, rng) * tp
                        }
                    } else {
                        radiance(scene, &reflected_ray, depth, rng) * re +
                            radiance(scene, &transmitted_ray, depth, rng) * tr
                    }
                }
            }
        }
        hit.emission + colour
    })
}

pub fn random_samp<T: Rng>(rng: &mut T) -> f64 {
    let r = 2.0 * rng.gen::<f64>();
    if r < 1.0 { r.sqrt() - 1.0 } else { 1.0 - (2.0 - r).sqrt() }
}

pub fn to_int(v: f64) -> u8 {
    let ch = (v.powf(1.0/2.2) * 255.0 + 0.5) as i64;
    if ch < 0 { 0 } else if ch > 255 { 255 } else { ch as u8 }
}
<|endoftext|>


<commit_before>extern crate num;

#[cfg(test)] extern crate hamcrest;
#[cfg(test)] extern crate quickcheck;

use num::{Float, Num, NumCast, Signed, Zero, cast};
use std::f64::consts::PI;
use std::fmt::{Display, Formatter, Error};
use std::ops::{Add, Div, Mul, Neg, Sub};

/// An angle.
///
/// Might be a value in degrees or in radians.
#[derive(Copy, Clone, Debug)]
pub enum Angle<T=f64> {
    Radians(T),
    Degrees(T)
}

impl<T: NumCast> Angle<T> {
    /// Yield the value encoded in radians.
    pub fn in_radians(self) -> T {
        match self {
            Radians(v) => v,
            Degrees(v) => cast(cast::<T, f64>(v).unwrap() / 180.0 * PI).unwrap()
        }
    }

    /// Yield the value encoded in degrees.
    pub fn in_degrees(self) -> T {
        match self {
            Radians(v) => cast(cast::<T, f64>(v).unwrap() / PI * 180.0).unwrap(),
            Degrees(v) => v
        }
    }

    /// An angle of 45°.
    pub fn eighth() -> Angle<T> {
        Degrees(cast(45).unwrap())
    }

    /// An angle of 90° (right angle).
    pub fn quarter() -> Angle<T> {
        Degrees(cast(90).unwrap())
    }

    /// An angle of 180° (straight).
    pub fn half() -> Angle<T> {
        Degrees(cast(180).unwrap())
    }

    /// An angle of 360° (perigon).
    pub fn full() -> Angle<T> {
        Degrees(cast(360).unwrap())
    }
}

impl<T: Copy + Num + NumCast + PartialOrd> Angle<T> {
    /// Create a new angle by normalizing the value into the range of
    /// [0, 2π) rad.
    pub fn normalized(self) -> Self {
        let (v, upper) = match self {
            Radians(v) => (v, cast(2.0 * PI).unwrap()),
            Degrees(v) => (v, cast(360.0).unwrap())
        };

        let normalized = if v < upper && v >= Zero::zero() {
            v
        } else {
            let v = v % upper;

            if v >= Zero::zero() {
                v
            } else {
                v + upper
            }
        };

        match self {
            Radians(_) => Radians(normalized),
            Degrees(_) => Degrees(normalized)
        }
    }
}

impl<T: Signed> Angle<T> {
    /// Compute the absolute angle.
    pub fn abs(self) -> Self {
        match self {
            Radians(v) => Radians(v.abs()),
            Degrees(v) => Degrees(v.abs())
        }
    }
}

impl<T: Float + NumCast> Angle<T> {
    /// Compute the sine of the angle.
    pub fn sin(self) -> T {
        self.in_radians().sin()
    }

    /// Compute the cosine of the angle.
    pub fn cos(self) -> T {
        self.in_radians().cos()
    }

    /// Compute the tangent of the angle.
    pub fn tan(self) -> T {
        self.in_radians().tan()
    }

    /// Simultaneously compute the sine and cosine of the number, `x`.
    /// Return `(sin(x), cos(x))`.
    pub fn sin_cos(self) -> (T, T) {
        self.in_radians().sin_cos()
    }
}

/// Compute the arcsine of a number. Return value is in the range of
/// [-π/2, π/2] rad or `None` if the number is outside the range [-1, 1].
pub fn asin<T: Float>(value: T) -> Option<Angle<T>> {
    let value = value.asin();
    if value.is_nan() {
        None
    } else {
        Some(Radians(value))
    }
}

/// Compute the arccosine of a number. Return value is in the range of
/// [0, π] rad or `None` if the number is outside the range [-1, 1].
pub fn acos<T: Float>(value: T) -> Option<Angle<T>> {
    let value = value.acos();
    if value.is_nan() {
        None
    } else {
        Some(Radians(value))
    }
}

/// Compute the arctangent of a number. Return value is in the range of
/// [-π/2, π/2] rad.
pub fn atan<T: Float>(value: T) -> Angle<T> {
    Radians(value.atan())
}

/// Compute the four quadrant arctangent of `y` and `x`.
pub fn atan2<T: Float>(y: T, x: T) -> Angle<T> {
    Radians(y.atan2(x))
}

/// Compute the approximate mean of a list of angles by averaging the
/// Cartesian coordinates of the angles on the unit circle. Return the
/// normalized angle.
pub fn mean_angle<T: Float>(angles: &[Angle<T>]) -> Angle<T>
{
    let mut x = T::zero();
    let mut y = T::zero();

    for angle in angles {
        let (sin, cos) = angle.sin_cos();

        x = x + cos;
        y = y + sin;
    }

    let n = cast(angles.len()).unwrap();
    let a = (y/n).atan2(x/n);

    Radians(a).normalized()
}

impl<T: Zero + Copy + NumCast> Zero for Angle<T> {
    fn zero() -> Self {
        Radians(T::zero())
    }

    fn is_zero(&self) -> bool {
        match self {
            &Radians(ref v) => v.is_zero(),
            &Degrees(ref v) => v.is_zero()
        }
    }
}

impl<T: PartialEq + Copy + NumCast> PartialEq for Angle<T> {
    fn eq(&self, other: &Angle<T>) -> bool {
        if let (Degrees(a), Degrees(b)) = (*self, *other) {
            a == b
        } else {
            self.in_radians() == other.in_radians()
        }
    }
}

macro_rules! math_additive(
    ($bound:ident, $func:ident) => (
        impl<T: $bound + Copy + NumCast> $bound for Angle<T> {
            type Output = Angle<T::Output>;
            fn $func(self, rhs: Angle<T>) -> Self::Output {
                if let (Degrees(a), Degrees(b)) = (self, rhs) {
                    Degrees(a.$func(b))
                } else {
                    Radians(self.in_radians().$func(rhs.in_radians()))
                }
            }
        }
    );
);

math_additive!(Add, add);
math_additive!(Sub, sub);

macro_rules! math_multiplicative(
    ($bound:ident, $func:ident, $($t:ident),*) => (
        impl<T: $bound> $bound<T> for Angle<T> {
            type Output = Angle<T::Output>;
            fn $func(self, rhs: T) -> Self::Output {
                match self {
                    Radians(v) => Radians(v.$func(rhs)),
                    Degrees(v) => Degrees(v.$func(rhs))
                }
            }
        }

        $(
            impl $bound<Angle<$t>> for $t {
                type Output = Angle<$t>;
                fn $func(self, rhs: Angle<$t>) -> Self::Output {
                    match rhs {
                        Radians(v) => Radians(self.$func(v)),
                        Degrees(v) => Degrees(self.$func(v))
                    }
                }
            }
        )*
    );
);

math_multiplicative!(Mul, mul, u8, u16, u32, u64, i8, i16, i32, i64, usize, isize, f32, f64);
math_multiplicative!(Div, div, u8, u16, u32, u64, i8, i16, i32, i64, usize, isize, f32, f64);

impl<T: Neg> Neg for Angle<T> {
    type Output = Angle<T::Output>;
    fn neg(self) -> Self::Output {
        match self {
            Radians(v) => Radians(-v),
            Degrees(v) => Degrees(-v)
        }
    }
}

impl<T: Display> Display for Angle<T> {
    fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
        match *self {
            Radians(ref v) => write!(f, "{}rad", v),
            Degrees(ref v) => write!(f, "{}°", v)
        }
    }
}

unsafe impl<T: Send> Send for Angle<T> {  }

// re-exports
pub use Angle::{Radians, Degrees};

#[cfg(test)]
mod tests {
    use hamcrest::{assert_that, is, close_to};
    use num::{Float, cast};
    use quickcheck::{Arbitrary, Gen, quickcheck};
    use std::f64::consts::PI;

    use super::*;

    #[test]
    fn test_angle_conversions() {
        fn prop(angle: Angle) -> bool {
            are_close(angle.in_radians(), Degrees(angle.in_degrees()).in_radians())
        }
        quickcheck(prop as fn(Angle) -> bool);
    }

    #[test]
    fn test_angle_math_multiplicative() {
        fn prop(a: Angle, x: f64) -> bool {
            match a {
                Radians(v) => (a * x).in_radians() == v * x &&
                              (a / x).in_radians() == v / x,
                Degrees(v) => (a * x).in_degrees() == v * x &&
                              (a / x).in_degrees() == v / x
            }
        }
        quickcheck(prop as fn(Angle, f64) -> bool);
    }

    #[test]
    fn test_angle_math_additive() {
        fn prop(a: Angle, b: Angle) -> bool {
            if let (Radians(x), Radians(y)) = (a, b) {
                (a + b).in_radians() == x + y &&
                (a - b).in_radians() == x - y
            } else if let (Degrees(x), Degrees(y)) = (a, b) {
                (a + b).in_degrees() == x + y &&
                (a - b).in_degrees() == x - y
            } else {
                (a + b).in_radians() == a.in_radians() + b.in_radians()
            }
        }
        quickcheck(prop as fn(Angle, Angle) -> bool);
    }

    #[test]
    fn test_angle_normalization() {
        fn prop(angle: Angle) -> bool {
            let v = angle.normalized();
            let rad = v.in_radians();
            let deg = v.in_degrees();

            0.0 <= rad && rad < 2.0 * PI &&
            0.0 <= deg && deg < 360.0 &&
            are_close(rad.cos(), angle.cos())
        }
        quickcheck(prop as fn(Angle) -> bool);
    }

    #[test]
    pub fn test_mean_angle() {
        assert_that(mean_angle(&[Degrees(90.0)]).in_degrees(), is(close_to(90.0, 0.000001)));
        assert_that(mean_angle(&[Degrees(90.0), Degrees(90.0)]).in_degrees(), is(close_to(90.0, 0.000001)));
        assert_that(mean_angle(&[Degrees(90.0), Degrees(180.0), Degrees(270.0)]).in_degrees(), is(close_to(180.0, 0.000001)));
        assert_that(mean_angle(&[Degrees(20.0), Degrees(350.0)]).in_degrees(), is(close_to(5.0, 0.000001)));
    }

    fn are_close<T: Float>(a: T, b: T) -> bool {
        (a - b).abs() < cast(1.0e-10).unwrap()
    }

    impl<T: Arbitrary> Arbitrary for Angle<T> {
        fn arbitrary<G: Gen>(g: &mut G) -> Self {
            let v = Arbitrary::arbitrary(g);
            if bool::arbitrary(g) {
                Radians(v)
            } else {
                Degrees(v)
            }
        }
    }
}
<commit_msg>Add method `Angle::min_dist()`<commit_after>extern crate num;

#[cfg(test)] extern crate hamcrest;
#[cfg(test)] extern crate quickcheck;

use num::{Float, Num, NumCast, Signed, Zero, cast};
use std::f64::consts::PI;
use std::fmt::{Display, Formatter, Error};
use std::ops::{Add, Div, Mul, Neg, Sub};

/// An angle.
///
/// Might be a value in degrees or in radians.
#[derive(Copy, Clone, Debug)]
pub enum Angle<T=f64> {
    Radians(T),
    Degrees(T)
}

impl<T: NumCast> Angle<T> {
    /// Yield the value encoded in radians.
    pub fn in_radians(self) -> T {
        match self {
            Radians(v) => v,
            Degrees(v) => cast(cast::<T, f64>(v).unwrap() / 180.0 * PI).unwrap()
        }
    }

    /// Yield the value encoded in degrees.
    pub fn in_degrees(self) -> T {
        match self {
            Radians(v) => cast(cast::<T, f64>(v).unwrap() / PI * 180.0).unwrap(),
            Degrees(v) => v
        }
    }

    /// An angle of 45°.
    pub fn eighth() -> Angle<T> {
        Degrees(cast(45).unwrap())
    }

    /// An angle of 90° (right angle).
    pub fn quarter() -> Angle<T> {
        Degrees(cast(90).unwrap())
    }

    /// An angle of 180° (straight).
    pub fn half() -> Angle<T> {
        Degrees(cast(180).unwrap())
    }

    /// An angle of 360° (perigon).
    pub fn full() -> Angle<T> {
        Degrees(cast(360).unwrap())
    }
}

impl<T: Copy + Num + NumCast + PartialOrd> Angle<T> {
    /// Create a new angle by normalizing the value into the range of
    /// [0, 2π) rad.
    pub fn normalized(self) -> Self {
        let (v, upper) = match self {
            Radians(v) => (v, cast(2.0 * PI).unwrap()),
            Degrees(v) => (v, cast(360.0).unwrap())
        };

        let normalized = if v < upper && v >= Zero::zero() {
            v
        } else {
            let v = v % upper;

            if v >= Zero::zero() {
                v
            } else {
                v + upper
            }
        };

        match self {
            Radians(_) => Radians(normalized),
            Degrees(_) => Degrees(normalized)
        }
    }
}

impl<T: Float> Angle<T> {
    /// Computes the minimal unsigned distance between two normalized angles. Returns an
    /// angle in the range of [0, π] rad.
    ///
    /// ```rust
    /// # use angular::*;
    /// let distance = Degrees(345.0).min_dist(Degrees(15.0));
    /// assert!((distance.in_degrees() - 30.0) < 1.0e-10);
    /// ```
    pub fn min_dist(self, other: Angle<T>) -> Angle<T> {
        let pi = cast(PI).unwrap();
        let two_pi = cast(2.0 * PI).unwrap();

        let a = self.in_radians();
        let b = other.in_radians();

        let d = (a - b).abs();

        // short-circout if both angles are normalized
        Radians(if a >= T::zero() && a < two_pi && b >= T::zero() && b < two_pi {
            d.min(two_pi - d)
        } else {
            pi - ((d % two_pi) - pi).abs()
        })
    }
}

impl<T: Signed> Angle<T> {
    /// Compute the absolute angle.
    pub fn abs(self) -> Self {
        match self {
            Radians(v) => Radians(v.abs()),
            Degrees(v) => Degrees(v.abs())
        }
    }
}

impl<T: Float + NumCast> Angle<T> {
    /// Compute the sine of the angle.
    pub fn sin(self) -> T {
        self.in_radians().sin()
    }

    /// Compute the cosine of the angle.
    pub fn cos(self) -> T {
        self.in_radians().cos()
    }

    /// Compute the tangent of the angle.
    pub fn tan(self) -> T {
        self.in_radians().tan()
    }

    /// Simultaneously compute the sine and cosine of the number, `x`.
    /// Return `(sin(x), cos(x))`.
    pub fn sin_cos(self) -> (T, T) {
        self.in_radians().sin_cos()
    }
}

impl<T: Zero + Copy + NumCast> Zero for Angle<T> {
    fn zero() -> Self {
        Radians(T::zero())
    }

    fn is_zero(&self) -> bool {
        match self {
            &Radians(ref v) => v.is_zero(),
            &Degrees(ref v) => v.is_zero()
        }
    }
}

impl<T: PartialEq + Copy + NumCast> PartialEq for Angle<T> {
    fn eq(&self, other: &Angle<T>) -> bool {
        if let (Degrees(a), Degrees(b)) = (*self, *other) {
            a == b
        } else {
            self.in_radians() == other.in_radians()
        }
    }
}

macro_rules! math_additive(
    ($bound:ident, $func:ident) => (
        impl<T: $bound + Copy + NumCast> $bound for Angle<T> {
            type Output = Angle<T::Output>;
            fn $func(self, rhs: Angle<T>) -> Self::Output {
                if let (Degrees(a), Degrees(b)) = (self, rhs) {
                    Degrees(a.$func(b))
                } else {
                    Radians(self.in_radians().$func(rhs.in_radians()))
                }
            }
        }
    );
);

math_additive!(Add, add);
math_additive!(Sub, sub);

macro_rules! math_multiplicative(
    ($bound:ident, $func:ident, $($t:ident),*) => (
        impl<T: $bound> $bound<T> for Angle<T> {
            type Output = Angle<T::Output>;
            fn $func(self, rhs: T) -> Self::Output {
                match self {
                    Radians(v) => Radians(v.$func(rhs)),
                    Degrees(v) => Degrees(v.$func(rhs))
                }
            }
        }

        $(
            impl $bound<Angle<$t>> for $t {
                type Output = Angle<$t>;
                fn $func(self, rhs: Angle<$t>) -> Self::Output {
                    match rhs {
                        Radians(v) => Radians(self.$func(v)),
                        Degrees(v) => Degrees(self.$func(v))
                    }
                }
            }
        )*
    );
);

math_multiplicative!(Mul, mul, u8, u16, u32, u64, i8, i16, i32, i64, usize, isize, f32, f64);
math_multiplicative!(Div, div, u8, u16, u32, u64, i8, i16, i32, i64, usize, isize, f32, f64);

impl<T: Neg> Neg for Angle<T> {
    type Output = Angle<T::Output>;
    fn neg(self) -> Self::Output {
        match self {
            Radians(v) => Radians(-v),
            Degrees(v) => Degrees(-v)
        }
    }
}

impl<T: Display> Display for Angle<T> {
    fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
        match *self {
            Radians(ref v) => write!(f, "{}rad", v),
            Degrees(ref v) => write!(f, "{}°", v)
        }
    }
}

unsafe impl<T: Send> Send for Angle<T> {  }

/// Compute the arcsine of a number. Return value is in the range of
/// [-π/2, π/2] rad or `None` if the number is outside the range [-1, 1].
pub fn asin<T: Float>(value: T) -> Option<Angle<T>> {
    let value = value.asin();
    if value.is_nan() {
        None
    } else {
        Some(Radians(value))
    }
}

/// Compute the arccosine of a number. Return value is in the range of
/// [0, π] rad or `None` if the number is outside the range [-1, 1].
pub fn acos<T: Float>(value: T) -> Option<Angle<T>> {
    let value = value.acos();
    if value.is_nan() {
        None
    } else {
        Some(Radians(value))
    }
}

/// Compute the arctangent of a number. Return value is in the range of
/// [-π/2, π/2] rad.
pub fn atan<T: Float>(value: T) -> Angle<T> {
    Radians(value.atan())
}

/// Compute the four quadrant arctangent of `y` and `x`.
pub fn atan2<T: Float>(y: T, x: T) -> Angle<T> {
    Radians(y.atan2(x))
}

/// Compute the approximate mean of a list of angles by averaging the
/// Cartesian coordinates of the angles on the unit circle. Return the
/// normalized angle.
pub fn mean_angle<T: Float>(angles: &[Angle<T>]) -> Angle<T>
{
    let mut x = T::zero();
    let mut y = T::zero();

    for angle in angles {
        let (sin, cos) = angle.sin_cos();

        x = x + cos;
        y = y + sin;
    }

    let n = cast(angles.len()).unwrap();
    let a = (y/n).atan2(x/n);

    Radians(a).normalized()
}

// re-exports
pub use Angle::{Radians, Degrees};

#[cfg(test)]
mod tests {
    use hamcrest::{assert_that, is, close_to};
    use num::{Float, cast};
    use quickcheck::{Arbitrary, Gen, quickcheck};
    use std::f64::consts::PI;

    use super::*;

    #[test]
    fn test_angle_conversions() {
        fn prop(angle: Angle) -> bool {
            are_close(angle.in_radians(), Degrees(angle.in_degrees()).in_radians())
        }
        quickcheck(prop as fn(Angle) -> bool);
    }

    #[test]
    fn test_angle_math_multiplicative() {
        fn prop(a: Angle, x: f64) -> bool {
            match a {
                Radians(v) => (a * x).in_radians() == v * x &&
                              (a / x).in_radians() == v / x,
                Degrees(v) => (a * x).in_degrees() == v * x &&
                              (a / x).in_degrees() == v / x
            }
        }
        quickcheck(prop as fn(Angle, f64) -> bool);
    }

    #[test]
    fn test_angle_math_additive() {
        fn prop(a: Angle, b: Angle) -> bool {
            if let (Radians(x), Radians(y)) = (a, b) {
                (a + b).in_radians() == x + y &&
                (a - b).in_radians() == x - y
            } else if let (Degrees(x), Degrees(y)) = (a, b) {
                (a + b).in_degrees() == x + y &&
                (a - b).in_degrees() == x - y
            } else {
                (a + b).in_radians() == a.in_radians() + b.in_radians()
            }
        }
        quickcheck(prop as fn(Angle, Angle) -> bool);
    }

    #[test]
    fn test_angle_normalization() {
        fn prop(angle: Angle) -> bool {
            let v = angle.normalized();
            let rad = v.in_radians();
            let deg = v.in_degrees();

            0.0 <= rad && rad < 2.0 * PI &&
            0.0 <= deg && deg < 360.0 &&
            are_close(rad.cos(), angle.cos())
        }
        quickcheck(prop as fn(Angle) -> bool);
    }

    #[test]
    fn test_angle_minimal_distance() {
        fn prop(a: Angle, b: Angle) -> bool {
            let d = a.min_dist(b);
            0.0 <= d.in_radians() && d.in_radians() <= PI
        }
        quickcheck(prop as fn(Angle, Angle) -> bool);

        assert_that(Degrees(180.0).min_dist(Degrees(0.0)).in_degrees(), is(close_to(180.0, 0.000001)));
        assert_that(Degrees(0.1).min_dist(Degrees(359.9)).in_degrees(), is(close_to(0.2, 0.000001)));
        assert_that(Degrees(1.0).min_dist(Degrees(2.0)).in_degrees(), is(close_to(1.0, 0.000001)));
    }

    #[test]
    pub fn test_mean_angle() {
        assert_that(mean_angle(&[Degrees(90.0)]).in_degrees(), is(close_to(90.0, 0.000001)));
        assert_that(mean_angle(&[Degrees(90.0), Degrees(90.0)]).in_degrees(), is(close_to(90.0, 0.000001)));
        assert_that(mean_angle(&[Degrees(90.0), Degrees(180.0), Degrees(270.0)]).in_degrees(), is(close_to(180.0, 0.000001)));
        assert_that(mean_angle(&[Degrees(20.0), Degrees(350.0)]).in_degrees(), is(close_to(5.0, 0.000001)));
    }

    fn are_close<T: Float>(a: T, b: T) -> bool {
        (a - b).abs() < cast(1.0e-10).unwrap()
    }

    impl<T: Arbitrary> Arbitrary for Angle<T> {
        fn arbitrary<G: Gen>(g: &mut G) -> Self {
            let v = Arbitrary::arbitrary(g);
            if bool::arbitrary(g) {
                Radians(v)
            } else {
                Degrees(v)
            }
        }
    }
}
<|endoftext|>


<commit_before>#![doc(
    html_logo_url = "https://cdn.rawgit.com/urschrei/polyline-ffi/master/line.svg",
    html_root_url = "https://urschrei.github.io/polyline-ffi/"
)]
//! This module exposes functions for accessing the Polyline encoding and decoding functions via FFI
//!
//!
//! ## A Note on Coordinate Order
//! This crate uses `Coordinate` and `LineString` types from the `geo-types` crate, which encodes coordinates in `(x, y)` order. The Polyline algorithm and first-party documentation assumes the _opposite_ coordinate order. It is thus advisable to pay careful attention to the order of the coordinates you use for encoding and decoding.

use polyline::{decode_polyline, encode_coordinates};
use std::f64;
use std::ffi::{CStr, CString};
use std::mem;
use std::slice;

use geo_types::{CoordFloat, LineString};
use libc::{c_char, c_void, size_t};

// we only want to allow 5 or 6, but we need the previous values for the cast to work
#[allow(dead_code)]
enum Precision {
    Zero,
    One,
    Two,
    Three,
    Four,
    Five,
    Six,
}

// We currently only allow 5 or 6
fn get_precision(input: u32) -> Option<u32> {
    match input {
        5 => Some(Precision::Five as u32),
        6 => Some(Precision::Six as u32),
        _ => None,
    }
}

/// A C-compatible `struct` used for passing arrays across the FFI boundary
#[repr(C)]
pub struct Array {
    pub data: *const libc::c_void,
    pub len: libc::size_t,
}

// Build an Array from a LineString, so it can be leaked across the FFI boundary
impl<T> From<geo_types::LineString<T>> for Array
where
    T: CoordFloat,
{
    fn from(sl: LineString<T>) -> Self {
        let v: Vec<[T; 2]> = sl.into_iter().map(|coord| [coord.x, coord.y]).collect();
        let array = Array {
            data: v.as_ptr() as *const libc::c_void,
            len: v.len() as libc::size_t,
        };
        mem::forget(v);
        array
    }
}

// Build an Array from &[[f64; 2]], so it can be leaked across the FFI boundary
impl From<Vec<[f64; 2]>> for Array {
    fn from(sl: Vec<[f64; 2]>) -> Self {
        let array = Array {
            data: sl.as_ptr() as *const c_void,
            len: sl.len() as size_t,
        };
        mem::forget(sl);
        array
    }
}

// Build &[[f64; 2]] from an Array, so it can be dropped
impl From<Array> for Vec<[f64; 2]> {
    fn from(arr: Array) -> Self {
        unsafe { slice::from_raw_parts(arr.data as *mut [f64; 2], arr.len).to_vec() }
    }
}

// Decode a Polyline into an Array
fn arr_from_string(incoming: &str, precision: u32) -> Array {
    let result: Array = if get_precision(precision).is_some() {
        match decode_polyline(incoming, precision) {
            Ok(res) => res.into(),
            // should be easy to check for
            Err(_) => vec![[f64::NAN, f64::NAN]].into(),
        }
    } else {
        // bad precision parameter
        vec![[f64::NAN, f64::NAN]].into()
    };
    result.into()
}

// Decode an Array into a Polyline
fn string_from_arr(incoming: Array, precision: u32) -> String {
    let inc: Vec<_> = incoming.into();
    if get_precision(precision).is_some() {
        match encode_coordinates(Into::<LineString<_>>::into(inc), precision) {
            Ok(res) => res,
            // we don't need to adapt the error
            Err(res) => res,
        }
    } else {
        "Bad precision parameter supplied".to_string()
    }
}

/// Convert a Polyline into an array of coordinates
///
/// Callers must pass two arguments:
///
/// - a pointer to `NUL`-terminated characters (`char*`)
/// - an unsigned 32-bit `int` for precision (5 for Google Polylines, 6 for
/// OSRM and Valhalla Polylines)
///
/// A decoding failure will return an [Array](struct.Array.html) whose `data` field is `[[NaN, NaN]]`, and whose `len` field is `1`.
///
/// Implementations calling this function **must** call [`drop_float_array`](fn.drop_float_array.html)
/// with the returned [Array](struct.Array.html), in order to free the memory it allocates.
///
/// # Safety
///
/// This function is unsafe because it accesses a raw pointer which could contain arbitrary data
#[no_mangle]
pub extern "C" fn decode_polyline_ffi(pl: *const c_char, precision: u32) -> Array {
    let s = unsafe { CStr::from_ptr(pl).to_str() };
    if let Ok(unwrapped) = s {
        arr_from_string(unwrapped, precision)
    } else {
        vec![[f64::NAN, f64::NAN]].into()
    }
}

/// Convert an array of coordinates into a Polyline
///
/// Callers must pass two arguments:
///
/// - a [Struct](struct.Array.html) with two fields:
///     - `data`, a void pointer to an array of floating-point lat, lon coordinates: `[[1.0, 2.0]]`
///     - `len`, the length of the array being passed. Its type must be `size_t`: `1`
/// - an unsigned 32-bit `int` for precision (5 for Google Polylines, 6 for
/// OSRM and Valhalla Polylines)
///
/// A decoding failure will return one of the following:
///
/// - a `char*` beginning with "Longitude error:" if invalid longitudes are passed
/// - a `char*` beginning with "Latitude error:" if invalid latitudes are passed
///
/// Implementations calling this function **must** call [`drop_cstring`](fn.drop_cstring.html)
/// with the returned `c_char` pointer, in order to free the memory it allocates.
///
/// # Safety
///
/// This function is unsafe because it accesses a raw pointer which could contain arbitrary data
#[no_mangle]
pub extern "C" fn encode_coordinates_ffi(coords: Array, precision: u32) -> *mut c_char {
    let s: String = string_from_arr(coords, precision);
    match CString::new(s) {
        Ok(res) => res.into_raw(),
        // It's arguably better to fail noisily, but this is robust
        Err(_) => CString::new("Couldn't decode Polyline".to_string())
            .unwrap()
            .into_raw(),
    }
}

/// Free Array memory which Rust has allocated across the FFI boundary
///
/// # Safety
///
/// This function is unsafe because it accesses a raw pointer which could contain arbitrary data
#[no_mangle]
pub extern "C" fn drop_float_array(arr: Array) {
    if arr.data.is_null() {
        return;
    }
    let _: Vec<_> = arr.into();
}

/// Free `CString` memory which Rust has allocated across the FFI boundary
///
/// # Safety
///
/// This function is unsafe because it accesses a raw pointer which could contain arbitrary data
#[no_mangle]
pub extern "C" fn drop_cstring(p: *mut c_char) {
    let _ = unsafe { CString::from_raw(p) };
}

#[cfg(test)]
mod tests {
    use super::*;
    use std::ffi::{CStr, CString};
    use std::ptr;

    #[test]
    fn test_array_conversion() {
        let original = vec![[1.0, 2.0], [3.0, 4.0], [5.0, 6.0]];
        // move into an Array, and leak it
        let arr: Array = original.into();
        // move back into a Vec -- leaked value still needs to be dropped
        let converted: Vec<_> = arr.into();
        assert_eq!(&converted, &[[1.0, 2.0], [3.0, 4.0], [5.0, 6.0]]);
        // drop it
        drop_float_array(converted.into());
    }

    #[test]
    fn test_drop_empty_float_array() {
        let original = vec![[2.0, 1.0], [4.0, 3.0]];
        // move into an Array, and leak it
        let mut arr: Array = original.into();
        // zero Array contents
        arr.data = ptr::null();
        drop_float_array(arr);
    }

    #[test]
    fn test_coordinate_conversion() {
        let input = vec![[2.0, 1.0], [4.0, 3.0]];
        let output = "_ibE_seK_seK_seK";
        let input_arr: Array = input.into();
        let transformed: String = super::string_from_arr(input_arr, 5);
        assert_eq!(transformed, output);
    }

    #[test]
    fn test_string_conversion() {
        let input = "_ibE_seK_seK_seK";
        let output = [[2.0, 1.0], [4.0, 3.0]];
        // String to Array
        let transformed: Array = super::arr_from_string(input, 5);
        // Array to Vec
        let transformed_arr: Vec<_> = transformed.into();
        assert_eq!(&transformed_arr, &output);
    }

    #[test]
    #[should_panic]
    fn test_bad_string_conversion() {
        let input = "_p~iF~ps|U_u🗑lLnnqC_mqNvxq`@";
        let output = vec![[1.0, 2.0], [3.0, 4.0]];
        // String to Array
        let transformed: Array = super::arr_from_string(input, 5);
        // Array to Vec
        let transformed_arr: Vec<_> = transformed.into();
        // this will fail, bc transformed_arr is [[NaN, NaN]]
        assert_eq!(transformed_arr, output.as_slice());
    }

    #[test]
    fn test_ffi_polyline_decoding() {
        let cstr = CString::new("_ibE_seK_seK_seK").unwrap();
        let ptr = cstr.as_ptr();
        let result: Vec<_> = decode_polyline_ffi(ptr, 5).into();
        assert_eq!(&result, &[[2.0, 1.0], [4.0, 3.0]]);
        drop_float_array(result.into());
    }

    #[test]
    #[should_panic]
    fn test_bad_precision_decode() {
        let cstr = CString::new("_ibE_seK_seK_seK").unwrap();
        let ptr = cstr.as_ptr();
        let result: Vec<_> = decode_polyline_ffi(ptr, 7).into();
        assert_eq!(&result, &[[2.0, 1.0], [4.0, 3.0]]);
        drop_float_array(result.into());
    }

    #[test]
    fn test_ffi_coordinate_encoding() {
        let input: Array = vec![[2.0, 1.0], [4.0, 3.0]].into();
        let output = "_ibE_seK_seK_seK".to_string();
        let pl = encode_coordinates_ffi(input, 5);
        // Allocate a new String
        let result = unsafe { CStr::from_ptr(pl).to_str().unwrap() };
        assert_eq!(&result, &output);
        // Drop received FFI data
        drop_cstring(pl);
    }

    #[test]
    #[should_panic]
    fn test_bad_precision_encode() {
        let input: Array = vec![[1.0, 2.0], [3.0, 4.0]].into();
        let output = "_ibE_seK_seK_seK".to_string();
        let pl = encode_coordinates_ffi(input, 4);
        // Allocate a new String
        let result = unsafe { CStr::from_ptr(pl).to_str().unwrap() };
        assert_eq!(&result, &output);
        // Drop received FFI data
        drop_cstring(pl);
    }
    #[test]
    fn test_long_vec() {
        use std::clone::Clone;
        let arr = include!("../test_fixtures/berlin.rs");
        let s = include!("../test_fixtures/berlin_decoded.rs");
        for _ in 0..9999 {
            let a = arr.clone().into();
            let s_ = s.clone();
            let n = 5;
            let encoded = encode_coordinates_ffi(a, n);
            let result = unsafe { CStr::from_ptr(encoded).to_str().unwrap() };
            assert_eq!(&result, &s_);
            drop_cstring(encoded);
        }
    }
}
<commit_msg>Mark extern fns unsafe, shrink-to-fit vectors<commit_after>#![doc(
    html_logo_url = "https://cdn.rawgit.com/urschrei/polyline-ffi/master/line.svg",
    html_root_url = "https://urschrei.github.io/polyline-ffi/"
)]
//! This module exposes functions for accessing the Polyline encoding and decoding functions via FFI
//!
//!
//! ## A Note on Coordinate Order
//! This crate uses `Coordinate` and `LineString` types from the `geo-types` crate, which encodes coordinates in `(x, y)` order. The Polyline algorithm and first-party documentation assumes the _opposite_ coordinate order. It is thus advisable to pay careful attention to the order of the coordinates you use for encoding and decoding.

use polyline::{decode_polyline, encode_coordinates};
use std::f64;
use std::ffi::{CStr, CString};
use std::mem;
use std::slice;

use geo_types::{CoordFloat, LineString};
use libc::{c_char, c_void, size_t};

// we only want to allow 5 or 6, but we need the previous values for the cast to work
#[allow(dead_code)]
enum Precision {
    Zero,
    One,
    Two,
    Three,
    Four,
    Five,
    Six,
}

// We currently only allow 5 or 6
fn get_precision(input: u32) -> Option<u32> {
    match input {
        5 => Some(Precision::Five as u32),
        6 => Some(Precision::Six as u32),
        _ => None,
    }
}

/// A C-compatible `struct` used for passing arrays across the FFI boundary
#[repr(C)]
pub struct Array {
    pub data: *const libc::c_void,
    pub len: libc::size_t,
}

// Build an Array from a LineString, so it can be leaked across the FFI boundary
impl<T> From<geo_types::LineString<T>> for Array
where
    T: CoordFloat,
{
    fn from(sl: LineString<T>) -> Self {
        let mut v: Vec<[T; 2]> = sl.into_iter().map(|coord| [coord.x, coord.y]).collect();
        v.shrink_to_fit();
        let array = Array {
            data: v.as_ptr() as *const libc::c_void,
            len: v.len() as libc::size_t,
        };
        mem::forget(v);
        array
    }
}

// Build an Array from &[[f64; 2]], so it can be leaked across the FFI boundary
impl From<Vec<[f64; 2]>> for Array {
    fn from(sl: Vec<[f64; 2]>) -> Self {
        let mut shrunken = sl;
        shrunken.shrink_to_fit();
        let array = Array {
            data: shrunken.as_ptr() as *const c_void,
            len: shrunken.len() as size_t,
        };
        mem::forget(shrunken);
        array
    }
}

// Build &[[f64; 2]] from an Array, so it can be dropped
impl From<Array> for Vec<[f64; 2]> {
    fn from(arr: Array) -> Self {
        unsafe { slice::from_raw_parts(arr.data as *mut [f64; 2], arr.len).to_vec() }
    }
}

// Decode a Polyline into an Array
fn arr_from_string(incoming: &str, precision: u32) -> Array {
    let result: Array = if get_precision(precision).is_some() {
        match decode_polyline(incoming, precision) {
            Ok(res) => res.into(),
            // should be easy to check for
            Err(_) => vec![[f64::NAN, f64::NAN]].into(),
        }
    } else {
        // bad precision parameter
        vec![[f64::NAN, f64::NAN]].into()
    };
    result
}

// Decode an Array into a Polyline
fn string_from_arr(incoming: Array, precision: u32) -> String {
    let inc: Vec<_> = incoming.into();
    if get_precision(precision).is_some() {
        match encode_coordinates(Into::<LineString<_>>::into(inc), precision) {
            Ok(res) => res,
            // we don't need to adapt the error
            Err(res) => res,
        }
    } else {
        "Bad precision parameter supplied".to_string()
    }
}

/// Convert a Polyline into an array of coordinates
///
/// Callers must pass two arguments:
///
/// - a pointer to `NUL`-terminated characters (`char*`)
/// - an unsigned 32-bit `int` for precision (5 for Google Polylines, 6 for
/// OSRM and Valhalla Polylines)
///
/// A decoding failure will return an [Array](struct.Array.html) whose `data` field is `[[NaN, NaN]]`, and whose `len` field is `1`.
///
/// Implementations calling this function **must** call [`drop_float_array`](fn.drop_float_array.html)
/// with the returned [Array](struct.Array.html), in order to free the memory it allocates.
///
/// # Safety
///
/// This function is unsafe because it accesses a raw pointer which could contain arbitrary data
#[no_mangle]
pub unsafe extern "C" fn decode_polyline_ffi(pl: *const c_char, precision: u32) -> Array {
    let s = CStr::from_ptr(pl).to_str();
    if let Ok(unwrapped) = s {
        arr_from_string(unwrapped, precision)
    } else {
        vec![[f64::NAN, f64::NAN]].into()
    }
}

/// Convert an array of coordinates into a Polyline
///
/// Callers must pass two arguments:
///
/// - a [Struct](struct.Array.html) with two fields:
///     - `data`, a void pointer to an array of floating-point lat, lon coordinates: `[[1.0, 2.0]]`
///     - `len`, the length of the array being passed. Its type must be `size_t`: `1`
/// - an unsigned 32-bit `int` for precision (5 for Google Polylines, 6 for
/// OSRM and Valhalla Polylines)
///
/// A decoding failure will return one of the following:
///
/// - a `char*` beginning with "Longitude error:" if invalid longitudes are passed
/// - a `char*` beginning with "Latitude error:" if invalid latitudes are passed
///
/// Implementations calling this function **must** call [`drop_cstring`](fn.drop_cstring.html)
/// with the returned `c_char` pointer, in order to free the memory it allocates.
///
/// # Safety
///
/// This function is unsafe because it accesses a raw pointer which could contain arbitrary data
#[no_mangle]
pub extern "C" fn encode_coordinates_ffi(coords: Array, precision: u32) -> *mut c_char {
    let s: String = string_from_arr(coords, precision);
    match CString::new(s) {
        Ok(res) => res.into_raw(),
        // It's arguably better to fail noisily, but this is robust
        Err(_) => CString::new("Couldn't decode Polyline".to_string())
            .unwrap()
            .into_raw(),
    }
}

/// Free Array memory which Rust has allocated across the FFI boundary
///
/// # Safety
///
/// This function is unsafe because it accesses a raw pointer which could contain arbitrary data
#[no_mangle]
pub extern "C" fn drop_float_array(arr: Array) {
    if arr.data.is_null() {
        return;
    }
    let _: Vec<_> = arr.into();
}

/// Free `CString` memory which Rust has allocated across the FFI boundary
///
/// # Safety
///
/// This function is unsafe because it accesses a raw pointer which could contain arbitrary data
#[no_mangle]
pub unsafe extern "C" fn drop_cstring(p: *mut c_char) {
    let _ = CString::from_raw(p);
}

#[cfg(test)]
mod tests {
    use super::*;
    use std::ffi::{CStr, CString};
    use std::ptr;

    #[test]
    fn test_array_conversion() {
        let original = vec![[1.0, 2.0], [3.0, 4.0], [5.0, 6.0]];
        // move into an Array, and leak it
        let arr: Array = original.into();
        // move back into a Vec -- leaked value still needs to be dropped
        let converted: Vec<_> = arr.into();
        assert_eq!(&converted, &[[1.0, 2.0], [3.0, 4.0], [5.0, 6.0]]);
        // drop it
        drop_float_array(converted.into());
    }

    #[test]
    fn test_drop_empty_float_array() {
        let original = vec![[2.0, 1.0], [4.0, 3.0]];
        // move into an Array, and leak it
        let mut arr: Array = original.into();
        // zero Array contents
        arr.data = ptr::null();
        drop_float_array(arr);
    }

    #[test]
    fn test_coordinate_conversion() {
        let input = vec![[2.0, 1.0], [4.0, 3.0]];
        let output = "_ibE_seK_seK_seK";
        let input_arr: Array = input.into();
        let transformed: String = super::string_from_arr(input_arr, 5);
        assert_eq!(transformed, output);
    }

    #[test]
    fn test_string_conversion() {
        let input = "_ibE_seK_seK_seK";
        let output = [[2.0, 1.0], [4.0, 3.0]];
        // String to Array
        let transformed: Array = super::arr_from_string(input, 5);
        // Array to Vec
        let transformed_arr: Vec<_> = transformed.into();
        assert_eq!(&transformed_arr, &output);
    }

    #[test]
    #[should_panic]
    fn test_bad_string_conversion() {
        let input = "_p~iF~ps|U_u🗑lLnnqC_mqNvxq`@";
        let output = vec![[1.0, 2.0], [3.0, 4.0]];
        // String to Array
        let transformed: Array = super::arr_from_string(input, 5);
        // Array to Vec
        let transformed_arr: Vec<_> = transformed.into();
        // this will fail, bc transformed_arr is [[NaN, NaN]]
        assert_eq!(transformed_arr, output.as_slice());
    }

    #[test]
    fn test_ffi_polyline_decoding() {
        let cstr = CString::new("_ibE_seK_seK_seK").unwrap();
        let ptr = cstr.as_ptr();
        let result: Vec<_> = unsafe { decode_polyline_ffi(ptr, 5).into() };
        assert_eq!(&result, &[[2.0, 1.0], [4.0, 3.0]]);
        drop_float_array(result.into());
    }

    #[test]
    #[should_panic]
    fn test_bad_precision_decode() {
        let cstr = CString::new("_ibE_seK_seK_seK").unwrap();
        let ptr = cstr.as_ptr();
        let result: Vec<_> = unsafe { decode_polyline_ffi(ptr, 7).into() };
        assert_eq!(&result, &[[2.0, 1.0], [4.0, 3.0]]);
        drop_float_array(result.into());
    }

    #[test]
    fn test_ffi_coordinate_encoding() {
        let input: Array = vec![[2.0, 1.0], [4.0, 3.0]].into();
        let output = "_ibE_seK_seK_seK".to_string();
        let pl = encode_coordinates_ffi(input, 5);
        // Allocate a new String
        let result = unsafe { CStr::from_ptr(pl).to_str().unwrap() };
        assert_eq!(&result, &output);
        // Drop received FFI data
        unsafe { drop_cstring(pl) };
    }

    #[test]
    #[should_panic]
    fn test_bad_precision_encode() {
        let input: Array = vec![[1.0, 2.0], [3.0, 4.0]].into();
        let output = "_ibE_seK_seK_seK".to_string();
        let pl = encode_coordinates_ffi(input, 4);
        // Allocate a new String
        let result = unsafe { CStr::from_ptr(pl).to_str().unwrap() };
        assert_eq!(&result, &output);
        // Drop received FFI data
        unsafe { drop_cstring(pl) };
    }

    #[test]
    fn test_long_vec() {
        use std::clone::Clone;
        let arr = include!("../test_fixtures/berlin.rs");
        let s = include!("../test_fixtures/berlin_decoded.rs");
        for _ in 0..9999 {
            let a = arr.clone().into();
            let s_ = s.clone();
            let n = 5;
            let encoded = encode_coordinates_ffi(a, n);
            let result = unsafe { CStr::from_ptr(encoded).to_str().unwrap() };
            assert_eq!(&result, &s_);
            unsafe { drop_cstring(encoded) };
        }
    }
}
<|endoftext|>


<commit_before>#[derive(Clone, Debug)]
pub enum Type {
    Int8,
    Int16,
    Int32,
    Int64,
    ISize,
    UInt8,
    UInt16,
    UInt32,
    UInt64,
    USize,
    Float32,
    Float64,
    Char,
    Bool,
    Array(Box<Type>, usize),
    Compound(Vec<Field>, usize)
}

impl Type {
    /// Returns the total size of a type value in bytes.
    pub fn size(&self) -> usize {
        match self {
            &Type::Int8 | &Type::UInt8 | &Type::Bool => 1,
            &Type::Int16 | &Type::UInt16 => 2,
            &Type::Int32 | &Type::UInt32 | &Type::Float32 => 4,
            &Type::Int64 | &Type::UInt64 | &Type::Float64 => 8,
            &Type::Char => ::std::mem::size_of::<char>(),
            &Type::USize => ::std::mem::size_of::<usize>(),
            &Type::ISize => ::std::mem::size_of::<isize>(),
            &Type::Array(ref ty, num) => ty.size() * num,
            &Type::Compound(_, size) => size,
        }
    }

    /// Returns true if the underlying type is a scalar.
    pub fn is_scalar(&self) -> bool {
        !self.is_array() && !self.is_compound()
    }

    /// Returns true if the underlying type is a fixed-size array.
    pub fn is_array(&self) -> bool {
        if let &Type::Array(_, _) = self { true } else { false }
    }

    /// Returns true if the underlying type is compound.
    pub fn is_compound(&self) -> bool {
        if let &Type::Compound(_, _) = self { true } else { false }
    }
}

#[derive(Clone, Debug)]
pub struct Field {
    pub ty: Type,
    pub name: String,
    pub offset: usize,
}

impl Field {
    pub fn new<S, U>(ty: &Type, name: S, offset: U) -> Field
    where S: Into<String>, U: Into<usize> {
        Field {
            ty: ty.clone(),
            name: name.into(),
            offset: offset.into()
        }
    }
}

pub trait TypeInfo : Copy {
    fn type_info() -> Type;
}

macro_rules! impl_scalar {
    ($t:ty, $i:ident) => (
        impl $crate::TypeInfo for $t {
            #[inline(always)]
            fn type_info() -> $crate::Type {
                $crate::Type::$i
            }
        }
    )
}

// implement TypeInfo for built-iun scalar types
impl_scalar!(i8, Int8);
impl_scalar!(i16, Int16);
impl_scalar!(i32, Int32);
impl_scalar!(i64, Int64);
impl_scalar!(isize, ISize);
impl_scalar!(u8, UInt8);
impl_scalar!(u16, UInt16);
impl_scalar!(u32, UInt32);
impl_scalar!(u64, UInt64);
impl_scalar!(usize, USize);
impl_scalar!(f32, Float32);
impl_scalar!(f64, Float64);
impl_scalar!(char, Char);
impl_scalar!(bool, Bool);

macro_rules! impl_array {
    ($($n:expr),*$(,)*) => {
        $(
            impl<T: $crate::TypeInfo> $crate::TypeInfo for [T; $n] {
                #[inline(always)]
                fn type_info() -> $crate::Type {
                    $crate::Type::Array(
                        Box::new(<T as $crate::TypeInfo>::type_info()),
                        $n
                    )
                }
            }
        )*
    };
}

// implement TypeInfo for fixed-size arrays of lengths 0..63
impl_array!(
    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
    0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
    0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
    0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
    0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
    0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
    0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
    0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
);

#[macro_export]
macro_rules! def {
    // private struct, private fields
    ($($(#[$attr:meta])* struct $s:ident { $($i:ident: $t:ty),+$(,)* })*) => (
        $(
            #[allow(dead_code)]
            #[derive(Clone, Copy)]
            $(#[$attr])*
            struct $s { $($i: $t),* }
            def!(@impl $s { $($i: $t),+ } );
        )*
    );

    // public struct, private fields
    ($($(#[$attr:meta])* pub struct $s:ident { $($i:ident: $t:ty),+$(,)* })*) => (
        $(
            #[allow(dead_code)]
            #[derive(Clone, Copy)]
            $(#[$attr])*
            pub struct $s { $($i: $t),* }
            def!(@impl $s { $($i: $t),+ } );
        )*
    );

    // public struct, public fields
    ($($(#[$attr:meta])* pub struct $s:ident { $(pub $i:ident: $t:ty),+$(,)* })*) => (
        $(
            #[allow(dead_code)]
            #[derive(Clone, Copy)]
            $(#[$attr])*
            pub struct $s { pub $($i: $t),* }
            def!(@impl $s { $($i: $t),+ } );
        )*
    );

    // implement TypeInfo trait
    (@impl $s:ident { $($i:ident: $t:ty),+ }) => (
        impl $crate::TypeInfo for $s {
            fn type_info() -> $crate::Type {
                let base = 0usize as *const $s;
                $crate::Type::Compound(vec![$(
                    $crate::Field::new(
                        &<$t as $crate::TypeInfo>::type_info(),
                        stringify!($i),
                        unsafe { &((*base).$i) as *const $t as usize}
                    )
                ),*], ::std::mem::size_of::<$s>())
            }
        }
    );
}

#[cfg(test)]
pub mod tests {
    use super::TypeInfo;

    def! {
        #[derive(Debug)]
        pub struct Foo {
            pub a: bool,
            pub b: i32,
        }

        pub struct Bar {
            pub foo: [Foo; 2],
            pub s: f64
        }
    }

    #[test]
    fn test_smoke() {
        println!("{:#?}", Bar::type_info());
    }
}
<commit_msg>Use explicit dereference to simplify matches<commit_after>#[derive(Clone, Debug)]
pub enum Type {
    Int8,
    Int16,
    Int32,
    Int64,
    ISize,
    UInt8,
    UInt16,
    UInt32,
    UInt64,
    USize,
    Float32,
    Float64,
    Char,
    Bool,
    Array(Box<Type>, usize),
    Compound(Vec<Field>, usize)
}

impl Type {
    /// Returns the total size of a type value in bytes.
    pub fn size(&self) -> usize {
        match *self {
            Type::Int8 | Type::UInt8 | Type::Bool => 1,
            Type::Int16 | Type::UInt16 => 2,
            Type::Int32 | Type::UInt32 | Type::Float32 => 4,
            Type::Int64 | Type::UInt64 | Type::Float64 => 8,
            Type::Char => ::std::mem::size_of::<char>(),
            Type::USize => ::std::mem::size_of::<usize>(),
            Type::ISize => ::std::mem::size_of::<isize>(),
            Type::Array(ref ty, num) => ty.size() * num,
            Type::Compound(_, size) => size,
        }
    }

    /// Returns true if the underlying type is a scalar.
    pub fn is_scalar(&self) -> bool {
        !self.is_array() && !self.is_compound()
    }

    /// Returns true if the underlying type is a fixed-size array.
    pub fn is_array(&self) -> bool {
        if let Type::Array(_, _) = *self { true } else { false }
    }

    /// Returns true if the underlying type is compound.
    pub fn is_compound(&self) -> bool {
        if let Type::Compound(_, _) = *self { true } else { false }
    }
}

#[derive(Clone, Debug)]
pub struct Field {
    pub ty: Type,
    pub name: String,
    pub offset: usize,
}

impl Field {
    pub fn new<S, U>(ty: &Type, name: S, offset: U) -> Field
    where S: Into<String>, U: Into<usize> {
        Field {
            ty: ty.clone(),
            name: name.into(),
            offset: offset.into()
        }
    }
}

pub trait TypeInfo : Copy {
    fn type_info() -> Type;
}

macro_rules! impl_scalar {
    ($t:ty, $i:ident) => (
        impl $crate::TypeInfo for $t {
            #[inline(always)]
            fn type_info() -> $crate::Type {
                $crate::Type::$i
            }
        }
    )
}

// implement TypeInfo for built-iun scalar types
impl_scalar!(i8, Int8);
impl_scalar!(i16, Int16);
impl_scalar!(i32, Int32);
impl_scalar!(i64, Int64);
impl_scalar!(isize, ISize);
impl_scalar!(u8, UInt8);
impl_scalar!(u16, UInt16);
impl_scalar!(u32, UInt32);
impl_scalar!(u64, UInt64);
impl_scalar!(usize, USize);
impl_scalar!(f32, Float32);
impl_scalar!(f64, Float64);
impl_scalar!(char, Char);
impl_scalar!(bool, Bool);

macro_rules! impl_array {
    ($($n:expr),*$(,)*) => {
        $(
            impl<T: $crate::TypeInfo> $crate::TypeInfo for [T; $n] {
                #[inline(always)]
                fn type_info() -> $crate::Type {
                    $crate::Type::Array(
                        Box::new(<T as $crate::TypeInfo>::type_info()),
                        $n
                    )
                }
            }
        )*
    };
}

// implement TypeInfo for fixed-size arrays of lengths 0..63
impl_array!(
    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
    0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
    0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
    0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
    0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
    0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
    0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
    0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
);

#[macro_export]
macro_rules! def {
    // private struct, private fields
    ($($(#[$attr:meta])* struct $s:ident { $($i:ident: $t:ty),+$(,)* })*) => (
        $(
            #[allow(dead_code)]
            #[derive(Clone, Copy)]
            $(#[$attr])*
            struct $s { $($i: $t),* }
            def!(@impl $s { $($i: $t),+ } );
        )*
    );

    // public struct, private fields
    ($($(#[$attr:meta])* pub struct $s:ident { $($i:ident: $t:ty),+$(,)* })*) => (
        $(
            #[allow(dead_code)]
            #[derive(Clone, Copy)]
            $(#[$attr])*
            pub struct $s { $($i: $t),* }
            def!(@impl $s { $($i: $t),+ } );
        )*
    );

    // public struct, public fields
    ($($(#[$attr:meta])* pub struct $s:ident { $(pub $i:ident: $t:ty),+$(,)* })*) => (
        $(
            #[allow(dead_code)]
            #[derive(Clone, Copy)]
            $(#[$attr])*
            pub struct $s { pub $($i: $t),* }
            def!(@impl $s { $($i: $t),+ } );
        )*
    );

    // implement TypeInfo trait
    (@impl $s:ident { $($i:ident: $t:ty),+ }) => (
        impl $crate::TypeInfo for $s {
            fn type_info() -> $crate::Type {
                let base = 0usize as *const $s;
                $crate::Type::Compound(vec![$(
                    $crate::Field::new(
                        &<$t as $crate::TypeInfo>::type_info(),
                        stringify!($i),
                        unsafe { &((*base).$i) as *const $t as usize}
                    )
                ),*], ::std::mem::size_of::<$s>())
            }
        }
    );
}

#[cfg(test)]
pub mod tests {
    use super::TypeInfo;

    def! {
        #[derive(Debug)]
        pub struct Foo {
            pub a: bool,
            pub b: i32,
        }

        pub struct Bar {
            pub foo: [Foo; 2],
            pub s: f64
        }
    }

    #[test]
    fn test_smoke() {
        println!("{:#?}", Bar::type_info());
    }
}
<|endoftext|>


<commit_before>#[test]
fn it_works() {
}
<commit_msg>Defining some structs for reading in files.<commit_after>struct Triangle {
    normal: [f32, ..3],
    v1: [f32, ..3],
    v2: [f32, ..3],
    v3: [f32, ..3],
    attr_byte_count: u16
}

struct BinaryStlHeader {
    header: [u8, ..80],
    num_triangles: u32
}

struct BinaryStlFile {
    header: BinaryStlHeader,
    triangles: [Triangle]
}

#[test]
fn it_works() {
}
<|endoftext|>


<commit_before>#![feature(plugin_registrar)]
#![feature(quote)]
#![feature(rustc_private)]
#![feature(slice_patterns)]

extern crate rustc_plugin;
extern crate syntax;

use rustc_plugin::Registry;
use syntax::abi::Abi;
use syntax::ast::Expr_::ExprTup;
use syntax::ast::Item_::ItemFn;
use syntax::ast::{
    Constness, Expr, FnDecl, FunctionRetTy, Generics, Ident, Item_, Path, TokenTree, Unsafety
};
use syntax::codemap::{DUMMY_SP, Span, Spanned};
use syntax::ext::base::{DummyResult, ExtCtxt, MacEager, MacResult};
use syntax::ext::build::AstBuilder;
use syntax::parse::PResult;
use syntax::parse::common::seq_sep_trailing_allowed;
use syntax::parse::parser::{Parser, PathParsingMode};
use syntax::parse::token::intern;
use syntax::parse::token::keywords::Keyword;
use syntax::parse::token::{DelimToken, Token};
use syntax::ptr::P;
use syntax::util::small_vector::SmallVector;

type ItemInfo = (Ident, Item_);

#[plugin_registrar]
pub fn plugin_registrar(reg: &mut Registry) {
    reg.register_macro("plague", plague_macro);
}

pub fn plague_macro<'cx>(cx: &'cx mut ExtCtxt, span: Span, tts: &[TokenTree]) -> Box<MacResult + 'cx> {
    let mut parser = cx.new_parser_from_tts(tts);

    match parse_plague(&mut parser) {
        Ok((params, fn_, should_panic)) => {
            match make_plague(cx, &mut parser, params, fn_, should_panic) {
                Ok(r) => r,
                Err(mut err) => {
                    err.emit();
                    DummyResult::any(span)
                }
            }
        }
        Err(mut err) => {
            err.emit();
            DummyResult::any(span)
        }
    }
}

enum FnKind {
    Decl {
        name: Ident,
        fn_: Item_,
        span: Span,
    },
    Path(Path)
}

fn parse_plague<'a>(parser: &mut Parser<'a>) -> PResult<'a, (Spanned<Vec<Param>>, FnKind, bool)> {
    try!(parser.expect_keyword(Keyword::For));

    let params = try!(parser.parse_seq(
        &Token::OpenDelim(DelimToken::Bracket),
        &Token::CloseDelim(DelimToken::Bracket),
        seq_sep_trailing_allowed(Token::Comma),
        parse_param
    ));

    try!(expect_keyword(parser, "test"));

    let should_panic = try!(parser.eat(&Token::Not));

    if parser.look_ahead(1, Token::is_ident) {
        Ok((
            params,
            try!(parse_fn_decl(parser)),
            should_panic
        ))
    }
    else {
        Ok((
            params,
            try!(parse_fn_use(parser)),
            should_panic
        ))
    }
}

type Param = (P<Expr>, Option<P<Expr>>);

fn parse_param<'a>(parser: &mut Parser<'a>) -> PResult<'a, Param> {
    let expr = try!(parser.parse_expr());

    let ret = if try!(parser.eat(&Token::RArrow)) {
        Some(try!(parser.parse_expr()))
    }
    else {
        None
    };

    Ok((expr, ret))
}

fn parse_fn_use<'a>(parser: &mut Parser<'a>) -> PResult<'a, FnKind> {
    let path = try!(parser.parse_path(PathParsingMode::NoTypesAllowed));

    Ok(FnKind::Path(path))
}

fn parse_fn_decl<'a>(parser: &mut Parser<'a>) -> PResult<'a, FnKind> {
    let mut span = parser.span;
    let (constness, unsafety, abi) = try!(parser.parse_fn_front_matter());

    // Yep, copy-pasted pieces from libsyntax
    let ident = try!(parser.parse_ident());
    let mut generics = try!(parser.parse_generics());
    let decl = try!(parser.parse_fn_decl(false));
    generics.where_clause = try!(parser.parse_where_clause());
    let body = try!(parser.parse_block());
    let fn_ = ItemFn(decl, unsafety, constness, abi, generics, body);

    span.hi = parser.last_span.hi;

    Ok(FnKind::Decl{ name: ident, fn_: fn_, span: span })
}

fn expect_keyword<'a>(parser: &mut Parser<'a>, kw: &str) -> PResult<'a, ()> {
    match parser.parse_ident() {
        Ok(test_ident) if test_ident.name.as_str() == kw => Ok(()),
        Ok(ident) => {
            Err(parser.fatal(&format!("expected `test`, found `{}`", ident)))
        }
        Err(mut err) => {
            err.cancel();
            Err(parser.fatal(&format!("expected `test`, found `{}`", parser.this_token_to_string())))
        }
    }
}

fn make_plague<'cx, 'a>(
    cx: &'cx mut ExtCtxt,
    parser: &mut Parser<'a>,
    params: Spanned<Vec<Param>>,
    fn_: FnKind,
    should_panic: bool
) -> PResult<'a, Box<MacResult + 'cx>> {
    if params.node.is_empty() {
        cx.span_err(params.span, "empty parametrized tests are useless");
    }

    let mut fns = Vec::with_capacity(params.node.len());

    let (ident, fn_, unpack_tuple) = match fn_ {
        FnKind::Decl { name, fn_, span } => {
            let unpack_tuple = if let &ItemFn(ref decl, _, _, _, _, _) = &fn_ {
                decl.inputs.len() > 1
            }
            else {
                panic!();
            };

            let unused = cx.attribute(span, cx.meta_list(
                    span,
                    intern("allow").as_str(),
                    vec![
                        cx.meta_word(span, intern("unused").as_str())
                    ]
            ));
            fns.push(cx.item(span, name, vec![unused], fn_));

            (name, cx.expr_ident(span, name), unpack_tuple)
        }
        FnKind::Path(path) => {
            let name = path.segments.iter().last().unwrap().identifier;
            // TODO: find a way to resolve the function
            (name, cx.expr_path(path), true)
        }
    };

    let attributes = {
        let make_attr = |name| {
            cx.attribute(DUMMY_SP, cx.meta_word(DUMMY_SP, intern(name).as_str()))
        };

        if should_panic {
            vec![make_attr("test"), make_attr("should_panic")]
        }
        else {
            vec![make_attr("test")]
        }
    };

    let span = params.span;
    for (i, param) in params.node.iter().enumerate() {
        let params = try!(make_params(parser, &param.0, unpack_tuple));
        let fn_ = make_test_fn(cx, span, fn_.clone(), params, &param.1);

        fns.push(cx.item(
            span,
            Ident::new(intern(&format!("{}_{}", ident.name, i)), ident.ctxt),
            attributes.clone(),
            fn_
        ));
    }

    Ok(MacEager::items(SmallVector::many(fns)))
}

fn make_test_fn<'cx>(
    cx: &'cx mut ExtCtxt,
    span: Span,
    fn_: P<Expr>,
    params: Vec<P<Expr>>,
    ret: &Option<P<Expr>>,
) -> Item_ {
    let call = cx.expr_call(span, fn_, params);
    let block = if let Some(ref exp_ret) = *ret {
        let expr = quote_expr!(cx, {
            assert_eq!($exp_ret, $call);
        });
        cx.block_expr(expr)
    }
    else {
        cx.block_expr(call)
    };

    Item_::ItemFn(
        P(FnDecl { inputs: vec![], output: FunctionRetTy::DefaultReturn(DUMMY_SP), variadic: false }),
        Unsafety::Normal,
        Constness::NotConst,
        Abi::Rust,
        Generics::default(),
        block
    )
}

fn make_params<'a>(
    parser: &mut Parser<'a>,
    params: &P<Expr>,
    unpack_tuple: bool
) -> PResult<'a, Vec<P<Expr>>> {
    if !unpack_tuple {
        Ok(vec![params.clone()])
    }
    else if let ExprTup(ref params) = params.node {
        Ok(params.clone())
    }
    else {
        Err(parser.span_fatal(params.span, "expected tuple, the test function has several arguments"))
    }
}
<commit_msg>Use more quote_*! goodies<commit_after>#![feature(plugin_registrar)]
#![feature(quote)]
#![feature(rustc_private)]
#![feature(slice_patterns)]

extern crate rustc_plugin;
extern crate syntax;

use rustc_plugin::Registry;
use syntax::abi::Abi;
use syntax::ast::Expr_::ExprTup;
use syntax::ast::Item_::ItemFn;
use syntax::ast::{
    Constness, Expr, FnDecl, FunctionRetTy, Generics, Ident, Item_, Path, TokenTree, Unsafety
};
use syntax::codemap::{DUMMY_SP, Span, Spanned};
use syntax::ext::base::{DummyResult, ExtCtxt, MacEager, MacResult};
use syntax::ext::build::AstBuilder;
use syntax::parse::PResult;
use syntax::parse::common::seq_sep_trailing_allowed;
use syntax::parse::parser::{Parser, PathParsingMode};
use syntax::parse::token::intern;
use syntax::parse::token::keywords::Keyword;
use syntax::parse::token::{DelimToken, Token};
use syntax::ptr::P;
use syntax::util::small_vector::SmallVector;

type ItemInfo = (Ident, Item_);

#[plugin_registrar]
pub fn plugin_registrar(reg: &mut Registry) {
    reg.register_macro("plague", plague_macro);
}

pub fn plague_macro<'cx>(cx: &'cx mut ExtCtxt, span: Span, tts: &[TokenTree]) -> Box<MacResult + 'cx> {
    let mut parser = cx.new_parser_from_tts(tts);

    match parse_plague(&mut parser) {
        Ok((params, fn_, should_panic)) => {
            match make_plague(cx, &mut parser, params, fn_, should_panic) {
                Ok(r) => r,
                Err(mut err) => {
                    err.emit();
                    DummyResult::any(span)
                }
            }
        }
        Err(mut err) => {
            err.emit();
            DummyResult::any(span)
        }
    }
}

enum FnKind {
    Decl {
        name: Ident,
        fn_: Item_,
        span: Span,
    },
    Path(Path)
}

fn parse_plague<'a>(parser: &mut Parser<'a>) -> PResult<'a, (Spanned<Vec<Param>>, FnKind, bool)> {
    try!(parser.expect_keyword(Keyword::For));

    let params = try!(parser.parse_seq(
        &Token::OpenDelim(DelimToken::Bracket),
        &Token::CloseDelim(DelimToken::Bracket),
        seq_sep_trailing_allowed(Token::Comma),
        parse_param
    ));

    try!(expect_keyword(parser, "test"));

    let should_panic = try!(parser.eat(&Token::Not));

    if parser.look_ahead(1, Token::is_ident) {
        Ok((
            params,
            try!(parse_fn_decl(parser)),
            should_panic
        ))
    }
    else {
        Ok((
            params,
            try!(parse_fn_use(parser)),
            should_panic
        ))
    }
}

type Param = (P<Expr>, Option<P<Expr>>);

fn parse_param<'a>(parser: &mut Parser<'a>) -> PResult<'a, Param> {
    let expr = try!(parser.parse_expr());

    let ret = if try!(parser.eat(&Token::RArrow)) {
        Some(try!(parser.parse_expr()))
    }
    else {
        None
    };

    Ok((expr, ret))
}

fn parse_fn_use<'a>(parser: &mut Parser<'a>) -> PResult<'a, FnKind> {
    let path = try!(parser.parse_path(PathParsingMode::NoTypesAllowed));

    Ok(FnKind::Path(path))
}

fn parse_fn_decl<'a>(parser: &mut Parser<'a>) -> PResult<'a, FnKind> {
    let mut span = parser.span;
    let (constness, unsafety, abi) = try!(parser.parse_fn_front_matter());

    // Yep, copy-pasted pieces from libsyntax
    let ident = try!(parser.parse_ident());
    let mut generics = try!(parser.parse_generics());
    let decl = try!(parser.parse_fn_decl(false));
    generics.where_clause = try!(parser.parse_where_clause());
    let body = try!(parser.parse_block());
    let fn_ = ItemFn(decl, unsafety, constness, abi, generics, body);

    span.hi = parser.last_span.hi;

    Ok(FnKind::Decl{ name: ident, fn_: fn_, span: span })
}

fn expect_keyword<'a>(parser: &mut Parser<'a>, kw: &str) -> PResult<'a, ()> {
    match parser.parse_ident() {
        Ok(test_ident) if test_ident.name.as_str() == kw => Ok(()),
        Ok(ident) => {
            Err(parser.fatal(&format!("expected `test`, found `{}`", ident)))
        }
        Err(mut err) => {
            err.cancel();
            Err(parser.fatal(&format!("expected `test`, found `{}`", parser.this_token_to_string())))
        }
    }
}

fn make_plague<'cx, 'a>(
    cx: &'cx mut ExtCtxt,
    parser: &mut Parser<'a>,
    params: Spanned<Vec<Param>>,
    fn_: FnKind,
    should_panic: bool
) -> PResult<'a, Box<MacResult + 'cx>> {
    if params.node.is_empty() {
        cx.span_err(params.span, "empty parametrized tests are useless");
    }

    let mut fns = Vec::with_capacity(params.node.len());

    let (ident, fn_, unpack_tuple) = match fn_ {
        FnKind::Decl { name, fn_, span } => {
            let unpack_tuple = if let &ItemFn(ref decl, _, _, _, _, _) = &fn_ {
                decl.inputs.len() > 1
            }
            else {
                panic!();
            };

            let unused = quote_meta_item!(cx, allow(unused));
            let unused = cx.attribute(span, unused);
            fns.push(cx.item(span, name, vec![unused], fn_));

            (name, cx.expr_ident(span, name), unpack_tuple)
        }
        FnKind::Path(path) => {
            let name = path.segments.iter().last().unwrap().identifier;
            // TODO: find a way to resolve the function
            (name, cx.expr_path(path), true)
        }
    };

    let attributes = if should_panic {
        vec![
            cx.attribute(DUMMY_SP, quote_meta_item!(cx, test)),
            cx.attribute(DUMMY_SP, quote_meta_item!(cx, should_panic)),
        ]
    }
    else {
        vec![cx.attribute(DUMMY_SP, quote_meta_item!(cx, test))]
    };

    let span = params.span;
    for (i, param) in params.node.iter().enumerate() {
        let params = try!(make_params(parser, &param.0, unpack_tuple));
        let fn_ = make_test_fn(cx, span, fn_.clone(), params, &param.1);

        fns.push(cx.item(
            span,
            Ident::new(intern(&format!("{}_{}", ident.name, i)), ident.ctxt),
            attributes.clone(),
            fn_
        ));
    }

    Ok(MacEager::items(SmallVector::many(fns)))
}

fn make_test_fn<'cx>(
    cx: &'cx mut ExtCtxt,
    span: Span,
    fn_: P<Expr>,
    params: Vec<P<Expr>>,
    ret: &Option<P<Expr>>,
) -> Item_ {
    let call = cx.expr_call(span, fn_, params);
    let call = if let Some(ref exp_ret) = *ret {
        quote_expr!(cx, {
            assert_eq!($exp_ret, $call);
        })
    }
    else {
        call
    };

    let block = cx.block_expr(call);

    Item_::ItemFn(
        P(FnDecl { inputs: vec![], output: FunctionRetTy::DefaultReturn(DUMMY_SP), variadic: false }),
        Unsafety::Normal,
        Constness::NotConst,
        Abi::Rust,
        Generics::default(),
        block
    )
}

fn make_params<'a>(
    parser: &mut Parser<'a>,
    params: &P<Expr>,
    unpack_tuple: bool
) -> PResult<'a, Vec<P<Expr>>> {
    if !unpack_tuple {
        Ok(vec![params.clone()])
    }
    else if let ExprTup(ref params) = params.node {
        Ok(params.clone())
    }
    else {
        Err(parser.span_fatal(params.span, "expected tuple, the test function has several arguments"))
    }
}
<|endoftext|>


<commit_before>
pub trait BaseTime {
  pub const Self MIN;
  pub const Self MAX;
  pub const Self NEVER;
}
impl BaseTime for i64 {

}

pub trait Field {
  type Data = Self;
  const unique_identifier: u64;
}

#[derive (Clone, PartialEq, Eq, PartialOrd, Ord)]
struct GenericExtendedTime <Base: BaseTime> {
  base: Base,
  iteration: u64,
  id: SiphashID,
}

pub trait Basics {
  type Time: BaseTime;
  type Constants;
}
type ExtendedTime <B: Basics> = GenericExtendedTime <B::Time>;

pub trait Accessor {
  pub fn get <Type: Field> (&mut self, id: SiphashID)-> Option <& Field::Data>;
}

pub trait Mutator <B: Basics>: Accessor {
  pub fn get_mutable <Type: Field> (&mut self, id: SiphashID)->& mut Option <Field::Data>;
  pub fn set <Type: Field> (&mut self, id: SiphashID, Option <Field::Data> data);
  pub fn random_bits (&mut self, bits:u32)->u64;
  pub fn random_id (&mut self)->SiphashID;
  pub fn now (& self)->B::Time;
  pub fn constants (& self)->& B::Constants;
}
pub trait PredictorAccessor <B: Basics>: Accessor {

}

pub type Event =Box <Fn (&mut Mutator)>;
enum Prediction <Time: BaseTime> {
  Nothing,
  Immediately (Event),
  At (Time, Event),
}

pub trait TimeSteward <B: Basics> {
  pub type A: Accessor;
  pub type M: Mutator;
  pub type P: PredictorAccessor;
  pub type Predictor = Box <Fn(&mut P)->Prediction>;

  pub fn new (constants: B::Constants, predictors: Vec<Predictor>)->Self;
  pub fn insert_fiat_event (&mut self, time: B::Time, distinguisher: u64, Event)->bool;
  pub fn erase_fiat_event (&mut self, time: B::Time, distinguisher: u64)->bool;
  pub fn accessor_after (&mut self, time: B::Time)->Accessor;
}

pub trait FlatTimeSteward <B: Basics>:TimeSteward <B> {
  pub fn valid_strictly_between (& self)->(B::Time, B::Time);
}

pub struct StandardFlatTimeSteward <B: Basics> {
  entity_states: HashMap< SiphashID, Box <Any>>,
  last_change: B::Time,
  fiat_events: BTreeMap< ExtendedTime <B>, Event>,
  predictions: HashMap<ExtendedTime <B>,
  prediction_times:
}
struct StandardFlatTimeStewardAccessor <'a, B: Basics> {
  steward: &'a StandardFlatTimeSteward <B>,
}
impl <'a, B: Basics> Accessor for StandardFlatTimeStewardAccessor <'a, B: Basics> {
  pub fn get <Type: Field> (&mut self, id: SiphashID)-> Option <& Field::Data> {
    self.steward.entity_states.get (id).map (| box | box.downcast <Field::Data>.unwrap ().borrow ()
  }
}
struct StandardFlatTimeStewardMutator <'a, B: Basics> {
  steward: & mut 'a StandardFlatTimeSteward <B>,
}
impl <'a, B: Basics> Accessor for StandardFlatTimeStewardMutator <'a, B: Basics> {
  pub fn get <Type: Field> (&mut self, id: SiphashID)-> Option <& Field::Data> {
    self.steward.entity_states.get (id)
  }
}

impl <B: Basics> TimeSteward <B> for StandardFlatTimeSteward <B> {
  pub type A: StandardFlatTimeStewardAccessor;
  pub type M: StandardFlatTimeStewardMutator;
  pub type P: StandardFlatTimeStewardPredictorAccessor;
  pub type Predictor = Box <Fn(&mut P)->Prediction>;

  pub fn new (constants: B::Constants, predictors: Vec<Predictor>)->Self;
  pub fn insert_fiat_event (&mut self, time: B::Time, distinguisher: u64, Event)->bool;
  pub fn erase_fiat_event (&mut self, time: B::Time, distinguisher: u64)->bool;
  pub fn accessor_after (&mut self, time: B::Time)->Accessor;
}

 #[test]
fn it_works() {
}
<commit_msg>More implementing<commit_after>use std::collections::{HashMap, BinaryHeap};
use std::hash::{Hash, Hasher,SipHasher};
use std::any::Any;

#[derive (Clone, PartialEq, Eq, PartialOrd, Ord)]
pub struct SiphashID {
data: [u64; 2],
}
pub struct SiphashID_Generator {
data: [SipHasher; 2],
}
impl Hasher for SiphashID_Generator {
fn finish (& self)->u64 {panic!()}//Hack: this is actually for generating SiphashID, not 64-bit
fn write (&mut self, bytes: & [u8]) {self.data [0].write (bytes);self.data [1].write (bytes);}
//TODO: should we implement the others for efficiency?
}
impl SiphashID_Generator {
fn generate (& self)->SiphashID {SiphashID {data: [self.data [0].finish (), self.data [1].finish ()]}}
}

pub trait BaseTime: Ord {
  fn min_time ()->Self;
  fn max_time ()->Self;
}
impl BaseTime for i64 {
  fn min_time ()->i64 {i64::min_value ()}
  fn max_time ()->i64 {i64::max_value ()}
}

pub trait Field {
  type Data;// = Self;
  fn unique_identifier ()->u64;
}

#[derive (Clone, PartialEq, Eq, PartialOrd, Ord)]
struct GenericExtendedTime <Base: BaseTime> {
  base: Base,
  iteration: u64,
  id: SiphashID,
}

pub trait Basics {
  type Time: BaseTime;
  type Constants;
}
type ExtendedTime <B: Basics> = GenericExtendedTime <B::Time>;

//Note: in the future, we expect we might use a custom hash table type that knows it can rely on SiphashID to already be random, so we don't need to hash it again.
impl Hash for SiphashID {
  fn hash <H: Hasher> (& self, state: &mut H) {self.data [0].hash (state);}
}
impl <Base: BaseTime> Hash for GenericExtendedTime <Base>{
  fn hash <H: Hasher> (& self, state: &mut H) {self.id.hash (state);}
}

pub trait Accessor {
  fn get <Type: Field> (&mut self, id: SiphashID)-> Option <& Type::Data>;
}

pub trait Mutator <B: Basics>: Accessor {
  fn get_mutable <Type: Field> (&mut self, id: SiphashID)->Option <&mut Type::Data>;
  fn set <Type: Field> (&mut self, id: SiphashID, data: Option <Type::Data>);
  fn random_bits (&mut self, bits:u32)->u64;
  fn random_id (&mut self)->SiphashID;
  fn now (& self)->B::Time;
}
pub trait PredictorAccessor <B: Basics>: Accessor {

}

pub type Event<B: Basics>  =Box <Fn (&mut Mutator <B>)>;
enum Prediction <B: Basics> {
  Nothing,
  Immediately (Event <B>),
  At (B::Time, Event <B>),
}

pub trait TimeSteward <B: Basics> {
  type A: Accessor;
  type M: Mutator<B>;
  type P: PredictorAccessor<B>;
  type Predictor;// = Box <Fn(&mut P)->Prediction>;

  fn new (constants: B::Constants, predictors: Vec<Self::Predictor>)->Self;
  fn insert_fiat_event (&mut self, time: B::Time, distinguisher: u64, event: Event<B>)->bool;
  fn erase_fiat_event (&mut self, time: B::Time, distinguisher: u64)->bool;
  fn accessor_after (&mut self, time: B::Time)->Accessor;
}

pub trait FlatTimeSteward <B: Basics>:TimeSteward <B> {
  fn valid_strictly_between (& self)->(B::Time, B::Time);
}

pub struct StandardFlatTimeSteward <B: Basics> {
  entity_states: HashMap< SiphashID, Box <Any>>,
  last_change: B::Time,
  fiat_events: HashMap< ExtendedTime <B>, Event<B>>,
  predictions: HashMap<ExtendedTime <B>, Prediction<B>>,
  upcoming_event_times: BinaryHeap <ExtendedTime <B>>,

}
struct StandardFlatTimeStewardAccessor <'a, B: Basics> {
  steward: &'a StandardFlatTimeSteward <B>,
}
struct StandardFlatTimeStewardMutator <'a, B: Basics> {
  steward: & 'a mut StandardFlatTimeSteward <B>,
}
struct StandardFlatTimeStewardPredictorAccessor <'a, B: Basics> {
  steward: & 'a mut StandardFlatTimeSteward <B>,
}

impl <'a, B: Basics> Accessor for StandardFlatTimeStewardAccessor <'a, B> {
  pub fn get <Type: Field> (&mut self, id: SiphashID)-> Option <& Type::Data> {
    self.steward.entity_states.get (id).map (| something | something.downcast ::<Field::Data> ().unwrap ().borrow ())
  }
}
impl <'a, B: Basics> Accessor for StandardFlatTimeStewardMutator <'a, B> {
  pub fn get <Type: Field> (&mut self, id: SiphashID)-> Option <& Type::Data> {
    self.steward.entity_states.get (id)
  }
}

impl <B: Basics> TimeSteward <B> for StandardFlatTimeSteward <B> {
  pub type A = StandardFlatTimeStewardAccessor<'a, B> ;
  pub type M = StandardFlatTimeStewardMutator<'a, B> ;
  pub type P = StandardFlatTimeStewardPredictorAccessor<'a, B> ;
  pub type Predictor = Box <Fn(&mut Self::P)->Prediction>;

  pub fn new (constants: B::Constants, predictors: Vec<Self::Predictor>)->Self {}
  pub fn insert_fiat_event (&mut self, time: B::Time, distinguisher: u64, event: Event)->bool {}
  pub fn erase_fiat_event (&mut self, time: B::Time, distinguisher: u64)->bool {}
  pub fn accessor_after (&mut self, time: B::Time)->Accessor {}
}

 #[test]
fn it_works() {
}
<|endoftext|>


<commit_before>#![allow(trivial_casts)]
#![cfg_attr(test, deny(warnings))]

extern crate conduit;
extern crate conduit_mime_types as mime;
extern crate time;
extern crate filetime;
#[cfg(test)] extern crate tempdir;

use std::collections::HashMap;
use std::error::Error;
use std::io;
use std::path::{PathBuf, Path};
use std::fs::File;
use conduit::{Request, Response, Handler};
use filetime::FileTime;

pub struct Static {
    path: PathBuf,
    types: mime::Types
}

impl Static {
    pub fn new<P: AsRef<Path>>(path: P) -> Static {
        Static {
            path: path.as_ref().to_path_buf(),
            types: mime::Types::new()
                .ok().expect("Couldn't load mime-types")
        }
    }
}

impl Handler for Static {
    #[allow(deprecated)]
    fn call(&self, request: &mut dyn Request) -> Result<Response, Box<dyn Error+Send>> {
        let request_path = &request.path()[1..];
        if request_path.contains("..") { return Ok(not_found()) }

        let path = self.path.join(request_path);
        let mime = self.types.mime_for_path(&path);
        let file = match File::open(&path) {
            Ok(f) => f,
            Err(..) => return Ok(not_found()),
        };
        let data = try!(file.metadata().map_err(|e| Box::new(e) as Box<dyn Error+Send>));
        if data.is_dir() {
            return Ok(not_found())
        }
        let mtime = FileTime::from_last_modification_time(&data);
        let ts = time::Timespec {
            sec: mtime.seconds_relative_to_1970() as i64,
            nsec: mtime.nanoseconds() as i32,
        };
        let tm = time::at(ts).to_utc();

        let mut headers = HashMap::new();
        headers.insert("Content-Type".to_string(), vec![mime.to_string()]);
        headers.insert("Content-Length".to_string(),
                       vec![data.len().to_string()]);
        headers.insert("Last-Modified".to_string(),
                       vec![tm.strftime("%a, %d %b %Y %T GMT").unwrap()
                              .to_string()]);

        Ok(Response {
            status: (200, "OK"),
            headers: headers,
            body: Box::new(file),
        })
    }
}

fn not_found() -> Response {
    let mut headers = HashMap::new();
    headers.insert("Content-Length".to_string(), vec!["0".to_string()]);
    headers.insert("Content-Type".to_string(), vec!["text/plain".to_string()]);
    Response {
        status: (404, "Not Found"),
        headers: headers,
        body: Box::new(io::empty()),
    }
}

#[cfg(test)]
mod tests {
    extern crate conduit_test as test;

    use std::fs::{self, File};
    use std::io::prelude::*;
    use tempdir::TempDir;

    use conduit::{Handler, Method};
    use Static;

    #[test]
    fn test_static() {
        let td = TempDir::new("conduit-static").unwrap();
        let root = td.path();
        let handler = Static::new(root.clone());
        File::create(&root.join("Cargo.toml")).unwrap()
             .write_all(b"[package]").unwrap();
        let mut req = test::MockRequest::new(Method::Get, "/Cargo.toml");
        let mut res = handler.call(&mut req).ok().expect("No response");
        let mut body = Vec::new();
        res.body.write_body(&mut body).unwrap();
        assert_eq!(body, b"[package]");
        assert_eq!(res.headers.get("Content-Type"),
                   Some(&vec!("text/plain".to_string())));
        assert_eq!(res.headers.get("Content-Length"),
                   Some(&vec!["9".to_string()]));
    }

    #[test]
    fn test_mime_types() {
        let td = TempDir::new("conduit-static").unwrap();
        let root = td.path();
        fs::create_dir(&root.join("src")).unwrap();
        File::create(&root.join("src/fixture.css")).unwrap();

        let handler = Static::new(root.clone());
        let mut req = test::MockRequest::new(Method::Get, "/src/fixture.css");
        let res = handler.call(&mut req).ok().expect("No response");
        assert_eq!(res.headers.get("Content-Type"),
                   Some(&vec!("text/css".to_string())));
        assert_eq!(res.headers.get("Content-Length"),
                   Some(&vec!["0".to_string()]));
    }

    #[test]
    fn test_missing() {
        let td = TempDir::new("conduit-static").unwrap();
        let root = td.path();

        let handler = Static::new(root.clone());
        let mut req = test::MockRequest::new(Method::Get, "/nope");
        let res = handler.call(&mut req).ok().expect("No response");
        assert_eq!(res.status.0, 404);
    }

    #[test]
    fn test_dir() {
        let td = TempDir::new("conduit-static").unwrap();
        let root = td.path();

        fs::create_dir(&root.join("foo")).unwrap();

        let handler = Static::new(root.clone());
        let mut req = test::MockRequest::new(Method::Get, "/foo");
        let res = handler.call(&mut req).ok().expect("No response");
        assert_eq!(res.status.0, 404);
    }

    #[test]
    fn last_modified() {
        let td = TempDir::new("conduit-static").unwrap();
        let root = td.path();
        File::create(&root.join("test")).unwrap();
        let handler = Static::new(root.clone());
        let mut req = test::MockRequest::new(Method::Get, "/test");
        let res = handler.call(&mut req).ok().expect("No response");
        assert_eq!(res.status.0, 200);
        assert!(res.headers.get("Last-Modified").is_some());
    }
}
<commit_msg>Use `?` instead of try! macro<commit_after>#![allow(trivial_casts)]
#![cfg_attr(test, deny(warnings))]

extern crate conduit;
extern crate conduit_mime_types as mime;
extern crate time;
extern crate filetime;
#[cfg(test)] extern crate tempdir;

use std::collections::HashMap;
use std::error::Error;
use std::io;
use std::path::{PathBuf, Path};
use std::fs::File;
use conduit::{Request, Response, Handler};
use filetime::FileTime;

pub struct Static {
    path: PathBuf,
    types: mime::Types
}

impl Static {
    pub fn new<P: AsRef<Path>>(path: P) -> Static {
        Static {
            path: path.as_ref().to_path_buf(),
            types: mime::Types::new()
                .ok().expect("Couldn't load mime-types")
        }
    }
}

impl Handler for Static {
    #[allow(deprecated)]
    fn call(&self, request: &mut dyn Request) -> Result<Response, Box<dyn Error+Send>> {
        let request_path = &request.path()[1..];
        if request_path.contains("..") { return Ok(not_found()) }

        let path = self.path.join(request_path);
        let mime = self.types.mime_for_path(&path);
        let file = match File::open(&path) {
            Ok(f) => f,
            Err(..) => return Ok(not_found()),
        };
        let data = file.metadata().map_err(|e| Box::new(e) as Box<dyn Error+Send>)?;
        if data.is_dir() {
            return Ok(not_found())
        }
        let mtime = FileTime::from_last_modification_time(&data);
        let ts = time::Timespec {
            sec: mtime.seconds_relative_to_1970() as i64,
            nsec: mtime.nanoseconds() as i32,
        };
        let tm = time::at(ts).to_utc();

        let mut headers = HashMap::new();
        headers.insert("Content-Type".to_string(), vec![mime.to_string()]);
        headers.insert("Content-Length".to_string(),
                       vec![data.len().to_string()]);
        headers.insert("Last-Modified".to_string(),
                       vec![tm.strftime("%a, %d %b %Y %T GMT").unwrap()
                              .to_string()]);

        Ok(Response {
            status: (200, "OK"),
            headers: headers,
            body: Box::new(file),
        })
    }
}

fn not_found() -> Response {
    let mut headers = HashMap::new();
    headers.insert("Content-Length".to_string(), vec!["0".to_string()]);
    headers.insert("Content-Type".to_string(), vec!["text/plain".to_string()]);
    Response {
        status: (404, "Not Found"),
        headers: headers,
        body: Box::new(io::empty()),
    }
}

#[cfg(test)]
mod tests {
    extern crate conduit_test as test;

    use std::fs::{self, File};
    use std::io::prelude::*;
    use tempdir::TempDir;

    use conduit::{Handler, Method};
    use Static;

    #[test]
    fn test_static() {
        let td = TempDir::new("conduit-static").unwrap();
        let root = td.path();
        let handler = Static::new(root.clone());
        File::create(&root.join("Cargo.toml")).unwrap()
             .write_all(b"[package]").unwrap();
        let mut req = test::MockRequest::new(Method::Get, "/Cargo.toml");
        let mut res = handler.call(&mut req).ok().expect("No response");
        let mut body = Vec::new();
        res.body.write_body(&mut body).unwrap();
        assert_eq!(body, b"[package]");
        assert_eq!(res.headers.get("Content-Type"),
                   Some(&vec!("text/plain".to_string())));
        assert_eq!(res.headers.get("Content-Length"),
                   Some(&vec!["9".to_string()]));
    }

    #[test]
    fn test_mime_types() {
        let td = TempDir::new("conduit-static").unwrap();
        let root = td.path();
        fs::create_dir(&root.join("src")).unwrap();
        File::create(&root.join("src/fixture.css")).unwrap();

        let handler = Static::new(root.clone());
        let mut req = test::MockRequest::new(Method::Get, "/src/fixture.css");
        let res = handler.call(&mut req).ok().expect("No response");
        assert_eq!(res.headers.get("Content-Type"),
                   Some(&vec!("text/css".to_string())));
        assert_eq!(res.headers.get("Content-Length"),
                   Some(&vec!["0".to_string()]));
    }

    #[test]
    fn test_missing() {
        let td = TempDir::new("conduit-static").unwrap();
        let root = td.path();

        let handler = Static::new(root.clone());
        let mut req = test::MockRequest::new(Method::Get, "/nope");
        let res = handler.call(&mut req).ok().expect("No response");
        assert_eq!(res.status.0, 404);
    }

    #[test]
    fn test_dir() {
        let td = TempDir::new("conduit-static").unwrap();
        let root = td.path();

        fs::create_dir(&root.join("foo")).unwrap();

        let handler = Static::new(root.clone());
        let mut req = test::MockRequest::new(Method::Get, "/foo");
        let res = handler.call(&mut req).ok().expect("No response");
        assert_eq!(res.status.0, 404);
    }

    #[test]
    fn last_modified() {
        let td = TempDir::new("conduit-static").unwrap();
        let root = td.path();
        File::create(&root.join("test")).unwrap();
        let handler = Static::new(root.clone());
        let mut req = test::MockRequest::new(Method::Get, "/test");
        let res = handler.call(&mut req).ok().expect("No response");
        assert_eq!(res.status.0, 200);
        assert!(res.headers.get("Last-Modified").is_some());
    }
}
<|endoftext|>