<commit_before>use cgmath::*;
use crossbeam;
use rand;
use rand::Rng;
use utils::v3;
use ncollide as nc;
use ncollide::broad_phase::BroadPhase;
use ncollide::bounding_volume;
use nalgebra as na;

pub type V3 = Vector3<f32>;

#[derive(Clone)]
pub struct CubeState {
    pub pos: V3,
    pub rot: Basis3<f32>,
    pub spd: V3,
    pub color: V3,
    pub blink: bool,
    pub rot_axe: V3,
    pub rot_spd: f32,
}

#[derive(Clone)]
pub struct State {
    pub tick: f64,
    pub cubes: Vec<CubeState>,
    pub light_pos: V3,
}

const CUBE_SPAN : f32 = 100.;

fn fmin(a: (f32, f32), b: (f32, f32)) -> (f32, f32) {
    if a.0 < b.0 { a } else { b }
}

impl State {
    pub fn new(object_count: u32) -> Self {
        let mut rng = rand::XorShiftRng::new_unseeded();
        let mut cubes = Vec::with_capacity(object_count as usize);
        for _ in 0 .. object_count {
            let x = rng.next_f32();
            let y = rng.next_f32();
            let z = rng.next_f32();
            let cube_state = CubeState {
                pos: v3((x - 0.5) * CUBE_SPAN,
                             (y - 0.5) * CUBE_SPAN,
                             (z - 1.) * CUBE_SPAN),
                color: v3(x, y, z),
                blink: false,
                rot: <Basis3<f32> as Rotation<_>>::one(),
                spd: v3(rng.next_f32() - 0.5,
                             rng.next_f32() - 0.5,
                             rng.next_f32() - 0.5).normalize()*rng.next_f32()*1.,
                rot_axe: v3(rng.next_f32() - 0.5,
                             rng.next_f32() - 0.5,
                             rng.next_f32() - 0.5)
                              .normalize(),
                rot_spd: rng.next_f32()*30.0,
            };
            cubes.push(cube_state);
        };
        State {
            tick: 0.0,
            cubes: cubes,
            light_pos: v3(0., 0., 0.),
        }
    }

    fn update_one(cube: &CubeState, dt: f32) -> CubeState {
        let a = v3(0., -9.8, 0.);
        let mut new_spd = cube.spd + a*dt;
        let dp = cube.spd * dt + a * dt * dt / 2.;
        let mut new_pos = cube.pos + dp;
        let mut bounce = false;
        let (dist, sprj) = fmin(((new_pos.x - (-CUBE_SPAN/2.)).abs(), new_spd.x), fmin(((new_pos.x - CUBE_SPAN/2.).abs(), -new_spd.x),
                    fmin(((new_pos.y - (-CUBE_SPAN/2.)).abs(), new_spd.y), fmin(((new_pos.y - (CUBE_SPAN/2.)).abs(), -new_spd.y),
                    fmin(((new_pos.z - (-CUBE_SPAN)).abs(), new_spd.z), ((new_pos.z - 0.).abs(), -new_spd.z))))));
        if new_pos.x < -CUBE_SPAN/2. {
            new_pos.x = -CUBE_SPAN - new_pos.x;
            new_spd.x = -new_spd.x*0.95;
            bounce = true;
        }
        if new_pos.x > CUBE_SPAN/2. {
            new_pos.x = CUBE_SPAN - new_pos.x;
            new_spd.x = -new_spd.x*0.95;
            bounce = true;
        }
        if new_pos.y < -CUBE_SPAN/2. {
            new_pos.y = -CUBE_SPAN - new_pos.y;
            new_spd.y = -new_spd.y*0.95;
            bounce = true;
        }
        if new_pos.y > CUBE_SPAN/2. {
            new_pos.y = CUBE_SPAN - new_pos.y;
            new_spd.y = -new_spd.y*0.95;
            bounce = true;
        }
        if new_pos.z < -CUBE_SPAN {
            new_pos.z = -CUBE_SPAN*2. - new_pos.z;
            new_spd.z = -new_spd.z*0.95;
            bounce = true;
        }
        if new_pos.z > 0. {
            new_pos.z = -new_pos.z;
            new_spd.z = -new_spd.z*0.95;
            bounce = true;
        }

        if bounce && new_spd.magnitude2() < 0.8 {
            new_spd = v3(0., 0., 0.);
        }
        CubeState {
            pos: new_pos,
            spd: new_spd,
            rot: Basis3::from_axis_angle(cube.rot_axe, deg(cube.rot_spd * dt).into()).concat(&cube.rot),
            blink: dist.abs() < sprj/4. && new_spd.magnitude2()>4.,
            .. *cube
        }
    }

    pub fn update(&self, dt: f32, thread_cnt: u32) -> Self {
        if dt == 0.0 {
          return (*self).clone();
        }
        // advance position of cubes
        let mut cubes = Vec::with_capacity(self.cubes.len());
        if thread_cnt == 1 {
            for cube in &self.cubes[..] {
                cubes.push(State::update_one(cube, dt));
            }
        } else {
            // Uninitialized content of cubes will be immediately overwritten
            unsafe{ cubes.set_len(self.cubes.len()) };
            crossbeam::scope(|scope| {
                let chunk_len = (cubes.len()/thread_cnt as usize)+1;
                for (chunk, chunk_src) in cubes[..].chunks_mut(chunk_len).zip(self.cubes[..].chunks(chunk_len)) {
                    scope.spawn(move || {
                        for (c, cube) in chunk.iter_mut().zip(chunk_src) {
                            *c = State::update_one(cube, dt);
                        };
                    });
                };
            });
        }
        let mut broad_phase = nc::broad_phase::DBVTBroadPhase::new(0.1f32, true);
        for (i, c) in self.cubes.iter().enumerate() {
            let bv = bounding_volume::bounding_sphere(&nc::shape::Cuboid::new(na::Vector3::new(0.5, 0.5, 0.5)), &na::Vector3::new(c.pos.x, c.pos.y, c.pos.z));
            //println!("{:?}", bv);
            broad_phase.deferred_add(i, bv, i);
        }
        let mut ccount = 0;
        broad_phase.update(&mut |&i1, &i2| {(self.cubes[i1].pos - self.cubes[i2].pos).magnitude2() < 3.}, &mut |&i1, &i2, c|{
            if c {
                let dc = cubes[i2].pos - cubes[i1].pos;
                let dp = dc.normalize();
                let ds = cubes[i2].spd - cubes[i1].spd;
                let ci_spd = ds.dot(dp);
                if ci_spd < 0. {
                    // closing in
                    cubes[i1].spd += dp*(ci_spd/2.002);
                    cubes[i1].rot_spd /= 1.1;
                    cubes[i2].spd -= dp*(ci_spd/2.002);
                    cubes[i2].rot_spd /= 1.1;
                }
                if dc.magnitude2()<3. {
                    let penetration = 1.732 - dc.magnitude();
                    cubes[i1].pos -= dp*penetration*0.2;
                    cubes[i2].pos += dp*penetration*0.2;
                }
            }
            //ccount += 1;
        });
        //println!("Comparisons: {}", ccount);

        let (lx, ly) = f64::sin_cos(self.tick*0.05);
        let (lx, ly) = (lx as f32, ly as f32);
        let light_pos = v3(lx*50., ly*50., 2.);//v3(0., 0., -100.);
        State {
            tick: self.tick + dt as f64,
            cubes: cubes,
            light_pos: light_pos,
        }
    }
}

use std::thread;
use std::sync::Arc;
use std::sync::mpsc::{channel, Receiver, Sender};

fn state_update_agent(thread_cnt: u32, rx: Receiver<(Arc<State>, f32)>, tx: Sender<State>) {
    loop {
        if let Ok((state_ref, dt)) = rx.recv() {
            if let Err(_) = tx.send(state_ref.update(dt, thread_cnt)) {
                return ();
            }
        } else {
            return ();
        }
    }
}

pub struct StateUpdateAgent {
    tx: Sender<(Arc<State>, f32)>,
    rx: Receiver<State>,
}

impl StateUpdateAgent {
    pub fn new(thread_cnt: u32) -> Self {
        let (otx, orx) = channel();
        let (itx, irx) = channel();
        thread::spawn(move || state_update_agent(thread_cnt, orx, itx));
        StateUpdateAgent {
            tx: otx,
            rx: irx,
        }
    }

    pub fn start_update<'a>(&'a self, state: Arc<State>, dt: f32) -> UpdateResult<'a> {
        self.tx.send((state, dt)).unwrap();
        UpdateResult{
            agent: self,
            result_aquired: false,
        }
    }
}

pub struct UpdateResult<'a> {
    agent: &'a StateUpdateAgent,
    result_aquired: bool,
}

impl<'a> UpdateResult<'a> {
    pub fn get(&mut self) -> State {
        self.result_aquired = true;
        self.agent.rx.recv().unwrap()
    }
}

impl<'a> Drop for UpdateResult<'a> {
    fn drop(&mut self) {
        if !self.result_aquired {
            error!("Update result is not retrieved before drop");
        }
    }
}
<commit_msg>Somewhat more realistic bouncing<commit_after>use cgmath::*;
use crossbeam;
use rand;
use rand::Rng;
use utils::v3;
use ncollide as nc;
use ncollide::broad_phase::BroadPhase;
use ncollide::bounding_volume;
use nalgebra as na;

pub type V3 = Vector3<f32>;

#[derive(Clone)]
pub struct CubeState {
    pub pos: V3,
    pub rot: Basis3<f32>,
    pub spd: V3,
    pub color: V3,
    pub blink: bool,
    pub rot_axe: V3,
    pub rot_spd: f32,
}

#[derive(Clone)]
pub struct State {
    pub tick: f64,
    pub cubes: Vec<CubeState>,
    pub light_pos: V3,
}

const CUBE_SPAN : f32 = 100.;

fn min_f32_pair<T: Iterator<Item=(f32,P)>, P: Copy>(mut it: T) -> Option<(f32, P)> {
    match it.next() {
        None => None,
        Some((v, p)) => {
            let mut mv = v;
            let mut mp = p;
            while let Some((v, p)) = it.next() {
                if mv.is_nan() || mv>v {
                    mv = v;
                    mp = p;
                }
            };
            if mv.is_nan() {
                None
            } else {
                Some((mv, mp))
            }
        }
    }
}

#[test]
fn min_f32_pair_test() {
    assert_eq!(min_f32_pair::<_,()>([].iter().cloned()), None);
    assert_eq!(min_f32_pair([(0., ()), (1., ())].iter().cloned()), Some((0., ())));
    assert_eq!(min_f32_pair([(1., ()), (0., ())].iter().cloned()), Some((0., ())));
    assert_eq!(min_f32_pair([(1., ()), (0., ()), (1., ())].iter().cloned()), Some((0., ())));
    assert_eq!(min_f32_pair([(0., ()), (0., ()), (1., ())].iter().cloned()), Some((0., ())));
    assert_eq!(min_f32_pair([(0., ()), (0., ()), (0., ())].iter().cloned()), Some((0., ())));
    assert_eq!(min_f32_pair([(0./0., ()), (0., ()), (1., ())].iter().cloned()), Some((0., ())));
    assert_eq!(min_f32_pair([(0./0., ()), (2., ()), (1., ())].iter().cloned()), Some((1., ())));
    assert_eq!(min_f32_pair([(0./0., ())].iter().cloned()), None);
}

#[derive(Debug)]
struct CollisionParms {
    normal_bounciness: f32,
    min_speed: f32,
    friction: f32,
}

// Distance from point p to the oriented plane (pp, n), positive if point on the same side as a normal
// "Oriented plane" isn't a proper mathematical term, I don't know how it is commonly called.
#[inline]
fn plane_dist(p: V3, (pp, n): (V3, V3)) -> f32 {
    (p-pp).dot(n)
}

// returns distance from point to the nearest oriented plane.
// Plane's normal points into allowed volume, points outside allowed volume is nearer to the plane, than points inside allowed volume.
fn nearest_plane(p: V3, plns: &[(V3,V3)]) -> (f32, (V3, V3)) {
    assert!(plns.len()>0);
    if let Some(dppn) = min_f32_pair(plns.iter().cloned().map(|plane|(plane_dist(p, plane), plane))) {
        dppn // Distance, Plane Point, Normal
    } else {
        // if plns slice is non-empty, then the only reason for min_f32_pair to return None is NaN somewhere
        panic!("nearest_plane: NaNs! NaNs everywhere!");
    }
}

// roots of quadratic equation
#[derive(Debug, Clone, Copy)]
enum QERoot {
    NoRoot,
    OneRoot(f32),
    TwoRoots(f32, f32),
}
use self::QERoot::{NoRoot, OneRoot, TwoRoots};

impl PartialEq<QERoot> for QERoot {
    fn eq(&self, other: &QERoot) -> bool {
        match (*self, *other) {
            (NoRoot, NoRoot) => true,
            (OneRoot(s1), OneRoot(o1)) => s1 == o1,
            (TwoRoots(s1, s2), TwoRoots(o1, o2)) => (s1==o1 && s2==o2) || (s1==o2 && s2==o1), // order independent equality. For test, mostly
            _ => false,
        }
    }
}

// solves quadratic equation a*x*x + b*x + c = 0
// TwoRoots are in unspecified order
fn qe_roots(a: f32, b: f32, c: f32) -> QERoot {
    if a == 0. {
        // linear equation
        OneRoot(-c/b)
    } else {
        let detsq = b*b - 4.*a*c;
        if detsq < 0. {
            NoRoot // complex root, no physical sense
        } else if detsq == 0. {
            OneRoot(-b/(2.*a))
        } else {
            let det = detsq.sqrt();
            // avoid close subtraction
            if b <= 0. {
                TwoRoots((-b+det)/(2.*a), 2.*c/(-b+det))
            } else {
                TwoRoots((-b-det)/(2.*a), 2.*c/(-b-det))
            }
        }
    }
}

#[test]
fn qe_roots_test() {
    assert_eq!(qe_roots(1., 0., 0.), OneRoot(0.));
    assert_eq!(qe_roots(1., 0., 1.), NoRoot);
    assert_eq!(qe_roots(1., 0., -1.), TwoRoots(-1., 1.));
    assert_eq!(qe_roots(2., 5., -25.), TwoRoots(-5., 2.5));
    assert_eq!(qe_roots(2., -5., 0.), TwoRoots(0., 2.5));
    assert_eq!(qe_roots(0., 5., -25.), OneRoot(5.));
}

// Returns smallest of roots, which is greater than zero.
// NaNs... are a possibility. In this context (collisions)
// it makes sense to ignore them, as they should be caused only
// by sphere which touches the plane and moves parallel to it,
// so sphere isn't really colliding with plane
fn nearest_physical_time(qer: QERoot) -> Option<f32> {
    match qer {
        NoRoot => None,
        OneRoot(r) => {
            if r.is_nan() || r <= 0. { None } else { Some(r) }
        },
        TwoRoots(r1, r2) => {
            if r1.is_nan() || r1 <= 0. {
                if r2.is_nan() || r2 <= 0. {
                    // both NaNs or less then zero
                    None
                } else {
                    // r1 isn't acceptable, r2 is
                    Some(r2)
                }
            } else if r2.is_nan() || r2 <= 0. {
                // r1 is acceptable, r2 isn't
                Some(r1)
            } else {
                // both are acceptable
                Some(f32::min(r1, r2))
            }
        }
    }
}

static TOUCH_TOLERANCE: f32 = 0.0001;
static EPS: f32 = 0.000001;

// p - initial position
// v - initial speed
// a - acceleration
// r - sphere radius
// (pp, n) - point on plane, normal pointing into allowed volume
fn time_to_hit(p: V3, v: V3, a: V3, r: f32, (pp, n): (V3, V3)) -> Option<f32> {
    // we care only about motion along normal, so let't project everything onto it.
    let nd = (p-pp).dot(n); // distance from center of sphere to the plane
    let nv = v.dot(n); // speed along nornal (positive - into allowed space)
    if (nd-r).abs() < TOUCH_TOLERANCE && nv < EPS {
        // sphere is moving alongside plane. skip it
        return None;
    };
    if nd-r < 0. {
        // sphere is in restricted space, skip this plane
        return None;
    }
    let na = a.dot(n); // acceleration (positive - into allowed space)
    // motion equation x = na*t*t/2 + nv*t + nd
    // solve na*t*t/2 + nv*t + (nd-r) = 0
    // and return physical root
    nearest_physical_time(qe_roots(na/2., nv, nd-r))
}

// dt - time step
// p - initial position
// v - initial speed
// a - acceleration
// r - radius
// plns - planes (point, interior normal)
// returns time to collision or dt, position at collision point, speed after collision
fn sphere_planes_collision(cp: &CollisionParms, dt: f32, p: V3, v: V3, a: V3, r: f32, plns: &[(V3,V3)]) -> (f32, V3, V3, Option<(V3,V3)>) {
    // recovery step if sphere is penetrating some plane(s)
    // TODO: do something more intelligent.
    let (d, (pp, n)) = nearest_plane(p, plns);
    if d+TOUCH_TOLERANCE < r {
        // distance to the nearest plane is less than sphere radius
        let pcorr = p + (r - d)*n; // move sphere into allowed volume (possibly), and be done with it for this step
        //println!("correction");
        return (dt, pcorr, v, Some((pp, n)));
    };

    // Enforce constraints. Acceleration should not force sphere into disallowed volume
    let mut a = a;
    let mut v = v;
    for plane in plns.iter().cloned() {
        let (pp, n) = plane;
        if (plane_dist(p, plane)-r).abs() < TOUCH_TOLERANCE {
            let na = n.dot(a); // normal acceleration
            let nv = n.dot(v); // normal speed
            if na < 0. && nv < cp.min_speed {
                // sphere moves toward or parrallel to disallowed volume and
                // acceleration forces sphere into disallowed volume,
                // correct it (apply reaction force)
                //println!("av correction before: a: {:?} v: {:?} na: {:?} nv:{:?} {:?}", a, v, na, nv, plane);
                a -= na*n;
                // correct speed to make the sphere move along the plane
                v -= nv*n;
                // add friction
                if v.magnitude() != 0. {
                    a -= v.normalize()*cp.friction;
                }
                //println!("av correction after: a: {:?} v: {:?}", a, v);
            }
        }
    }

    // Spere is inside allowed volume, let's check if it will hit anything in this time step
    // Checking final point is not enough. Top of the parabola could be outside allowed volume.
    // So we need to compute time-to-hit for all planes, select first one, then if it is greater than dt, we have no collisions
    if let Some((t,(pp,n))) = min_f32_pair(plns.iter().cloned().filter_map(|(pp, n)|time_to_hit(p, v, a, r, (pp, n)).map(|t|(t,(pp, n))))) {
        if t > dt {
            // collision after time step
            (dt, p+v*dt+a*dt*dt/2., v+a*dt, None)
        } else {
            let v1 = v+a*t; // speed at impact
            let vb = (v1-v1.dot(n)*2.*n)*cp.normal_bounciness;  // speed after bounce
            (t, p+v*t+a*t*t/2., vb, Some((pp, n)))
        }
    } else {
        // No collisions. Allowed space is unbounded
        (dt, p+v*dt+a*dt*dt/2., v+a*dt, None)
    }
}

static BOUNCE_PLANES: [(V3,V3);6] = [
    (Vector3{x:0., y:0., z:0.}, Vector3{x:0., y:0., z:-1.}), // z = 0 plane
    (Vector3{x:0., y:0., z:-CUBE_SPAN}, Vector3{x:0., y:0., z:1.}), // z = -CUBE_SPAN
    (Vector3{x:CUBE_SPAN/2.,  y:0., z:0.}, Vector3{x:-1., y:0., z:0.}), // and so on
    (Vector3{x:-CUBE_SPAN/2., y:0., z:0.}, Vector3{x:1., y:0., z:0.}),
    (Vector3{x:0., y:CUBE_SPAN/2., z:0.}, Vector3{x:0., y:-1., z:0.}),
    (Vector3{x:0., y:-CUBE_SPAN/2., z:0.}, Vector3{x:0.1, y:0.9949874371, z:0.}),
];

impl State {
    pub fn new(object_count: u32) -> Self {
        let mut rng = rand::XorShiftRng::new_unseeded();
        let mut cubes = Vec::with_capacity(object_count as usize);
        for _ in 0 .. object_count {
            let x = rng.next_f32();
            let y = rng.next_f32();
            let z = rng.next_f32();
            let cube_state = CubeState {
                pos: v3((x - 0.5) * CUBE_SPAN,
                             (y - 0.5) * CUBE_SPAN,
                             (z - 1.) * CUBE_SPAN),
                color: v3(x, y, z),
                blink: false,
                rot: <Basis3<f32> as Rotation<_>>::one(),
                spd: v3(rng.next_f32() - 0.5,
                             rng.next_f32() - 0.5,
                             rng.next_f32() - 0.5).normalize()*rng.next_f32()*1.,
                rot_axe: v3(rng.next_f32() - 0.5,
                             rng.next_f32() - 0.5,
                             rng.next_f32() - 0.5)
                              .normalize(),
                rot_spd: rng.next_f32()*30.0,
            };
            cubes.push(cube_state);
        };
        State {
            tick: 0.0,
            cubes: cubes,
            light_pos: v3(0., 0., 0.),
        }
    }

    fn update_one(cube: &CubeState, dt: f32) -> CubeState {
        let r = 0.866;
        let a = v3(0., -9.8, 0.);
        let cp = CollisionParms {
            normal_bounciness: 0.9,
            min_speed: 0.1,
            friction: 0.1,
        };
        // bounce_planes is vec of tuples (point on plane, plane normal pointing inside allowed volume)
        let mut pos = cube.pos;
        let mut spd = cube.spd;
        let mut rt = dt;
        for i in 0..5 { // prevent too many bounces in one time step
            let (t, p, v, maybe_plane) = sphere_planes_collision(&cp, rt, pos, spd, a, r, &BOUNCE_PLANES);
            //println!("Step: {:?}, dt: {:?}, p: {:?}, v: {:?}, {:?}", i, t, p, v, maybe_plane);
            rt -= t;
            pos = p;
            spd = v;
            if rt <= 0. || maybe_plane.is_none() {
                break;
            }
        }

        CubeState {
            pos: pos,
            spd: spd,
            rot: Basis3::from_axis_angle(cube.rot_axe, deg(cube.rot_spd * dt).into()).concat(&cube.rot),
            blink: false,
            .. *cube
        }
    }

    pub fn update(&self, dt: f32, thread_cnt: u32) -> Self {
        if dt == 0.0 {
          return (*self).clone();
        }
        // advance position of cubes
        let mut cubes = Vec::with_capacity(self.cubes.len());
        if thread_cnt == 1 {
            for cube in &self.cubes[..] {
                cubes.push(State::update_one(cube, dt));
            }
        } else {
            // Uninitialized content of cubes will be immediately overwritten
            unsafe{ cubes.set_len(self.cubes.len()) };
            crossbeam::scope(|scope| {
                let chunk_len = (cubes.len()/thread_cnt as usize)+1;
                for (chunk, chunk_src) in cubes[..].chunks_mut(chunk_len).zip(self.cubes[..].chunks(chunk_len)) {
                    scope.spawn(move || {
                        for (c, cube) in chunk.iter_mut().zip(chunk_src) {
                            *c = State::update_one(cube, dt);
                        };
                    });
                };
            });
        }
        let mut broad_phase = nc::broad_phase::DBVTBroadPhase::new(0.1f32, true);
        for (i, c) in self.cubes.iter().enumerate() {
            let bv = bounding_volume::bounding_sphere(&nc::shape::Cuboid::new(na::Vector3::new(0.5, 0.5, 0.5)), &na::Vector3::new(c.pos.x, c.pos.y, c.pos.z));
            //println!("{:?}", bv);
            broad_phase.deferred_add(i, bv, i);
        }
        let mut ccount = 0;
        broad_phase.update(&mut |&i1, &i2| {(self.cubes[i1].pos - self.cubes[i2].pos).magnitude2() < 3.}, &mut |&i1, &i2, c|{
            if c {
                let dc = cubes[i2].pos - cubes[i1].pos;
                let dp = dc.normalize();
                let ds = cubes[i2].spd - cubes[i1].spd;
                let ci_spd = ds.dot(dp);
                if ci_spd < 0. {
                    // closing in
                    cubes[i1].spd += dp*(ci_spd/2.002);
                    cubes[i1].rot_spd /= 1.1;
                    cubes[i2].spd -= dp*(ci_spd/2.002);
                    cubes[i2].rot_spd /= 1.1;
                }
                if dc.magnitude2()<3. {
                    let penetration = 1.732 - dc.magnitude();
                    cubes[i1].pos -= dp*penetration*0.5;
                    cubes[i2].pos += dp*penetration*0.5;
                }
            }
            //ccount += 1;
        });
        //println!("Comparisons: {}", ccount);

        let (lx, ly) = f64::sin_cos(self.tick*0.05);
        let (lx, ly) = (lx as f32, ly as f32);
        let light_pos = v3(lx*50., ly*50., 2.);//v3(0., 0., -100.);
        State {
            tick: self.tick + dt as f64,
            cubes: cubes,
            light_pos: light_pos,
        }
    }
}

use std::thread;
use std::sync::Arc;
use std::sync::mpsc::{channel, Receiver, Sender};

fn state_update_agent(thread_cnt: u32, rx: Receiver<(Arc<State>, f32)>, tx: Sender<State>) {
    loop {
        if let Ok((state_ref, dt)) = rx.recv() {
            if let Err(_) = tx.send(state_ref.update(dt, thread_cnt)) {
                return ();
            }
        } else {
            return ();
        }
    }
}

pub struct StateUpdateAgent {
    tx: Sender<(Arc<State>, f32)>,
    rx: Receiver<State>,
}

impl StateUpdateAgent {
    pub fn new(thread_cnt: u32) -> Self {
        let (otx, orx) = channel();
        let (itx, irx) = channel();
        thread::spawn(move || state_update_agent(thread_cnt, orx, itx));
        StateUpdateAgent {
            tx: otx,
            rx: irx,
        }
    }

    pub fn start_update<'a>(&'a self, state: Arc<State>, dt: f32) -> UpdateResult<'a> {
        self.tx.send((state, dt)).unwrap();
        UpdateResult{
            agent: self,
            result_aquired: false,
        }
    }
}

pub struct UpdateResult<'a> {
    agent: &'a StateUpdateAgent,
    result_aquired: bool,
}

impl<'a> UpdateResult<'a> {
    pub fn get(&mut self) -> State {
        self.result_aquired = true;
        self.agent.rx.recv().unwrap()
    }
}

impl<'a> Drop for UpdateResult<'a> {
    fn drop(&mut self) {
        if !self.result_aquired {
            error!("Update result is not retrieved before drop");
        }
    }
}
<|endoftext|>


<commit_before><commit_msg>Delay cloning in export_helper and make it non-mutable<commit_after><|endoftext|>


<commit_before>use crate::{
    analysis::{self, conversion_type::ConversionType, rust_type::rust_type},
    chunk::conversion_from_glib::Mode,
    env::Env,
    library,
    nameutil::use_glib_type,
    traits::*,
};

pub trait TranslateFromGlib {
    fn translate_from_glib_as_function(
        &self,
        env: &Env,
        array_length: Option<&String>,
    ) -> (String, String);
}

impl TranslateFromGlib for Mode {
    fn translate_from_glib_as_function(
        &self,
        env: &Env,
        array_length: Option<&String>,
    ) -> (String, String) {
        use crate::analysis::conversion_type::ConversionType::*;
        match ConversionType::of(env, self.typ) {
            Direct => (String::new(), String::new()),
            Scalar => match env.library.type_(self.typ) {
                library::Type::Fundamental(library::Fundamental::UniChar) => (
                    "std::convert::TryFrom::try_from(".into(),
                    ").expect(\"conversion from an invalid Unicode value attempted\")".into(),
                ),
                _ => ("from_glib(".into(), ")".into()),
            },
            Pointer => {
                let trans = from_glib_xxx(self.transfer, array_length);
                match *env.type_(self.typ) {
                    library::Type::List(..)
                    | library::Type::SList(..)
                    | library::Type::PtrArray(..)
                    | library::Type::CArray(..) => {
                        if array_length.is_some() {
                            (format!("FromGlibContainer::{}", trans.0), trans.1)
                        } else {
                            (format!("FromGlibPtrContainer::{}", trans.0), trans.1)
                        }
                    }
                    _ => trans,
                }
            }
            Borrow => ("/*TODO: conversion Borrow*/".into(), String::new()),
            Unknown => ("/*Unknown conversion*/".into(), String::new()),
        }
    }
}

impl TranslateFromGlib for analysis::return_value::Info {
    fn translate_from_glib_as_function(
        &self,
        env: &Env,
        array_length: Option<&String>,
    ) -> (String, String) {
        match self.parameter {
            Some(ref par) => match self.base_tid {
                Some(tid) => {
                    let rust_type = rust_type(env, tid);
                    let from_glib_xxx = from_glib_xxx(par.transfer, None);

                    let prefix = if *par.nullable {
                        format!("Option::<{}>::{}", rust_type.into_string(), from_glib_xxx.0)
                    } else {
                        format!("{}::{}", rust_type.into_string(), from_glib_xxx.0)
                    };
                    let suffix_function = if *par.nullable {
                        "map(|o| o.unsafe_cast())"
                    } else {
                        "unsafe_cast()"
                    };

                    if let Some(ref msg) = self.nullable_return_is_error {
                        assert!(*par.nullable);
                        (
                            prefix,
                            format!(
                                "{}.{}.ok_or_else(|| glib_bool_error!(\"{}\"))",
                                from_glib_xxx.1, suffix_function, msg
                            ),
                        )
                    } else {
                        (prefix, format!("{}.{}", from_glib_xxx.1, suffix_function))
                    }
                }
                None if self.bool_return_is_error.is_some() => (
                    use_glib_type(env, "glib_result_from_gboolean!("),
                    format!(", \"{}\")", self.bool_return_is_error.as_ref().unwrap()),
                ),
                None if self.nullable_return_is_error.is_some() => {
                    let res = Mode::from(par).translate_from_glib_as_function(env, array_length);
                    if let Some(ref msg) = self.nullable_return_is_error {
                        assert!(*par.nullable);
                        (
                            format!("Option::<_>::{}", res.0),
                            format!("{}.ok_or_else(|| glib_bool_error!(\"{}\"))", res.1, msg),
                        )
                    } else {
                        res
                    }
                }
                None => Mode::from(par).translate_from_glib_as_function(env, array_length),
            },
            None => (String::new(), ";".into()),
        }
    }
}

fn from_glib_xxx(transfer: library::Transfer, array_length: Option<&String>) -> (String, String) {
    use crate::library::Transfer;
    let good_print = |name: &str| format!(", {}.assume_init() as usize)", name);
    match (transfer, array_length) {
        (Transfer::None, None) => ("from_glib_none(".into(), ")".into()),
        (Transfer::Full, None) => ("from_glib_full(".into(), ")".into()),
        (Transfer::Container, None) => ("from_glib_container(".into(), ")".into()),
        (Transfer::None, Some(array_length_name)) => {
            ("from_glib_none_num(".into(), good_print(array_length_name))
        }
        (Transfer::Full, Some(array_length_name)) => {
            ("from_glib_full_num(".into(), good_print(array_length_name))
        }
        (Transfer::Container, Some(array_length_name)) => (
            "from_glib_container_num(".into(),
            good_print(array_length_name),
        ),
    }
}
<commit_msg>Add missing glib prefix for macros<commit_after>use crate::{
    analysis::{self, conversion_type::ConversionType, rust_type::rust_type},
    chunk::conversion_from_glib::Mode,
    env::Env,
    library,
    nameutil::use_glib_type,
    traits::*,
};

pub trait TranslateFromGlib {
    fn translate_from_glib_as_function(
        &self,
        env: &Env,
        array_length: Option<&String>,
    ) -> (String, String);
}

impl TranslateFromGlib for Mode {
    fn translate_from_glib_as_function(
        &self,
        env: &Env,
        array_length: Option<&String>,
    ) -> (String, String) {
        use crate::analysis::conversion_type::ConversionType::*;
        match ConversionType::of(env, self.typ) {
            Direct => (String::new(), String::new()),
            Scalar => match env.library.type_(self.typ) {
                library::Type::Fundamental(library::Fundamental::UniChar) => (
                    "std::convert::TryFrom::try_from(".into(),
                    ").expect(\"conversion from an invalid Unicode value attempted\")".into(),
                ),
                _ => ("from_glib(".into(), ")".into()),
            },
            Pointer => {
                let trans = from_glib_xxx(self.transfer, array_length);
                match *env.type_(self.typ) {
                    library::Type::List(..)
                    | library::Type::SList(..)
                    | library::Type::PtrArray(..)
                    | library::Type::CArray(..) => {
                        if array_length.is_some() {
                            (format!("FromGlibContainer::{}", trans.0), trans.1)
                        } else {
                            (format!("FromGlibPtrContainer::{}", trans.0), trans.1)
                        }
                    }
                    _ => trans,
                }
            }
            Borrow => ("/*TODO: conversion Borrow*/".into(), String::new()),
            Unknown => ("/*Unknown conversion*/".into(), String::new()),
        }
    }
}

impl TranslateFromGlib for analysis::return_value::Info {
    fn translate_from_glib_as_function(
        &self,
        env: &Env,
        array_length: Option<&String>,
    ) -> (String, String) {
        match self.parameter {
            Some(ref par) => match self.base_tid {
                Some(tid) => {
                    let rust_type = rust_type(env, tid);
                    let from_glib_xxx = from_glib_xxx(par.transfer, None);

                    let prefix = if *par.nullable {
                        format!("Option::<{}>::{}", rust_type.into_string(), from_glib_xxx.0)
                    } else {
                        format!("{}::{}", rust_type.into_string(), from_glib_xxx.0)
                    };
                    let suffix_function = if *par.nullable {
                        "map(|o| o.unsafe_cast())"
                    } else {
                        "unsafe_cast()"
                    };

                    if let Some(ref msg) = self.nullable_return_is_error {
                        assert!(*par.nullable);
                        (
                            prefix,
                            format!(
                                "{}.{}.ok_or_else(|| {}(\"{}\"))",
                                from_glib_xxx.1,
                                suffix_function,
                                use_glib_type(env, "glib_bool_error!"),
                                msg
                            ),
                        )
                    } else {
                        (prefix, format!("{}.{}", from_glib_xxx.1, suffix_function))
                    }
                }
                None if self.bool_return_is_error.is_some() => (
                    use_glib_type(env, "glib_result_from_gboolean!("),
                    format!(", \"{}\")", self.bool_return_is_error.as_ref().unwrap()),
                ),
                None if self.nullable_return_is_error.is_some() => {
                    let res = Mode::from(par).translate_from_glib_as_function(env, array_length);
                    if let Some(ref msg) = self.nullable_return_is_error {
                        assert!(*par.nullable);
                        (
                            format!("Option::<_>::{}", res.0),
                            format!(
                                "{}.ok_or_else(|| {}(\"{}\"))",
                                res.1,
                                use_glib_type(env, "glib_bool_error!"),
                                msg
                            ),
                        )
                    } else {
                        res
                    }
                }
                None => Mode::from(par).translate_from_glib_as_function(env, array_length),
            },
            None => (String::new(), ";".into()),
        }
    }
}

fn from_glib_xxx(transfer: library::Transfer, array_length: Option<&String>) -> (String, String) {
    use crate::library::Transfer;
    let good_print = |name: &str| format!(", {}.assume_init() as usize)", name);
    match (transfer, array_length) {
        (Transfer::None, None) => ("from_glib_none(".into(), ")".into()),
        (Transfer::Full, None) => ("from_glib_full(".into(), ")".into()),
        (Transfer::Container, None) => ("from_glib_container(".into(), ")".into()),
        (Transfer::None, Some(array_length_name)) => {
            ("from_glib_none_num(".into(), good_print(array_length_name))
        }
        (Transfer::Full, Some(array_length_name)) => {
            ("from_glib_full_num(".into(), good_print(array_length_name))
        }
        (Transfer::Container, Some(array_length_name)) => (
            "from_glib_container_num(".into(),
            good_print(array_length_name),
        ),
    }
}
<|endoftext|>


<commit_before>#![feature(rustc_macro)]

#[macro_use]
extern crate diesel;
#[macro_use]
extern crate diesel_codegen;

#[derive(Identifiable)]
//~^ ERROR custom derive attribute panicked
//~| HELP Could not find a field named `id` on `User`
pub struct User {
    name: String,
    hair_color: Option<String>,
}

fn main() {}
<commit_msg>Whoops, `rustc_macro` slipped through<commit_after>#![feature(proc_macro)]

#[macro_use]
extern crate diesel;
#[macro_use]
extern crate diesel_codegen;

#[derive(Identifiable)]
//~^ ERROR custom derive attribute panicked
//~| HELP Could not find a field named `id` on `User`
pub struct User {
    name: String,
    hair_color: Option<String>,
}

fn main() {}
<|endoftext|>


<commit_before>mod token;

use chrono::{offset::Utc, DateTime};
use diesel::{self, pg::PgConnection};

use self::token::{create_token, HashedEmailToken};
pub use self::token::{CreationError, EmailToken, EmailVerificationToken, VerificationError};
use schema::emails;
use user::{AuthenticatedUser, UnverifiedUser, UserLike};

pub struct VerifiedEmail {
    id: i32,
    email: String,
    user_id: i32,
    created_at: DateTime<Utc>,
    updated_at: DateTime<Utc>,
}

impl VerifiedEmail {
    pub fn id(&self) -> i32 {
        self.id
    }

    pub fn email(&self) -> &str {
        &self.email
    }

    pub fn user_id(&self) -> i32 {
        self.user_id
    }

    pub fn created_at(&self) -> DateTime<Utc> {
        self.created_at
    }

    pub fn updated_at(&self) -> DateTime<Utc> {
        self.updated_at
    }
}

#[derive(Debug, Queryable, QueryableByName)]
#[table_name = "emails"]
pub struct Email {
    id: i32,
    email: String,
    user_id: i32,
    verified: bool,
    verification_token: Option<HashedEmailToken>,
    confirmed_at: Option<DateTime<Utc>>,
    created_at: DateTime<Utc>,
    updated_at: DateTime<Utc>,
}

impl Email {
    pub fn first_by_user_id(
        user_id: i32,
        conn: &PgConnection,
    ) -> Result<Self, diesel::result::Error> {
        use diesel::prelude::*;

        emails::table
            .filter(emails::dsl::user_id.eq(user_id))
            .get_result(conn)
    }

    pub fn by_id(id: i32, conn: &PgConnection) -> Result<Self, diesel::result::Error> {
        use diesel::prelude::*;

        emails::table
            .filter(emails::dsl::id.eq(id))
            .get_result(conn)
    }

    pub fn to_verified(self) -> Result<VerifiedEmail, UnverifiedEmail> {
        if self.verified {
            Ok(VerifiedEmail {
                id: self.id,
                email: self.email,
                user_id: self.user_id,
                created_at: self.created_at,
                updated_at: self.updated_at,
            })
        } else {
            Err(UnverifiedEmail {
                id: self.id,
                email: self.email,
                user_id: self.user_id,
                verified: false,
                verification_token: self.verification_token,
                confirmed_at: None,
                created_at: self.created_at,
                updated_at: self.updated_at,
            })
        }
    }
}

pub struct VerifyEmail {
    id: i32,
    email: String,
    user_id: i32,
    verified: bool,
    verification_token: Option<HashedEmailToken>,
    confirmed_at: DateTime<Utc>,
    created_at: DateTime<Utc>,
    updated_at: DateTime<Utc>,
}

#[derive(AsChangeset)]
#[table_name = "emails"]
pub struct EmailVerificationChangeset {
    id: i32,
    verified: bool,
    verification_token: Option<HashedEmailToken>,
    confirmed_at: DateTime<Utc>,
}

impl VerifyEmail {
    pub(crate) fn store_verify(
        self,
        conn: &PgConnection,
    ) -> Result<VerifiedEmail, diesel::result::Error> {
        use diesel::prelude::*;
        use schema::emails;

        diesel::update(emails::table)
            .set(&EmailVerificationChangeset {
                id: self.id,
                verified: true,
                verification_token: None,
                confirmed_at: self.confirmed_at,
            })
            .execute(conn)
            .map(|_| VerifiedEmail {
                id: self.id,
                email: self.email,
                user_id: self.user_id,
                created_at: self.created_at,
                updated_at: self.updated_at,
            })
            .map_err(|e| {
                error!("Failed to verify email: {}, {:?}", e, e);
                e
            })
    }
}

#[derive(Queryable, QueryableByName)]
#[table_name = "emails"]
pub struct UnverifiedEmail {
    id: i32,
    email: String,
    user_id: i32, // foreign key to User
    verified: bool,
    verification_token: Option<HashedEmailToken>,
    #[allow(dead_code)]
    confirmed_at: Option<DateTime<Utc>>,
    created_at: DateTime<Utc>,
    updated_at: DateTime<Utc>,
}

impl UnverifiedEmail {
    pub fn id(&self) -> i32 {
        self.id
    }

    pub fn email(&self) -> &str {
        &self.email
    }

    pub(crate) fn verify_and_log_in(
        self,
        user: UnverifiedUser,
        token: EmailVerificationToken,
    ) -> Result<(AuthenticatedUser, VerifyEmail), VerificationError> {
        self.verify(token).map(|verify_email| {
            (
                AuthenticatedUser {
                    id: user.id,
                    primary_email: None,
                    created_at: user.created_at,
                    updated_at: user.updated_at,
                },
                verify_email,
            )
        })
    }

    pub fn verify(self, token: EmailVerificationToken) -> Result<VerifyEmail, VerificationError> {
        if self.verification_token.is_some() && !self.verified {
            token::VerifyEmail::verify_email(self.verification_token.as_ref().unwrap(), token).map(
                |_| VerifyEmail {
                    id: self.id,
                    email: self.email,
                    user_id: self.user_id,
                    verified: true,
                    verification_token: None,
                    confirmed_at: Utc::now(),
                    created_at: self.created_at,
                    updated_at: self.updated_at,
                },
            )
        } else if !self.verified {
            Err(VerificationError::Process)
        } else {
            // TODO: don't error if email is already verified
            Err(VerificationError::Process)
        }
    }
}

#[derive(Insertable)]
#[table_name = "emails"]
pub struct NewEmail {
    email: String,
    user_id: i32,
    verified: bool,
    verification_token: HashedEmailToken,
}

impl NewEmail {
    pub fn insert(self, conn: &PgConnection) -> Result<UnverifiedEmail, diesel::result::Error> {
        use diesel::prelude::*;

        diesel::insert_into(emails::table)
            .values(&self)
            .get_result(conn)
    }

    pub fn new<U: UserLike>(email: String, user: &U) -> Result<(Self, EmailToken), CreationError> {
        create_token().map(|(email_token, verification_token)| {
            (
                NewEmail {
                    email,
                    user_id: user.id(),
                    verified: false,
                    verification_token,
                },
                email_token,
            )
        })
    }
}

#[cfg(test)]
mod tests {
    use test_helper::*;

    #[test]
    fn create_email() {
        with_connection(|conn| {
            with_unverified_user(conn, |user| {
                with_unverified_email(conn, &user, |_email, _token| Ok(()))
            })
        })
    }

    #[test]
    fn verify_email() {
        with_connection(|conn| {
            with_unverified_user(conn, |user| {
                with_unverified_email(conn, &user, |email, token| {
                    email.verify(token)?.store_verify(conn)?;

                    Ok(())
                })
            })
        })
    }
}
<commit_msg>Get rid of compiler warnings<commit_after>mod token;

use chrono::{offset::Utc, DateTime};
use diesel::{self, pg::PgConnection};

use self::token::{create_token, HashedEmailToken};
pub use self::token::{CreationError, EmailToken, EmailVerificationToken, VerificationError};
use schema::emails;
use user::{AuthenticatedUser, UnverifiedUser, UserLike};

pub struct VerifiedEmail {
    id: i32,
    email: String,
    user_id: i32,
    created_at: DateTime<Utc>,
    updated_at: DateTime<Utc>,
}

impl VerifiedEmail {
    pub fn id(&self) -> i32 {
        self.id
    }

    pub fn email(&self) -> &str {
        &self.email
    }

    pub fn user_id(&self) -> i32 {
        self.user_id
    }

    pub fn created_at(&self) -> DateTime<Utc> {
        self.created_at
    }

    pub fn updated_at(&self) -> DateTime<Utc> {
        self.updated_at
    }
}

#[derive(Debug, Queryable, QueryableByName)]
#[table_name = "emails"]
pub struct Email {
    id: i32,
    email: String,
    user_id: i32,
    verified: bool,
    verification_token: Option<HashedEmailToken>,
    confirmed_at: Option<DateTime<Utc>>,
    created_at: DateTime<Utc>,
    updated_at: DateTime<Utc>,
}

impl Email {
    pub fn first_by_user_id(
        user_id: i32,
        conn: &PgConnection,
    ) -> Result<Self, diesel::result::Error> {
        use diesel::prelude::*;

        emails::table
            .filter(emails::dsl::user_id.eq(user_id))
            .get_result(conn)
    }

    pub fn by_id(id: i32, conn: &PgConnection) -> Result<Self, diesel::result::Error> {
        use diesel::prelude::*;

        emails::table
            .filter(emails::dsl::id.eq(id))
            .get_result(conn)
    }

    pub fn to_verified(self) -> Result<VerifiedEmail, UnverifiedEmail> {
        if self.verified {
            Ok(VerifiedEmail {
                id: self.id,
                email: self.email,
                user_id: self.user_id,
                created_at: self.created_at,
                updated_at: self.updated_at,
            })
        } else {
            Err(UnverifiedEmail {
                id: self.id,
                email: self.email,
                user_id: self.user_id,
                verified: false,
                verification_token: self.verification_token,
                confirmed_at: None,
                created_at: self.created_at,
                updated_at: self.updated_at,
            })
        }
    }
}

pub struct VerifyEmail {
    id: i32,
    email: String,
    user_id: i32,
    #[allow(dead_code)]
    verified: bool,
    #[allow(dead_code)]
    verification_token: Option<HashedEmailToken>,
    confirmed_at: DateTime<Utc>,
    created_at: DateTime<Utc>,
    updated_at: DateTime<Utc>,
}

#[derive(AsChangeset)]
#[table_name = "emails"]
pub struct EmailVerificationChangeset {
    #[allow(dead_code)]
    id: i32,
    verified: bool,
    verification_token: Option<HashedEmailToken>,
    confirmed_at: DateTime<Utc>,
}

impl VerifyEmail {
    pub(crate) fn store_verify(
        self,
        conn: &PgConnection,
    ) -> Result<VerifiedEmail, diesel::result::Error> {
        use diesel::prelude::*;
        use schema::emails;

        diesel::update(emails::table)
            .set(&EmailVerificationChangeset {
                id: self.id,
                verified: true,
                verification_token: None,
                confirmed_at: self.confirmed_at,
            })
            .execute(conn)
            .map(|_| VerifiedEmail {
                id: self.id,
                email: self.email,
                user_id: self.user_id,
                created_at: self.created_at,
                updated_at: self.updated_at,
            })
            .map_err(|e| {
                error!("Failed to verify email: {}, {:?}", e, e);
                e
            })
    }
}

#[derive(Queryable, QueryableByName)]
#[table_name = "emails"]
pub struct UnverifiedEmail {
    id: i32,
    email: String,
    user_id: i32, // foreign key to User
    verified: bool,
    verification_token: Option<HashedEmailToken>,
    #[allow(dead_code)]
    confirmed_at: Option<DateTime<Utc>>,
    created_at: DateTime<Utc>,
    updated_at: DateTime<Utc>,
}

impl UnverifiedEmail {
    pub fn id(&self) -> i32 {
        self.id
    }

    pub fn email(&self) -> &str {
        &self.email
    }

    pub(crate) fn verify_and_log_in(
        self,
        user: UnverifiedUser,
        token: EmailVerificationToken,
    ) -> Result<(AuthenticatedUser, VerifyEmail), VerificationError> {
        self.verify(token).map(|verify_email| {
            (
                AuthenticatedUser {
                    id: user.id,
                    primary_email: None,
                    created_at: user.created_at,
                    updated_at: user.updated_at,
                },
                verify_email,
            )
        })
    }

    pub fn verify(self, token: EmailVerificationToken) -> Result<VerifyEmail, VerificationError> {
        if self.verification_token.is_some() && !self.verified {
            token::VerifyEmail::verify_email(self.verification_token.as_ref().unwrap(), token).map(
                |_| VerifyEmail {
                    id: self.id,
                    email: self.email,
                    user_id: self.user_id,
                    verified: true,
                    verification_token: None,
                    confirmed_at: Utc::now(),
                    created_at: self.created_at,
                    updated_at: self.updated_at,
                },
            )
        } else if !self.verified {
            Err(VerificationError::Process)
        } else {
            // TODO: don't error if email is already verified
            Err(VerificationError::Process)
        }
    }
}

#[derive(Insertable)]
#[table_name = "emails"]
pub struct NewEmail {
    email: String,
    user_id: i32,
    verified: bool,
    verification_token: HashedEmailToken,
}

impl NewEmail {
    pub fn insert(self, conn: &PgConnection) -> Result<UnverifiedEmail, diesel::result::Error> {
        use diesel::prelude::*;

        diesel::insert_into(emails::table)
            .values(&self)
            .get_result(conn)
    }

    pub fn new<U: UserLike>(email: String, user: &U) -> Result<(Self, EmailToken), CreationError> {
        create_token().map(|(email_token, verification_token)| {
            (
                NewEmail {
                    email,
                    user_id: user.id(),
                    verified: false,
                    verification_token,
                },
                email_token,
            )
        })
    }
}

#[cfg(test)]
mod tests {
    use test_helper::*;

    #[test]
    fn create_email() {
        with_connection(|conn| {
            with_unverified_user(conn, |user| {
                with_unverified_email(conn, &user, |_email, _token| Ok(()))
            })
        })
    }

    #[test]
    fn verify_email() {
        with_connection(|conn| {
            with_unverified_user(conn, |user| {
                with_unverified_email(conn, &user, |email, token| {
                    email.verify(token)?.store_verify(conn)?;

                    Ok(())
                })
            })
        })
    }
}
<|endoftext|>


<commit_before>use rustc::mir;
use rustc::ty::Ty;

use super::{EvalResult, EvalContext, Lvalue, Machine, ValTy};

use super::value::{PrimVal, PrimValKind, Value, bytes_to_f32, bytes_to_f64, f32_to_bytes,
                   f64_to_bytes};

impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> {
    fn binop_with_overflow(
        &mut self,
        op: mir::BinOp,
        left: ValTy<'tcx>,
        right: ValTy<'tcx>,
    ) -> EvalResult<'tcx, (PrimVal, bool)> {
        let left_val = self.value_to_primval(left)?;
        let right_val = self.value_to_primval(right)?;
        self.binary_op(op, left_val, left.ty, right_val, right.ty)
    }

    /// Applies the binary operation `op` to the two operands and writes a tuple of the result
    /// and a boolean signifying the potential overflow to the destination.
    pub fn intrinsic_with_overflow(
        &mut self,
        op: mir::BinOp,
        left: ValTy<'tcx>,
        right: ValTy<'tcx>,
        dest: Lvalue,
        dest_ty: Ty<'tcx>,
    ) -> EvalResult<'tcx> {
        let (val, overflowed) = self.binop_with_overflow(op, left, right)?;
        let val = Value::ByValPair(val, PrimVal::from_bool(overflowed));
        let valty = ValTy {
            value: val,
            ty: dest_ty,
        };
        self.write_value(valty, dest)
    }

    /// Applies the binary operation `op` to the arguments and writes the result to the
    /// destination. Returns `true` if the operation overflowed.
    pub fn intrinsic_overflowing(
        &mut self,
        op: mir::BinOp,
        left: ValTy<'tcx>,
        right: ValTy<'tcx>,
        dest: Lvalue,
        dest_ty: Ty<'tcx>,
    ) -> EvalResult<'tcx, bool> {
        let (val, overflowed) = self.binop_with_overflow(op, left, right)?;
        self.write_primval(dest, val, dest_ty)?;
        Ok(overflowed)
    }
}

macro_rules! overflow {
    ($op:ident, $l:expr, $r:expr) => ({
        let (val, overflowed) = $l.$op($r);
        let primval = PrimVal::Bytes(val as u128);
        Ok((primval, overflowed))
    })
}

macro_rules! int_arithmetic {
    ($kind:expr, $int_op:ident, $l:expr, $r:expr) => ({
        let l = $l;
        let r = $r;
        use super::PrimValKind::*;
        match $kind {
            I8  => overflow!($int_op, l as i8,  r as i8),
            I16 => overflow!($int_op, l as i16, r as i16),
            I32 => overflow!($int_op, l as i32, r as i32),
            I64 => overflow!($int_op, l as i64, r as i64),
            I128 => overflow!($int_op, l as i128, r as i128),
            U8  => overflow!($int_op, l as u8,  r as u8),
            U16 => overflow!($int_op, l as u16, r as u16),
            U32 => overflow!($int_op, l as u32, r as u32),
            U64 => overflow!($int_op, l as u64, r as u64),
            U128 => overflow!($int_op, l as u128, r as u128),
            _ => bug!("int_arithmetic should only be called on int primvals"),
        }
    })
}

macro_rules! int_shift {
    ($kind:expr, $int_op:ident, $l:expr, $r:expr) => ({
        let l = $l;
        let r = $r;
        let r_wrapped = r as u32;
        match $kind {
            I8  => overflow!($int_op, l as i8,  r_wrapped),
            I16 => overflow!($int_op, l as i16, r_wrapped),
            I32 => overflow!($int_op, l as i32, r_wrapped),
            I64 => overflow!($int_op, l as i64, r_wrapped),
            I128 => overflow!($int_op, l as i128, r_wrapped),
            U8  => overflow!($int_op, l as u8,  r_wrapped),
            U16 => overflow!($int_op, l as u16, r_wrapped),
            U32 => overflow!($int_op, l as u32, r_wrapped),
            U64 => overflow!($int_op, l as u64, r_wrapped),
            U128 => overflow!($int_op, l as u128, r_wrapped),
            _ => bug!("int_shift should only be called on int primvals"),
        }.map(|(val, over)| (val, over || r != r_wrapped as u128))
    })
}

macro_rules! float_arithmetic {
    ($from_bytes:ident, $to_bytes:ident, $float_op:tt, $l:expr, $r:expr) => ({
        let l = $from_bytes($l);
        let r = $from_bytes($r);
        let bytes = $to_bytes(l $float_op r);
        PrimVal::Bytes(bytes)
    })
}

macro_rules! f32_arithmetic {
    ($float_op:tt, $l:expr, $r:expr) => (
        float_arithmetic!(bytes_to_f32, f32_to_bytes, $float_op, $l, $r)
    )
}

macro_rules! f64_arithmetic {
    ($float_op:tt, $l:expr, $r:expr) => (
        float_arithmetic!(bytes_to_f64, f64_to_bytes, $float_op, $l, $r)
    )
}

impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> {
    /// Returns the result of the specified operation and whether it overflowed.
    pub fn binary_op(
        &self,
        bin_op: mir::BinOp,
        left: PrimVal,
        left_ty: Ty<'tcx>,
        right: PrimVal,
        right_ty: Ty<'tcx>,
    ) -> EvalResult<'tcx, (PrimVal, bool)> {
        use rustc::mir::BinOp::*;
        use super::PrimValKind::*;

        let left_kind = self.ty_to_primval_kind(left_ty)?;
        let right_kind = self.ty_to_primval_kind(right_ty)?;
        //trace!("Running binary op {:?}: {:?} ({:?}), {:?} ({:?})", bin_op, left, left_kind, right, right_kind);

        // I: Handle operations that support pointers
        if !left_kind.is_float() && !right_kind.is_float() {
            if let Some(handled) = M::try_ptr_op(self, bin_op, left, left_ty, right, right_ty)? {
                return Ok(handled);
            }
        }

        // II: From now on, everything must be bytes, no pointers
        let l = left.to_bytes()?;
        let r = right.to_bytes()?;

        // These ops can have an RHS with a different numeric type.
        if right_kind.is_int() && (bin_op == Shl || bin_op == Shr) {
            return match bin_op {
                Shl => int_shift!(left_kind, overflowing_shl, l, r),
                Shr => int_shift!(left_kind, overflowing_shr, l, r),
                _ => bug!("it has already been checked that this is a shift op"),
            };
        }

        if left_kind != right_kind {
            let msg = format!(
                "unimplemented binary op {:?}: {:?} ({:?}), {:?} ({:?})",
                bin_op,
                left,
                left_kind,
                right,
                right_kind
            );
            return err!(Unimplemented(msg));
        }

        let val = match (bin_op, left_kind) {
            (Eq, F32) => PrimVal::from_bool(bytes_to_f32(l) == bytes_to_f32(r)),
            (Ne, F32) => PrimVal::from_bool(bytes_to_f32(l) != bytes_to_f32(r)),
            (Lt, F32) => PrimVal::from_bool(bytes_to_f32(l) < bytes_to_f32(r)),
            (Le, F32) => PrimVal::from_bool(bytes_to_f32(l) <= bytes_to_f32(r)),
            (Gt, F32) => PrimVal::from_bool(bytes_to_f32(l) > bytes_to_f32(r)),
            (Ge, F32) => PrimVal::from_bool(bytes_to_f32(l) >= bytes_to_f32(r)),

            (Eq, F64) => PrimVal::from_bool(bytes_to_f64(l) == bytes_to_f64(r)),
            (Ne, F64) => PrimVal::from_bool(bytes_to_f64(l) != bytes_to_f64(r)),
            (Lt, F64) => PrimVal::from_bool(bytes_to_f64(l) < bytes_to_f64(r)),
            (Le, F64) => PrimVal::from_bool(bytes_to_f64(l) <= bytes_to_f64(r)),
            (Gt, F64) => PrimVal::from_bool(bytes_to_f64(l) > bytes_to_f64(r)),
            (Ge, F64) => PrimVal::from_bool(bytes_to_f64(l) >= bytes_to_f64(r)),

            (Add, F32) => f32_arithmetic!(+, l, r),
            (Sub, F32) => f32_arithmetic!(-, l, r),
            (Mul, F32) => f32_arithmetic!(*, l, r),
            (Div, F32) => f32_arithmetic!(/, l, r),
            (Rem, F32) => f32_arithmetic!(%, l, r),

            (Add, F64) => f64_arithmetic!(+, l, r),
            (Sub, F64) => f64_arithmetic!(-, l, r),
            (Mul, F64) => f64_arithmetic!(*, l, r),
            (Div, F64) => f64_arithmetic!(/, l, r),
            (Rem, F64) => f64_arithmetic!(%, l, r),

            (Eq, _) => PrimVal::from_bool(l == r),
            (Ne, _) => PrimVal::from_bool(l != r),

            (Lt, k) if k.is_signed_int() => PrimVal::from_bool((l as i128) < (r as i128)),
            (Lt, _) => PrimVal::from_bool(l < r),
            (Le, k) if k.is_signed_int() => PrimVal::from_bool((l as i128) <= (r as i128)),
            (Le, _) => PrimVal::from_bool(l <= r),
            (Gt, k) if k.is_signed_int() => PrimVal::from_bool((l as i128) > (r as i128)),
            (Gt, _) => PrimVal::from_bool(l > r),
            (Ge, k) if k.is_signed_int() => PrimVal::from_bool((l as i128) >= (r as i128)),
            (Ge, _) => PrimVal::from_bool(l >= r),

            (BitOr, _) => PrimVal::Bytes(l | r),
            (BitAnd, _) => PrimVal::Bytes(l & r),
            (BitXor, _) => PrimVal::Bytes(l ^ r),

            (Add, k) if k.is_int() => return int_arithmetic!(k, overflowing_add, l, r),
            (Sub, k) if k.is_int() => return int_arithmetic!(k, overflowing_sub, l, r),
            (Mul, k) if k.is_int() => return int_arithmetic!(k, overflowing_mul, l, r),
            (Div, k) if k.is_int() => return int_arithmetic!(k, overflowing_div, l, r),
            (Rem, k) if k.is_int() => return int_arithmetic!(k, overflowing_rem, l, r),

            _ => {
                let msg = format!(
                    "unimplemented binary op {:?}: {:?} ({:?}), {:?} ({:?})",
                    bin_op,
                    left,
                    left_kind,
                    right,
                    right_kind
                );
                return err!(Unimplemented(msg));
            }
        };

        Ok((val, false))
    }
}

pub fn unary_op<'tcx>(
    un_op: mir::UnOp,
    val: PrimVal,
    val_kind: PrimValKind,
) -> EvalResult<'tcx, PrimVal> {
    use rustc::mir::UnOp::*;
    use super::PrimValKind::*;

    let bytes = val.to_bytes()?;

    let result_bytes = match (un_op, val_kind) {
        (Not, Bool) => !val.to_bool()? as u128,

        (Not, U8) => !(bytes as u8) as u128,
        (Not, U16) => !(bytes as u16) as u128,
        (Not, U32) => !(bytes as u32) as u128,
        (Not, U64) => !(bytes as u64) as u128,
        (Not, U128) => !bytes,

        (Not, I8) => !(bytes as i8) as u128,
        (Not, I16) => !(bytes as i16) as u128,
        (Not, I32) => !(bytes as i32) as u128,
        (Not, I64) => !(bytes as i64) as u128,
        (Not, I128) => !(bytes as i128) as u128,

        (Neg, I8) => -(bytes as i8) as u128,
        (Neg, I16) => -(bytes as i16) as u128,
        (Neg, I32) => -(bytes as i32) as u128,
        (Neg, I64) => -(bytes as i64) as u128,
        (Neg, I128) => -(bytes as i128) as u128,

        (Neg, F32) => f32_to_bytes(-bytes_to_f32(bytes)),
        (Neg, F64) => f64_to_bytes(-bytes_to_f64(bytes)),

        _ => {
            let msg = format!("unimplemented unary op: {:?}, {:?}", un_op, val);
            return err!(Unimplemented(msg));
        }
    };

    Ok(PrimVal::Bytes(result_bytes))
}
<commit_msg>Use rustc's APFloat impl instead of interpreter host floats<commit_after>use rustc::mir;
use rustc::ty::Ty;
use rustc_const_math::ConstFloat;
use syntax::ast::FloatTy;
use std::cmp::Ordering;

use super::{EvalResult, EvalContext, Lvalue, Machine, ValTy};

use super::value::{PrimVal, PrimValKind, Value, bytes_to_f32, bytes_to_f64, f32_to_bytes,
                   f64_to_bytes};

impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> {
    fn binop_with_overflow(
        &mut self,
        op: mir::BinOp,
        left: ValTy<'tcx>,
        right: ValTy<'tcx>,
    ) -> EvalResult<'tcx, (PrimVal, bool)> {
        let left_val = self.value_to_primval(left)?;
        let right_val = self.value_to_primval(right)?;
        self.binary_op(op, left_val, left.ty, right_val, right.ty)
    }

    /// Applies the binary operation `op` to the two operands and writes a tuple of the result
    /// and a boolean signifying the potential overflow to the destination.
    pub fn intrinsic_with_overflow(
        &mut self,
        op: mir::BinOp,
        left: ValTy<'tcx>,
        right: ValTy<'tcx>,
        dest: Lvalue,
        dest_ty: Ty<'tcx>,
    ) -> EvalResult<'tcx> {
        let (val, overflowed) = self.binop_with_overflow(op, left, right)?;
        let val = Value::ByValPair(val, PrimVal::from_bool(overflowed));
        let valty = ValTy {
            value: val,
            ty: dest_ty,
        };
        self.write_value(valty, dest)
    }

    /// Applies the binary operation `op` to the arguments and writes the result to the
    /// destination. Returns `true` if the operation overflowed.
    pub fn intrinsic_overflowing(
        &mut self,
        op: mir::BinOp,
        left: ValTy<'tcx>,
        right: ValTy<'tcx>,
        dest: Lvalue,
        dest_ty: Ty<'tcx>,
    ) -> EvalResult<'tcx, bool> {
        let (val, overflowed) = self.binop_with_overflow(op, left, right)?;
        self.write_primval(dest, val, dest_ty)?;
        Ok(overflowed)
    }
}

macro_rules! overflow {
    ($op:ident, $l:expr, $r:expr) => ({
        let (val, overflowed) = $l.$op($r);
        let primval = PrimVal::Bytes(val as u128);
        Ok((primval, overflowed))
    })
}

macro_rules! int_arithmetic {
    ($kind:expr, $int_op:ident, $l:expr, $r:expr) => ({
        let l = $l;
        let r = $r;
        use super::PrimValKind::*;
        match $kind {
            I8  => overflow!($int_op, l as i8,  r as i8),
            I16 => overflow!($int_op, l as i16, r as i16),
            I32 => overflow!($int_op, l as i32, r as i32),
            I64 => overflow!($int_op, l as i64, r as i64),
            I128 => overflow!($int_op, l as i128, r as i128),
            U8  => overflow!($int_op, l as u8,  r as u8),
            U16 => overflow!($int_op, l as u16, r as u16),
            U32 => overflow!($int_op, l as u32, r as u32),
            U64 => overflow!($int_op, l as u64, r as u64),
            U128 => overflow!($int_op, l as u128, r as u128),
            _ => bug!("int_arithmetic should only be called on int primvals"),
        }
    })
}

macro_rules! int_shift {
    ($kind:expr, $int_op:ident, $l:expr, $r:expr) => ({
        let l = $l;
        let r = $r;
        let r_wrapped = r as u32;
        match $kind {
            I8  => overflow!($int_op, l as i8,  r_wrapped),
            I16 => overflow!($int_op, l as i16, r_wrapped),
            I32 => overflow!($int_op, l as i32, r_wrapped),
            I64 => overflow!($int_op, l as i64, r_wrapped),
            I128 => overflow!($int_op, l as i128, r_wrapped),
            U8  => overflow!($int_op, l as u8,  r_wrapped),
            U16 => overflow!($int_op, l as u16, r_wrapped),
            U32 => overflow!($int_op, l as u32, r_wrapped),
            U64 => overflow!($int_op, l as u64, r_wrapped),
            U128 => overflow!($int_op, l as u128, r_wrapped),
            _ => bug!("int_shift should only be called on int primvals"),
        }.map(|(val, over)| (val, over || r != r_wrapped as u128))
    })
}

impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> {
    /// Returns the result of the specified operation and whether it overflowed.
    pub fn binary_op(
        &self,
        bin_op: mir::BinOp,
        left: PrimVal,
        left_ty: Ty<'tcx>,
        right: PrimVal,
        right_ty: Ty<'tcx>,
    ) -> EvalResult<'tcx, (PrimVal, bool)> {
        use rustc::mir::BinOp::*;
        use super::PrimValKind::*;

        let left_kind = self.ty_to_primval_kind(left_ty)?;
        let right_kind = self.ty_to_primval_kind(right_ty)?;
        //trace!("Running binary op {:?}: {:?} ({:?}), {:?} ({:?})", bin_op, left, left_kind, right, right_kind);

        // I: Handle operations that support pointers
        if !left_kind.is_float() && !right_kind.is_float() {
            if let Some(handled) = M::try_ptr_op(self, bin_op, left, left_ty, right, right_ty)? {
                return Ok(handled);
            }
        }

        // II: From now on, everything must be bytes, no pointers
        let l = left.to_bytes()?;
        let r = right.to_bytes()?;

        // These ops can have an RHS with a different numeric type.
        if right_kind.is_int() && (bin_op == Shl || bin_op == Shr) {
            return match bin_op {
                Shl => int_shift!(left_kind, overflowing_shl, l, r),
                Shr => int_shift!(left_kind, overflowing_shr, l, r),
                _ => bug!("it has already been checked that this is a shift op"),
            };
        }

        if left_kind != right_kind {
            let msg = format!(
                "unimplemented binary op {:?}: {:?} ({:?}), {:?} ({:?})",
                bin_op,
                left,
                left_kind,
                right,
                right_kind
            );
            return err!(Unimplemented(msg));
        }

        let float_op = |op, l, r, ty| {
            let l = ConstFloat {
                bits: l,
                ty,
            };
            let r = ConstFloat {
                bits: r,
                ty,
            };
            match op {
                Eq => PrimVal::from_bool(l.try_cmp(r).unwrap() == Ordering::Equal),
                Ne => PrimVal::from_bool(l.try_cmp(r).unwrap() != Ordering::Equal),
                Lt => PrimVal::from_bool(l.try_cmp(r).unwrap() == Ordering::Less),
                Le => PrimVal::from_bool(l.try_cmp(r).unwrap() != Ordering::Greater),
                Gt => PrimVal::from_bool(l.try_cmp(r).unwrap() == Ordering::Greater),
                Ge => PrimVal::from_bool(l.try_cmp(r).unwrap() != Ordering::Less),
                Add => PrimVal::Bytes((l + r).unwrap().bits),
                Sub => PrimVal::Bytes((l - r).unwrap().bits),
                Mul => PrimVal::Bytes((l * r).unwrap().bits),
                Div => PrimVal::Bytes((l / r).unwrap().bits),
                Rem => PrimVal::Bytes((l % r).unwrap().bits),
                _ => bug!("invalid float op: `{:?}`", op),
            }
        };

        let val = match (bin_op, left_kind) {
            (_, F32) => float_op(bin_op, l, r, FloatTy::F32),
            (_, F64) => float_op(bin_op, l, r, FloatTy::F64),

            (Eq, _) => PrimVal::from_bool(l == r),
            (Ne, _) => PrimVal::from_bool(l != r),

            (Lt, k) if k.is_signed_int() => PrimVal::from_bool((l as i128) < (r as i128)),
            (Lt, _) => PrimVal::from_bool(l < r),
            (Le, k) if k.is_signed_int() => PrimVal::from_bool((l as i128) <= (r as i128)),
            (Le, _) => PrimVal::from_bool(l <= r),
            (Gt, k) if k.is_signed_int() => PrimVal::from_bool((l as i128) > (r as i128)),
            (Gt, _) => PrimVal::from_bool(l > r),
            (Ge, k) if k.is_signed_int() => PrimVal::from_bool((l as i128) >= (r as i128)),
            (Ge, _) => PrimVal::from_bool(l >= r),

            (BitOr, _) => PrimVal::Bytes(l | r),
            (BitAnd, _) => PrimVal::Bytes(l & r),
            (BitXor, _) => PrimVal::Bytes(l ^ r),

            (Add, k) if k.is_int() => return int_arithmetic!(k, overflowing_add, l, r),
            (Sub, k) if k.is_int() => return int_arithmetic!(k, overflowing_sub, l, r),
            (Mul, k) if k.is_int() => return int_arithmetic!(k, overflowing_mul, l, r),
            (Div, k) if k.is_int() => return int_arithmetic!(k, overflowing_div, l, r),
            (Rem, k) if k.is_int() => return int_arithmetic!(k, overflowing_rem, l, r),

            _ => {
                let msg = format!(
                    "unimplemented binary op {:?}: {:?} ({:?}), {:?} ({:?})",
                    bin_op,
                    left,
                    left_kind,
                    right,
                    right_kind
                );
                return err!(Unimplemented(msg));
            }
        };

        Ok((val, false))
    }
}

pub fn unary_op<'tcx>(
    un_op: mir::UnOp,
    val: PrimVal,
    val_kind: PrimValKind,
) -> EvalResult<'tcx, PrimVal> {
    use rustc::mir::UnOp::*;
    use super::PrimValKind::*;

    let bytes = val.to_bytes()?;

    let result_bytes = match (un_op, val_kind) {
        (Not, Bool) => !val.to_bool()? as u128,

        (Not, U8) => !(bytes as u8) as u128,
        (Not, U16) => !(bytes as u16) as u128,
        (Not, U32) => !(bytes as u32) as u128,
        (Not, U64) => !(bytes as u64) as u128,
        (Not, U128) => !bytes,

        (Not, I8) => !(bytes as i8) as u128,
        (Not, I16) => !(bytes as i16) as u128,
        (Not, I32) => !(bytes as i32) as u128,
        (Not, I64) => !(bytes as i64) as u128,
        (Not, I128) => !(bytes as i128) as u128,

        (Neg, I8) => -(bytes as i8) as u128,
        (Neg, I16) => -(bytes as i16) as u128,
        (Neg, I32) => -(bytes as i32) as u128,
        (Neg, I64) => -(bytes as i64) as u128,
        (Neg, I128) => -(bytes as i128) as u128,

        (Neg, F32) => f32_to_bytes(-bytes_to_f32(bytes)),
        (Neg, F64) => f64_to_bytes(-bytes_to_f64(bytes)),

        _ => {
            let msg = format!("unimplemented unary op: {:?}, {:?}", un_op, val);
            return err!(Unimplemented(msg));
        }
    };

    Ok(PrimVal::Bytes(result_bytes))
}
<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use std::ascii::StrAsciiExt;
use extra::arc::Arc;
use extra::sort::tim_sort;

use selectors::*;
use stylesheets::{Stylesheet, iter_style_rules};
use media_queries::{Device, Screen};
use properties::{PropertyDeclaration, PropertyDeclarationBlock};
use servo_util::tree::{TreeNodeRefAsElement, TreeNode, ElementLike};

pub enum StylesheetOrigin {
    UserAgentOrigin,
    AuthorOrigin,
    UserOrigin,
}

pub struct Stylist {
    priv ua_rules: PerOriginRules,
    priv author_rules: PerOriginRules,
    priv user_rules: PerOriginRules,
}

impl Stylist {
    #[inline]
    pub fn new() -> Stylist {
        Stylist {
            ua_rules: PerOriginRules::new(),
            author_rules: PerOriginRules::new(),
            user_rules: PerOriginRules::new(),
        }
    }

    pub fn add_stylesheet(&mut self, stylesheet: Stylesheet, origin: StylesheetOrigin) {
        let rules = match origin {
            UserAgentOrigin => &mut self.ua_rules,
            AuthorOrigin => &mut self.author_rules,
            UserOrigin => &mut self.user_rules,
        };
        let mut added_normal_declarations = false;
        let mut added_important_declarations = false;

        macro_rules! append(
            ($priority: ident, $flag: ident) => {
                if style_rule.declarations.$priority.len() > 0 {
                    $flag = true;
                    for selector in style_rule.selectors.iter() {
                        // TODO: avoid copying?
                        rules.$priority.push(Rule {
                            selector: @(*selector).clone(),
                            declarations: Arc::new(style_rule.declarations.$priority.clone()),
                        })
                    }
                }
            };
        )

        let device = &Device { media_type: Screen };  // TODO, use Print when printing
        do iter_style_rules(stylesheet.rules.as_slice(), device) |style_rule| {
            append!(normal, added_normal_declarations);
            append!(important, added_important_declarations);
        }

        // These sorts need to be stable
        // Do not sort already-sorted unchanged vectors
        if added_normal_declarations {
            tim_sort(rules.normal)
        }
        if added_important_declarations {
            tim_sort(rules.important)
        }
    }

    pub fn get_applicable_declarations<N: TreeNode<T>, T: TreeNodeRefAsElement<N, E>, E: ElementLike>(
            &self, element: &T, style_attribute: Option<&PropertyDeclarationBlock>,
            pseudo_element: Option<PseudoElement>) -> ~[Arc<~[PropertyDeclaration]>] {
        assert!(element.is_element())
        assert!(style_attribute.is_none() || pseudo_element.is_none(),
                "Style attributes do not apply to pseudo-elements")
        let mut applicable_declarations = ~[];  // TODO: use an iterator?

        macro_rules! append(
            ($rules: expr) => {
                for rule in $rules.iter() {
                    if matches_selector::<N, T, E>(rule.selector, element, pseudo_element) {
                        applicable_declarations.push(rule.declarations.clone())
                    }
                }
            };
        );

        // In cascading order
        append!(self.ua_rules.normal);
        append!(self.user_rules.normal);

        // Style attributes have author origin but higher specificity than style rules.
        append!(self.author_rules.normal);
        // TODO: avoid copying?
        style_attribute.map(|sa| applicable_declarations.push(Arc::new(sa.normal.clone())));

        append!(self.author_rules.important);
        // TODO: avoid copying?
        style_attribute.map(|sa| applicable_declarations.push(Arc::new(sa.important.clone())));

        append!(self.user_rules.important);
        append!(self.ua_rules.important);

        applicable_declarations
    }
}

struct PerOriginRules {
    normal: ~[Rule],
    important: ~[Rule],
}

impl PerOriginRules {
    #[inline]
    fn new() -> PerOriginRules {
        PerOriginRules { normal: ~[], important: ~[] }
    }
}

#[deriving(Clone)]
struct Rule {
    selector: @Selector,
    declarations: Arc<~[PropertyDeclaration]>,
}

impl Ord for Rule {
    #[inline]
    fn lt(&self, other: &Rule) -> bool {
        self.selector.specificity < other.selector.specificity
    }
}

#[inline]
fn matches_selector<N: TreeNode<T>, T: TreeNodeRefAsElement<N, E>, E: ElementLike>(
        selector: &Selector, element: &T, pseudo_element: Option<PseudoElement>) -> bool {
    selector.pseudo_element == pseudo_element &&
    matches_compound_selector::<N, T, E>(&selector.compound_selectors, element)
}

fn matches_compound_selector<N: TreeNode<T>, T: TreeNodeRefAsElement<N, E>, E: ElementLike>(
        selector: &CompoundSelector, element: &T) -> bool {
    if !do selector.simple_selectors.iter().all |simple_selector| {
            matches_simple_selector(simple_selector, element)
    } {
        return false
    }
    match selector.next {
        None => true,
        Some((ref next_selector, combinator)) => {
            let (siblings, just_one) = match combinator {
                Child => (false, true),
                Descendant => (false, false),
                NextSibling => (true, true),
                LaterSibling => (true, false),
            };
            let mut node = element.clone();
            loop {
                let next_node = if siblings {
                    node.node().prev_sibling()
                } else {
                    node.node().parent_node()
                };
                match next_node {
                    None => return false,
                    Some(next_node) => node = next_node,
                }
                if node.is_element() {
                    if matches_compound_selector(&**next_selector, &node) {
                        return true
                    } else if just_one {
                        return false
                    }
                }
            }
        }
    }
}

#[inline]
fn matches_simple_selector<N: TreeNode<T>, T: TreeNodeRefAsElement<N, E>, E: ElementLike>(
        selector: &SimpleSelector, element: &T) -> bool {
    static WHITESPACE: &'static [char] = &'static [' ', '\t', '\n', '\r', '\x0C'];

    match *selector {
        // TODO: case-sensitivity depends on the document type
        // TODO: intern element names
        LocalNameSelector(ref name) => {
            do element.with_imm_element_like |element: &E| {
                element.get_local_name().eq_ignore_ascii_case(name.as_slice())
            }
        }
        NamespaceSelector(_) => false,  // TODO, when the DOM supports namespaces on elements.
        // TODO: case-sensitivity depends on the document type and quirks mode
        // TODO: cache and intern IDs on elements.
        IDSelector(ref id) => {
            do element.with_imm_element_like |element: &E| {
                element.get_attr("id") == Some(id.as_slice())
            }
        }
        // TODO: cache and intern classe names on elements.
        ClassSelector(ref class) => {
            do element.with_imm_element_like |element: &E| {
                match element.get_attr("class") {
                    None => false,
                    // TODO: case-sensitivity depends on the document type and quirks mode
                    Some(ref class_attr)
                    => class_attr.split_iter(WHITESPACE).any(|c| c == class.as_slice()),
                }
            }
        }

        AttrExists(ref attr) => match_attribute(attr, element, |_| true),
        AttrEqual(ref attr, ref value) => match_attribute(attr, element, |v| v == value.as_slice()),
        AttrIncludes(ref attr, ref value) => do match_attribute(attr, element) |attr_value| {
            attr_value.split_iter(WHITESPACE).any(|v| v == value.as_slice())
        },
        AttrDashMatch(ref attr, ref value, ref dashing_value)
        => do match_attribute(attr, element) |attr_value| {
            attr_value == value.as_slice() || attr_value.starts_with(dashing_value.as_slice())
        },
        AttrPrefixMatch(ref attr, ref value) => do match_attribute(attr, element) |attr_value| {
            attr_value.starts_with(value.as_slice())
        },
        AttrSubstringMatch(ref attr, ref value) => do match_attribute(attr, element) |attr_value| {
            attr_value.contains(value.as_slice())
        },
        AttrSuffixMatch(ref attr, ref value) => do match_attribute(attr, element) |attr_value| {
            attr_value.ends_with(value.as_slice())
        },

        FirstChild => matches_first_child(element),

        Negation(ref negated) => {
            !negated.iter().all(|s| matches_simple_selector(s, element))
        },
    }
}

#[inline]
fn matches_first_child<N: TreeNode<T>, T: TreeNodeRefAsElement<N, E>, E: ElementLike>(
        element: &T) -> bool {
    let mut node = element.clone();
    loop {
        match node.node().prev_sibling() {
            Some(prev_sibling) => {
                node = prev_sibling;
                if node.is_element() {
                    return false
                }
            }
            None => return !element.is_root(),
        }
    }
}

#[inline]
fn match_attribute<N: TreeNode<T>, T: TreeNodeRefAsElement<N, E>, E: ElementLike>(
        attr: &AttrSelector, element: &T, f: &fn(&str)-> bool) -> bool {
    do element.with_imm_element_like |element: &E| {
        match attr.namespace {
            Some(_) => false,  // TODO, when the DOM supports namespaces on attributes
            None => match element.get_attr(attr.name) {
                None => false,
                Some(ref value) => f(value.as_slice())
            }
        }
    }
}
<commit_msg>auto merge of #1146 : SimonSapin/servo/less-at-pointers, r=jdm<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use std::ascii::StrAsciiExt;
use extra::arc::Arc;
use extra::sort::tim_sort;

use selectors::*;
use stylesheets::{Stylesheet, iter_style_rules};
use media_queries::{Device, Screen};
use properties::{PropertyDeclaration, PropertyDeclarationBlock};
use servo_util::tree::{TreeNodeRefAsElement, TreeNode, ElementLike};

pub enum StylesheetOrigin {
    UserAgentOrigin,
    AuthorOrigin,
    UserOrigin,
}

pub struct Stylist {
    priv ua_rules: PerOriginRules,
    priv author_rules: PerOriginRules,
    priv user_rules: PerOriginRules,
}

impl Stylist {
    #[inline]
    pub fn new() -> Stylist {
        Stylist {
            ua_rules: PerOriginRules::new(),
            author_rules: PerOriginRules::new(),
            user_rules: PerOriginRules::new(),
        }
    }

    pub fn add_stylesheet(&mut self, stylesheet: Stylesheet, origin: StylesheetOrigin) {
        let rules = match origin {
            UserAgentOrigin => &mut self.ua_rules,
            AuthorOrigin => &mut self.author_rules,
            UserOrigin => &mut self.user_rules,
        };
        let mut added_normal_declarations = false;
        let mut added_important_declarations = false;

        macro_rules! append(
            ($priority: ident, $flag: ident) => {
                if style_rule.declarations.$priority.len() > 0 {
                    $flag = true;
                    for selector in style_rule.selectors.iter() {
                        // TODO: avoid copying?
                        rules.$priority.push(Rule {
                            selector: selector.clone(),
                            declarations: Arc::new(style_rule.declarations.$priority.clone()),
                        })
                    }
                }
            };
        )

        let device = &Device { media_type: Screen };  // TODO, use Print when printing
        do iter_style_rules(stylesheet.rules.as_slice(), device) |style_rule| {
            append!(normal, added_normal_declarations);
            append!(important, added_important_declarations);
        }

        // These sorts need to be stable
        // Do not sort already-sorted unchanged vectors
        if added_normal_declarations {
            tim_sort(rules.normal)
        }
        if added_important_declarations {
            tim_sort(rules.important)
        }
    }

    pub fn get_applicable_declarations<N: TreeNode<T>, T: TreeNodeRefAsElement<N, E>, E: ElementLike>(
            &self, element: &T, style_attribute: Option<&PropertyDeclarationBlock>,
            pseudo_element: Option<PseudoElement>) -> ~[Arc<~[PropertyDeclaration]>] {
        assert!(element.is_element())
        assert!(style_attribute.is_none() || pseudo_element.is_none(),
                "Style attributes do not apply to pseudo-elements")
        let mut applicable_declarations = ~[];  // TODO: use an iterator?

        macro_rules! append(
            ($rules: expr) => {
                for rule in $rules.iter() {
                    if matches_selector::<N, T, E>(&rule.selector, element, pseudo_element) {
                        applicable_declarations.push(rule.declarations.clone())
                    }
                }
            };
        );

        // In cascading order
        append!(self.ua_rules.normal);
        append!(self.user_rules.normal);

        // Style attributes have author origin but higher specificity than style rules.
        append!(self.author_rules.normal);
        // TODO: avoid copying?
        style_attribute.map(|sa| applicable_declarations.push(Arc::new(sa.normal.clone())));

        append!(self.author_rules.important);
        // TODO: avoid copying?
        style_attribute.map(|sa| applicable_declarations.push(Arc::new(sa.important.clone())));

        append!(self.user_rules.important);
        append!(self.ua_rules.important);

        applicable_declarations
    }
}

struct PerOriginRules {
    normal: ~[Rule],
    important: ~[Rule],
}

impl PerOriginRules {
    #[inline]
    fn new() -> PerOriginRules {
        PerOriginRules { normal: ~[], important: ~[] }
    }
}

#[deriving(Clone)]
struct Rule {
    selector: Selector,
    declarations: Arc<~[PropertyDeclaration]>,
}

impl Ord for Rule {
    #[inline]
    fn lt(&self, other: &Rule) -> bool {
        self.selector.specificity < other.selector.specificity
    }
}

#[inline]
fn matches_selector<N: TreeNode<T>, T: TreeNodeRefAsElement<N, E>, E: ElementLike>(
        selector: &Selector, element: &T, pseudo_element: Option<PseudoElement>) -> bool {
    selector.pseudo_element == pseudo_element &&
    matches_compound_selector::<N, T, E>(&selector.compound_selectors, element)
}

fn matches_compound_selector<N: TreeNode<T>, T: TreeNodeRefAsElement<N, E>, E: ElementLike>(
        selector: &CompoundSelector, element: &T) -> bool {
    if !do selector.simple_selectors.iter().all |simple_selector| {
            matches_simple_selector(simple_selector, element)
    } {
        return false
    }
    match selector.next {
        None => true,
        Some((ref next_selector, combinator)) => {
            let (siblings, just_one) = match combinator {
                Child => (false, true),
                Descendant => (false, false),
                NextSibling => (true, true),
                LaterSibling => (true, false),
            };
            let mut node = element.clone();
            loop {
                let next_node = if siblings {
                    node.node().prev_sibling()
                } else {
                    node.node().parent_node()
                };
                match next_node {
                    None => return false,
                    Some(next_node) => node = next_node,
                }
                if node.is_element() {
                    if matches_compound_selector(&**next_selector, &node) {
                        return true
                    } else if just_one {
                        return false
                    }
                }
            }
        }
    }
}

#[inline]
fn matches_simple_selector<N: TreeNode<T>, T: TreeNodeRefAsElement<N, E>, E: ElementLike>(
        selector: &SimpleSelector, element: &T) -> bool {
    static WHITESPACE: &'static [char] = &'static [' ', '\t', '\n', '\r', '\x0C'];

    match *selector {
        // TODO: case-sensitivity depends on the document type
        // TODO: intern element names
        LocalNameSelector(ref name) => {
            do element.with_imm_element_like |element: &E| {
                element.get_local_name().eq_ignore_ascii_case(name.as_slice())
            }
        }
        NamespaceSelector(_) => false,  // TODO, when the DOM supports namespaces on elements.
        // TODO: case-sensitivity depends on the document type and quirks mode
        // TODO: cache and intern IDs on elements.
        IDSelector(ref id) => {
            do element.with_imm_element_like |element: &E| {
                element.get_attr("id") == Some(id.as_slice())
            }
        }
        // TODO: cache and intern classe names on elements.
        ClassSelector(ref class) => {
            do element.with_imm_element_like |element: &E| {
                match element.get_attr("class") {
                    None => false,
                    // TODO: case-sensitivity depends on the document type and quirks mode
                    Some(ref class_attr)
                    => class_attr.split_iter(WHITESPACE).any(|c| c == class.as_slice()),
                }
            }
        }

        AttrExists(ref attr) => match_attribute(attr, element, |_| true),
        AttrEqual(ref attr, ref value) => match_attribute(attr, element, |v| v == value.as_slice()),
        AttrIncludes(ref attr, ref value) => do match_attribute(attr, element) |attr_value| {
            attr_value.split_iter(WHITESPACE).any(|v| v == value.as_slice())
        },
        AttrDashMatch(ref attr, ref value, ref dashing_value)
        => do match_attribute(attr, element) |attr_value| {
            attr_value == value.as_slice() || attr_value.starts_with(dashing_value.as_slice())
        },
        AttrPrefixMatch(ref attr, ref value) => do match_attribute(attr, element) |attr_value| {
            attr_value.starts_with(value.as_slice())
        },
        AttrSubstringMatch(ref attr, ref value) => do match_attribute(attr, element) |attr_value| {
            attr_value.contains(value.as_slice())
        },
        AttrSuffixMatch(ref attr, ref value) => do match_attribute(attr, element) |attr_value| {
            attr_value.ends_with(value.as_slice())
        },

        FirstChild => matches_first_child(element),

        Negation(ref negated) => {
            !negated.iter().all(|s| matches_simple_selector(s, element))
        },
    }
}

#[inline]
fn matches_first_child<N: TreeNode<T>, T: TreeNodeRefAsElement<N, E>, E: ElementLike>(
        element: &T) -> bool {
    let mut node = element.clone();
    loop {
        match node.node().prev_sibling() {
            Some(prev_sibling) => {
                node = prev_sibling;
                if node.is_element() {
                    return false
                }
            }
            None => return !element.is_root(),
        }
    }
}

#[inline]
fn match_attribute<N: TreeNode<T>, T: TreeNodeRefAsElement<N, E>, E: ElementLike>(
        attr: &AttrSelector, element: &T, f: &fn(&str)-> bool) -> bool {
    do element.with_imm_element_like |element: &E| {
        match attr.namespace {
            Some(_) => false,  // TODO, when the DOM supports namespaces on attributes
            None => match element.get_attr(attr.name) {
                None => false,
                Some(ref value) => f(value.as_slice())
            }
        }
    }
}
<|endoftext|>