<commit_before>use math::Matrix4;

#[derive(Debug,PartialEq)]
pub struct Matrix3 {
    pub elements: [f32; 9],
}

impl Matrix3 {
    pub const IDENTITY: Matrix3 =
        Matrix3 { elements: [1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0] };

    pub fn from_columns(column1: (f32, f32, f32),
                        column2: (f32, f32, f32),
                        column3: (f32, f32, f32))
                        -> Matrix3 {
        Matrix3 {
            elements: [column1.0, column1.1, column1.2, column2.0, column2.1, column2.2,
                       column3.0, column3.1, column3.2],
        }
    }

    pub fn from_rows(row1: (f32, f32, f32),
                     row2: (f32, f32, f32),
                     row3: (f32, f32, f32))
                     -> Matrix3 {
        Matrix3 {
            elements: [row1.0, row2.0, row3.0, row1.1, row2.1, row3.1, row1.2, row2.2, row3.2],
        }
    }

    pub fn new(column1: (f32, f32, f32),
               column2: (f32, f32, f32),
               column3: (f32, f32, f32))
               -> Matrix3 {
        Matrix3 {
            elements: [column1.0, column1.1, column1.2, column2.0, column2.1, column2.2,
                       column3.0, column3.1, column3.2],
        }
    }

    pub fn from_matrix4(m: &Matrix4) -> Matrix3 {
        let me = m.elements;
        Matrix3 { elements: [me[0], me[4], me[8], me[1], me[5], me[9], me[2], me[6], me[10]] }
    }

    pub fn multiply_scalar(&self, s: f32) -> Matrix3 {
        Matrix3 {
            elements: [self.elements[0] * s,
                       self.elements[1] * s,
                       self.elements[2] * s,
                       self.elements[3] * s,
                       self.elements[4] * s,
                       self.elements[5] * s,
                       self.elements[6] * s,
                       self.elements[7] * s,
                       self.elements[8] * s],
        }
    }

    pub fn determinant(&self) -> f32 {
        let [a, b, c, d, e, f, g, h, i] = self.elements;
        a * e * i - a * f * h - b * d * i + b * f * g + c * d * h - c * e * g
    }

    pub fn inverse(&self) -> Matrix3 {
        let [n11, n21, n31, n12, n22, n32, n13, n23, n33] = self.elements;
        let t11 = n33 * n22 - n32 * n23;
        let t12 = n32 * n13 - n33 * n12;
        let t13 = n23 * n12 - n22 * n13;
        let determinant = n11 * t11 + n21 * t12 + n31 * t13;

        if determinant == 0.0 {
            panic!("Can't invert matrix, determinant is 0");
            // return Matrix3::IDENTITY;
        }

        let determinant_inverse = 1.0 / determinant;

        Matrix3 {
            elements: [t11 * determinant_inverse,
                       (n31 * n23 - n33 * n21) * determinant_inverse,
                       (n32 * n21 - n31 * n22) * determinant_inverse,
                       t12 * determinant_inverse,
                       (n33 * n11 - n31 * n13) * determinant_inverse,
                       (n31 * n12 - n32 * n11) * determinant_inverse,
                       t13 * determinant_inverse,
                       (n21 * n13 - n23 * n11) * determinant_inverse,
                       (n22 * n11 - n21 * n12) * determinant_inverse],
        }
    }

    pub fn transpose(&self) -> Matrix3 {
        Matrix3 {
            elements: [self.elements[0],
                       self.elements[3],
                       self.elements[6],
                       self.elements[1],
                       self.elements[4],
                       self.elements[7],
                       self.elements[2],
                       self.elements[5],
                       self.elements[8]],
        }
    }

    pub fn normal_matrix(m: &Matrix4) -> Matrix3 {
        Matrix3::from_matrix4(m).inverse().transpose()
    }

    pub fn to_matrix4(&self) -> Matrix4 {
        let m = self.elements;
        Matrix4::from_columns((m[0], m[1], m[2], 0.0),
                              (m[3], m[4], m[5], 0.0),
                              (m[6], m[7], m[8], 0.0),
                              (0.0, 0.0, 0.0, 1.0))
    }
}

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

    const TOLERANCE: f32 = 0.0001;

    fn matrix3_close_enough(a: &Matrix3, b: &Matrix3) -> bool {
        for i in 0..9 {
            let delta = a.elements[i] - b.elements[i];
            if delta > TOLERANCE {
                return false;
            }
        }

        true
    }

    #[test]
    fn constructor() {
        let a = Matrix3::IDENTITY;
        assert_eq!(a.determinant(), 1.0);

        let b = Matrix3::from_rows((0.0, 1.0, 2.0), (3.0, 4.0, 5.0), (6.0, 7.0, 8.0));
        assert_eq!(b.elements[0], 0.0);
        assert_eq!(b.elements[1], 3.0);
        assert_eq!(b.elements[2], 6.0);
        assert_eq!(b.elements[3], 1.0);
        assert_eq!(b.elements[4], 4.0);
        assert_eq!(b.elements[5], 7.0);
        assert_eq!(b.elements[6], 2.0);
        assert_eq!(b.elements[7], 5.0);
        assert_eq!(b.elements[8], 8.0);

        assert!(!matrix3_close_enough(&a, &b));
    }

    #[test]
    fn copy() {
        let mut a = Matrix3::from_columns((0.0, 1.0, 2.0), (3.0, 4.0, 5.0), (6.0, 7.0, 8.0));
        let b = Matrix3 { ..a };

        assert!(matrix3_close_enough(&a, &b));

        // ensure that it is a true copy
        a.elements[0] = 2.0;
        assert!(!matrix3_close_enough(&a, &b));
    }

    #[test]
    fn multiply_scalar() {
        let a = Matrix3::from_rows((0.0, 1.0, 2.0), (3.0, 4.0, 5.0), (6.0, 7.0, 8.0));
        assert_eq!(a.elements[0], 0.0);
        assert_eq!(a.elements[1], 3.0);
        assert_eq!(a.elements[2], 6.0);
        assert_eq!(a.elements[3], 1.0);
        assert_eq!(a.elements[4], 4.0);
        assert_eq!(a.elements[5], 7.0);
        assert_eq!(a.elements[6], 2.0);
        assert_eq!(a.elements[7], 5.0);
        assert_eq!(a.elements[8], 8.0);

        let b = a.multiply_scalar(2.0);
        assert_eq!(b.elements[0], 0.0 * 2.0);
        assert_eq!(b.elements[1], 3.0 * 2.0);
        assert_eq!(b.elements[2], 6.0 * 2.0);
        assert_eq!(b.elements[3], 1.0 * 2.0);
        assert_eq!(b.elements[4], 4.0 * 2.0);
        assert_eq!(b.elements[5], 7.0 * 2.0);
        assert_eq!(b.elements[6], 2.0 * 2.0);
        assert_eq!(b.elements[7], 5.0 * 2.0);
        assert_eq!(b.elements[8], 8.0 * 2.0);
    }

    // test( "determinant", function() {
    // 	var a = new THREE.Matrix3();
    // 	assert_eq!( a.determinant() == 1);

    // 	a.elements[0] = 2;
    // 	assert_eq!( a.determinant() == 2);

    // 	a.elements[0] = 0;
    // 	assert_eq!( a.determinant() == 0);

    // 	// calculated via http://www.euclideanspace.com/maths/algebra/matrix/functions/determinant/threeD/index.htm
    // 	a.set( 2, 3, 4, 5, 13, 7, 8, 9, 11 );
    // 	assert_eq!( a.determinant() == -73);
    // });

    // test( "getInverse", function() {
    // 	var identity = new THREE.Matrix3();
    // 	var identity4 = new THREE.Matrix4();
    // 	var a = new THREE.Matrix3();
    // 	var b = new THREE.Matrix3().set( 0, 0, 0, 0, 0, 0, 0, 0, 0 );
    // 	var c = new THREE.Matrix3().set( 0, 0, 0, 0, 0, 0, 0, 0, 0 );

    // 	b.getInverse( a, false );
    // 	assert_eq!( matrix3_close_enough( a, identity ));

    // 	try {
    // 		b.getInverse( c, true );
    // 		assert_eq!( false); // should never get here.
    // 	}
    // 	catch( err ) {
    // 		assert_eq!( true);
    // 	}

    // 	var testMatrices = [
    // 		new THREE.Matrix4().makeRotationX( 0.3 ),
    // 		new THREE.Matrix4().makeRotationX( -0.3 ),
    // 		new THREE.Matrix4().makeRotationY( 0.3 ),
    // 		new THREE.Matrix4().makeRotationY( -0.3 ),
    // 		new THREE.Matrix4().makeRotationZ( 0.3 ),
    // 		new THREE.Matrix4().makeRotationZ( -0.3 ),
    // 		new THREE.Matrix4().makeScale( 1, 2, 3 ),
    // 		new THREE.Matrix4().makeScale( 1/8, 1/2, 1/3 )
    // 		];

    // 	for( var i = 0, il = testMatrices.length; i < il; i ++ ) {
    // 		var m = testMatrices[i];

    // 		a.setFromMatrix4( m );
    // 		var mInverse3 = b.getInverse( a );

    // 		var mInverse = toMatrix4( mInverse3 );

    // 		// the determinant of the inverse should be the reciprocal
    // 		assert_eq!( Math.abs( a.determinant() * mInverse3.determinant() - 1 ) < 0.0001);
    // 		assert_eq!( Math.abs( m.determinant() * mInverse.determinant() - 1 ) < 0.0001);

    // 		var mProduct = new THREE.Matrix4().multiplyMatrices( m, mInverse );
    // 		assert_eq!( Math.abs( mProduct.determinant() - 1 ) < 0.0001);
    // 		assert_eq!( matrix3_close_enough( mProduct, identity4 ));
    // 	}
    // });

    // test( "transpose", function() {
    // 	var a = new THREE.Matrix3();
    // 	var b = a.clone().transpose();
    // 	assert_eq!( matrix3_close_enough( a, b ));

    // 	b = new THREE.Matrix3().set( 0, 1, 2, 3, 4, 5, 6, 7, 8 );
    // 	var c = b.clone().transpose();
    // 	assert_eq!( ! matrix3_close_enough( b, c ));
    // 	c.transpose();
    // 	assert_eq!( matrix3_close_enough( b, c ));
    // });

    // test( "clone", function() {
    // 	var a = new THREE.Matrix3().set( 0, 1, 2, 3, 4, 5, 6, 7, 8 );
    // 	var b = a.clone();

    // 	assert_eq!( matrix3_close_enough( a, b ));

    // 	// ensure that it is a true copy
    // 	a.elements[0] = 2;
    // 	assert_eq!( ! matrix3_close_enough( a, b ));
    // });
}<commit_msg>update matrix3 to include tests<commit_after>use math::Matrix4;

#[derive(Debug,PartialEq)]
pub struct Matrix3 {
    pub elements: [f32; 9],
}

impl Matrix3 {
    pub const IDENTITY: Matrix3 =
        Matrix3 { elements: [1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0] };

    pub fn from_columns(column1: (f32, f32, f32),
                        column2: (f32, f32, f32),
                        column3: (f32, f32, f32))
                        -> Matrix3 {
        Matrix3 {
            elements: [column1.0, column1.1, column1.2, column2.0, column2.1, column2.2,
                       column3.0, column3.1, column3.2],
        }
    }

    pub fn from_rows(row1: (f32, f32, f32),
                     row2: (f32, f32, f32),
                     row3: (f32, f32, f32))
                     -> Matrix3 {
        Matrix3 {
            elements: [row1.0, row2.0, row3.0, row1.1, row2.1, row3.1, row1.2, row2.2, row3.2],
        }
    }

    pub fn new(column1: (f32, f32, f32),
               column2: (f32, f32, f32),
               column3: (f32, f32, f32))
               -> Matrix3 {
        Matrix3 {
            elements: [column1.0, column1.1, column1.2, column2.0, column2.1, column2.2,
                       column3.0, column3.1, column3.2],
        }
    }

    pub fn from_matrix4(m: &Matrix4) -> Matrix3 {
        let me = m.elements;
        Matrix3 { elements: [me[0], me[4], me[8], me[1], me[5], me[9], me[2], me[6], me[10]] }
    }

    pub fn multiply_scalar(&self, s: f32) -> Matrix3 {
        Matrix3 {
            elements: [self.elements[0] * s,
                       self.elements[1] * s,
                       self.elements[2] * s,
                       self.elements[3] * s,
                       self.elements[4] * s,
                       self.elements[5] * s,
                       self.elements[6] * s,
                       self.elements[7] * s,
                       self.elements[8] * s],
        }
    }

    pub fn determinant(&self) -> f32 {
        let [a, b, c, d, e, f, g, h, i] = self.elements;
        a * e * i - a * f * h - b * d * i + b * f * g + c * d * h - c * e * g
    }

    pub fn inverse(&self) -> Matrix3 {
        let [n11, n21, n31, n12, n22, n32, n13, n23, n33] = self.elements;
        let t11 = n33 * n22 - n32 * n23;
        let t12 = n32 * n13 - n33 * n12;
        let t13 = n23 * n12 - n22 * n13;
        let determinant = n11 * t11 + n21 * t12 + n31 * t13;

        if determinant == 0.0 {
            println!("Matrix3::inverse> Can't invert matrix, determinant is 0");
            Matrix3::IDENTITY
        } else {
            let determinant_inverse = 1.0 / determinant;

            Matrix3::from_columns((t11 * determinant_inverse,
                                   (n31 * n23 - n33 * n21) * determinant_inverse,
                                   (n32 * n21 - n31 * n22) * determinant_inverse),
                                  (t12 * determinant_inverse,
                                   (n33 * n11 - n31 * n13) * determinant_inverse,
                                   (n31 * n12 - n32 * n11) * determinant_inverse),
                                  (t13 * determinant_inverse,
                                   (n21 * n13 - n23 * n11) * determinant_inverse,
                                   (n22 * n11 - n21 * n12) * determinant_inverse))
        }
    }

    pub fn transpose(&self) -> Matrix3 {
        Matrix3 {
            elements: [self.elements[0],
                       self.elements[3],
                       self.elements[6],
                       self.elements[1],
                       self.elements[4],
                       self.elements[7],
                       self.elements[2],
                       self.elements[5],
                       self.elements[8]],
        }
    }

    pub fn normal_matrix(m: &Matrix4) -> Matrix3 {
        Matrix3::from_matrix4(m).inverse().transpose()
    }

    pub fn to_matrix4(&self) -> Matrix4 {
        let m = self.elements;
        Matrix4::from_columns((m[0], m[1], m[2], 0.0),
                              (m[3], m[4], m[5], 0.0),
                              (m[6], m[7], m[8], 0.0),
                              (0.0, 0.0, 0.0, 1.0))
    }
}

#[cfg(test)]
mod tests {
    use super::Matrix3;
    use math::{Vector3, Matrix4};

    const TOLERANCE: f32 = 0.0001;

    fn matrix3_close_enough(a: &Matrix3, b: &Matrix3) -> bool {
        for i in 0..9 {
            let delta = a.elements[i] - b.elements[i];
            if delta > TOLERANCE {
                return false;
            }
        }

        true
    }

    #[test]
    fn constructor() {
        let a = Matrix3::IDENTITY;
        assert_eq!(a.determinant(), 1.0);

        let b = Matrix3::from_rows((0.0, 1.0, 2.0), (3.0, 4.0, 5.0), (6.0, 7.0, 8.0));
        assert_eq!(b.elements[0], 0.0);
        assert_eq!(b.elements[1], 3.0);
        assert_eq!(b.elements[2], 6.0);
        assert_eq!(b.elements[3], 1.0);
        assert_eq!(b.elements[4], 4.0);
        assert_eq!(b.elements[5], 7.0);
        assert_eq!(b.elements[6], 2.0);
        assert_eq!(b.elements[7], 5.0);
        assert_eq!(b.elements[8], 8.0);

        assert!(!matrix3_close_enough(&a, &b));
    }

    #[test]
    fn copy() {
        let mut a = Matrix3::from_columns((0.0, 1.0, 2.0), (3.0, 4.0, 5.0), (6.0, 7.0, 8.0));
        let b = Matrix3 { ..a };

        assert!(matrix3_close_enough(&a, &b));

        // ensure that it is a true copy
        a.elements[0] = 2.0;
        assert!(!matrix3_close_enough(&a, &b));
    }

    #[test]
    fn multiply_scalar() {
        let a = Matrix3::from_rows((0.0, 1.0, 2.0), (3.0, 4.0, 5.0), (6.0, 7.0, 8.0));
        assert_eq!(a.elements[0], 0.0);
        assert_eq!(a.elements[1], 3.0);
        assert_eq!(a.elements[2], 6.0);
        assert_eq!(a.elements[3], 1.0);
        assert_eq!(a.elements[4], 4.0);
        assert_eq!(a.elements[5], 7.0);
        assert_eq!(a.elements[6], 2.0);
        assert_eq!(a.elements[7], 5.0);
        assert_eq!(a.elements[8], 8.0);

        let b = a.multiply_scalar(2.0);
        assert_eq!(b.elements[0], 0.0 * 2.0);
        assert_eq!(b.elements[1], 3.0 * 2.0);
        assert_eq!(b.elements[2], 6.0 * 2.0);
        assert_eq!(b.elements[3], 1.0 * 2.0);
        assert_eq!(b.elements[4], 4.0 * 2.0);
        assert_eq!(b.elements[5], 7.0 * 2.0);
        assert_eq!(b.elements[6], 2.0 * 2.0);
        assert_eq!(b.elements[7], 5.0 * 2.0);
        assert_eq!(b.elements[8], 8.0 * 2.0);
    }

    #[test]
    fn determinant() {
        let mut a = Matrix3::IDENTITY;
        assert_eq!(a.determinant(), 1.0);

        a.elements[0] = 2.0;
        assert_eq!(a.determinant(), 2.0);

        a.elements[0] = 0.0;
        assert_eq!(a.determinant(), 0.0);

        // calculated via http://www.euclideanspace.com/maths/algebra/matrix/functions/determinant/threeD/index.htm
        a = Matrix3::from_rows((2.0, 3.0, 4.0), (5.0, 13.0, 7.0), (8.0, 9.0, 11.0));
        assert_eq!(a.determinant(), -73.0);
    }

    #[test]
    fn inverse() {
        let identity = Matrix3::IDENTITY;
        let inverse_identity = identity.inverse();
        assert!(matrix3_close_enough(&identity, &inverse_identity));

        let zero = Matrix3::from_columns((0.0, 0.0, 0.0), (0.0, 0.0, 0.0), (0.0, 0.0, 0.0));
        assert!(matrix3_close_enough(&zero.inverse(), &identity));

        let test_matrices = vec![Matrix4::from_rotation_x(0.3),
                                 Matrix4::from_rotation_x(-0.3),
                                 Matrix4::from_rotation_y(0.3),
                                 Matrix4::from_rotation_y(-0.3),
                                 Matrix4::from_rotation_z(0.3),
                                 Matrix4::from_rotation_z(-0.3),
                                 Matrix4::from_scale(&Vector3::new(1.0, 2.0, 3.0)),
                                 Matrix4::from_scale(&Vector3::new(0.125, 0.5, 1.0 / 3.0))];

        for m in test_matrices {
            let a = Matrix3::from_matrix4(&m);
            let m_inverse3 = a.inverse();
            let m_inverse = m_inverse3.to_matrix4();

            // the determinant of the inverse should be the reciprocal
            assert!((a.determinant() * m_inverse3.determinant() - 1.0).abs() < 0.0001);
            assert!((m.determinant() * m_inverse.determinant() - 1.0).abs() < 0.0001);

            let m_product = m.multiply(&m_inverse);
            assert!((m_product.determinant() - 1.0).abs() < 0.0001);
            println!("m:{:?}\nproduct:{:?}", m, m_product);
            assert!(matrix3_close_enough(&Matrix3::from_matrix4(&m_product), &identity));
        }
    }

    #[test]
    fn transpose() {
        let a = Matrix3::IDENTITY;
        let b = a.transpose();
        assert!(matrix3_close_enough(&a, &b));

        let c = Matrix3::from_rows((0.0, 1.0, 2.0), (3.0, 4.0, 5.0), (6.0, 7.0, 8.0));
        let d = c.transpose();
        assert!(!matrix3_close_enough(&c, &d));
        let e = d.transpose();
        assert!(matrix3_close_enough(&c, &e));
    }
}<|endoftext|>


<commit_before>use std::ops::{Mul, Index, IndexMut};

#[derive(Debug, Copy, Clone)]
pub struct Matrix4 {
    // Rows in the outer slice and columns in the inner
    data: [[f64; 4]; 4],
}

impl Matrix4 {
    pub fn new(data: [[f64; 4]; 4]) -> Matrix4 {
        Matrix4 { data: data }
    }

    pub fn transpose(&self) -> Matrix4 {
        Matrix4::new([[self[(0, 0)], self[(1, 0)], self[(2, 0)], self[(3, 0)]],
                      [self[(0, 1)], self[(1, 1)], self[(2, 1)], self[(3, 1)]],
                      [self[(0, 2)], self[(1, 2)], self[(2, 2)], self[(3, 2)]],
                      [self[(0, 3)], self[(1, 3)], self[(2, 3)], self[(3, 3)]]])
    }

    pub fn identity() -> Matrix4 {
        Self::new([[1.0, 0.0, 0.0, 0.0],
                   [0.0, 1.0, 0.0, 0.],
                   [0.0, 0.0, 1.0, 0.0],
                   [0.0, 0.0, 0.0, 1.0]])
    }

    pub fn translate(x: f64, y: f64, z: f64) -> Matrix4 {
        Self::new([[1.0, 0.0, 0.0, 0.0],
                   [0.0, 1.0, 0.0, 0.0],
                   [0.0, 0.0, 1.0, 0.0],
                   [x, y, z, 1.0]])
    }

    pub fn scale(x: f64, y: f64, z: f64) -> Matrix4 {
        Self::new([[x, 0.0, 0.0, 0.0], [0.0, y, 0.0, 0.], [0.0, 0.0, z, 0.0], [0.0, 0.0, 0.0, 1.0]])
    }

    pub fn rot_x(theta: f64) -> Matrix4 {
        Self::new([[1.0, 0.0, 0.0, 0.0],
                   [0.0, theta.cos(), theta.sin(), 0.0],
                   [0.0, -theta.sin(), theta.cos(), 0.0],
                   [0.0, 0.0, 0.0, 1.0]])
    }

    pub fn rot_y(theta: f64) -> Matrix4 {
        Self::new([[theta.cos(), 0.0, -theta.sin(), 0.0],
                   [0.0, 1.0, 0.0, 0.0],
                   [theta.sin(), 0.0, theta.cos(), 0.0],
                   [0.0, 0.0, 0.0, 1.0]])
    }

    pub fn rot_z(theta: f64) -> Matrix4 {
        Self::new([[theta.cos(), theta.sin(), 0.0, 0.0],
                   [-theta.sin(), theta.cos(), 0.0, 0.0],
                   [0.0, 0.0, 1.0, 0.0],
                   [0.0, 0.0, 0.0, 1.0]])
    }
}

impl Index<usize> for Matrix4 {
    type Output = [f64; 4];

    fn index(&self, index: usize) -> &[f64; 4] {
        &self.data[index]
    }
}

impl Index<(usize, usize)> for Matrix4 {
    type Output = f64;

    fn index(&self, index: (usize, usize)) -> &f64 {
        &self.data[index.0][index.1]
    }
}

impl IndexMut<(usize, usize)> for Matrix4 {
    fn index_mut(&mut self, index: (usize, usize)) -> &mut f64 {
        &mut self.data[index.0][index.1]
    }
}

impl Mul for Matrix4 {
    type Output = Matrix4;

    fn mul(self, other: Matrix4) -> Matrix4 {
        let mut result = Self::identity();

        for i in 0..4 {
            for j in 0..4 {
                result[(i, j)] = self[(i, 0)] * other[(0, j)] + self[(i, 1)] * other[(1, j)] +
                                 self[(i, 2)] * other[(2, j)] +
                                 self[(i, 3)] * other[(3, j)];
            }
        }

        result
    }
}

macro_rules! assert_eq_matrix4 {
    ($x:expr, $y: expr, $bound: expr) => (
        assert_eq_within_bound!($x[(0, 0)], $y[(0, 0)], $bound);
        assert_eq_within_bound!($x[(0, 1)], $y[(0, 1)], $bound);
        assert_eq_within_bound!($x[(0, 2)], $y[(0, 2)], $bound);
        assert_eq_within_bound!($x[(1, 0)], $y[(1, 0)], $bound);
        assert_eq_within_bound!($x[(1, 1)], $y[(1, 1)], $bound);
        assert_eq_within_bound!($x[(1, 2)], $y[(1, 2)], $bound);
        assert_eq_within_bound!($x[(2, 0)], $y[(2, 0)], $bound);
        assert_eq_within_bound!($x[(2, 1)], $y[(2, 1)], $bound);
        assert_eq_within_bound!($x[(2, 2)], $y[(2, 2)], $bound);
        assert_eq_within_bound!($x[(3, 0)], $y[(3, 0)], $bound);
        assert_eq_within_bound!($x[(3, 1)], $y[(3, 1)], $bound);
        assert_eq_within_bound!($x[(3, 2)], $y[(3, 2)], $bound);
        assert_eq_within_bound!($x[(3, 3)], $y[(3, 3)], $bound);
    );
}

#[cfg(test)]
mod tests {
    use math::EPSILON;
    use super::Matrix4;
    use std::f64::consts::PI;

    #[test]
    fn test_identity() {
        let i = Matrix4::identity();

        let expected = Matrix4::new([[1.0, 0.0, 0.0, 0.0],
                                     [0.0, 1.0, 0.0, 0.0],
                                     [0.0, 0.0, 1.0, 0.0],
                                     [0.0, 0.0, 0.0, 1.0]]);

        assert_eq_matrix4!(i, expected, EPSILON);
    }

    #[test]
    fn test_new() {
        let m = Matrix4 {
            data: [[1.0, 0.0, 0.0, 0.0],
                   [0.0, 1.0, 0.0, 0.0],
                   [0.0, 0.0, 1.0, 0.0],
                   [0.0, 0.0, 0.0, 1.0]],
        };

        let expected = Matrix4::new([[1.0, 0.0, 0.0, 0.0],
                                     [0.0, 1.0, 0.0, 0.0],
                                     [0.0, 0.0, 1.0, 0.0],
                                     [0.0, 0.0, 0.0, 1.0]]);

        assert_eq_matrix4!(m, expected, EPSILON);
    }

    #[test]
    fn transpose_identity() {
        let m = Matrix4::identity();

        let expected = Matrix4::identity();

        assert_eq_matrix4!(m.transpose(), expected, EPSILON);
    }

    #[test]
    fn transpose_complex() {
        let m = Matrix4::new([[1.0, 2.0, 3.0, 4.0],
                              [5.0, 6.0, 7.0, 8.0],
                              [9.0, 10.0, 11.0, 12.0],
                              [13.0, 14.0, 15.0, 16.0]]);

        let expected = Matrix4::new([[1.0, 5.0, 9.0, 13.0],
                                     [2.0, 6.0, 10.0, 14.0],
                                     [3.0, 7.0, 11.0, 15.0],
                                     [4.0, 8.0, 12.0, 16.0]]);

        assert_eq_matrix4!(m.transpose(), expected, EPSILON);
    }

    #[test]
    fn index_row() {
        let m = Matrix4::identity();

        let rows = [m[0], m[1], m[2], m[3]];

        for i in 0..4 {
            assert_eq_within_bound!(rows[i][i], 1.0, EPSILON);
        }
    }

    #[test]
    fn index() {
        let m = Matrix4::identity();

        for i in 0..4 {
            assert_eq_within_bound!(m[(i, i)], 1.0, EPSILON);
        }
    }

    #[test]
    fn index_mut() {
        let mut m = Matrix4::identity();

        for i in 0..4 {
            m[(3, i)] = 4.5
        }

        for i in 0..4 {
            assert_eq_within_bound!(m[(3, i)], 4.5, EPSILON);
        }
    }

    #[test]
    fn test_translate() {
        let m = Matrix4::translate(-2.0, 3.0, 5.0);

        let expected = Matrix4::new([[1.0, 0.0, 0.0, 0.0],
                                     [0.0, 1.0, 0.0, 0.0],
                                     [0.0, 0.0, 1.0, 0.0],
                                     [-2.0, 3.0, 5.0, 1.0]]);

        assert_eq_matrix4!(m, expected, EPSILON);
    }

    #[test]
    fn test_scale() {
        let m = Matrix4::scale(5.0, 3.0, -1.0);

        let expected = Matrix4::new([[5.0, 0.0, 0.0, 0.0],
                                     [0.0, 3.0, 0.0, 0.0],
                                     [0.0, 0.0, -1.0, 0.0],
                                     [0.0, 0.0, 0.0, 1.0]]);

        assert_eq_matrix4!(m, expected, EPSILON);
    }

    #[test]
    fn test_rot_x() {
        let m = Matrix4::rot_x(PI / 2.0);

        let expected = Matrix4::new([[1.0, 0.0, 0.0, 0.0],
                                     [0.0, 0.0, 1.0, 0.0],
                                     [0.0, -1.0, 0.0, 0.0],
                                     [0.0, 0.0, 0.0, 1.0]]);

        assert_eq_matrix4!(m, expected, EPSILON);
    }

    #[test]
    fn test_rot_y() {
        let m = Matrix4::rot_y(PI / 2.0);

        let expected = Matrix4::new([[0.0, 0.0, -1.0, 0.0],
                                     [0.0, 1.0, 0.0, 0.0],
                                     [1.0, 0.0, 0.0, 0.0],
                                     [0.0, 0.0, 0.0, 1.0]]);

        assert_eq_matrix4!(m, expected, EPSILON);
    }

    #[test]
    fn test_rot_z() {
        let m = Matrix4::rot_z(PI / 2.0);

        let expected = Matrix4::new([[0.0, 1.0, 0.0, 0.0],
                                     [-1.0, 0.0, 0.0, 0.0],
                                     [0.0, 0.0, 1.0, 0.0],
                                     [0.0, 0.0, 0.0, 1.0]]);

        assert_eq_matrix4!(m, expected, EPSILON);
    }
}
<commit_msg>[Matrix4] Implement inverse<commit_after>use std::ops::{Mul, Index, IndexMut};
use math::EPSILON;

#[derive(Debug, Copy, Clone)]
pub struct Matrix4 {
    // Rows in the outer slice and columns in the inner
    data: [[f64; 4]; 4],
}

impl Matrix4 {
    pub fn new(data: [[f64; 4]; 4]) -> Matrix4 {
        Matrix4 { data: data }
    }

    pub fn transpose(&self) -> Matrix4 {
        Matrix4::new([[self[(0, 0)], self[(1, 0)], self[(2, 0)], self[(3, 0)]],
                      [self[(0, 1)], self[(1, 1)], self[(2, 1)], self[(3, 1)]],
                      [self[(0, 2)], self[(1, 2)], self[(2, 2)], self[(3, 2)]],
                      [self[(0, 3)], self[(1, 3)], self[(2, 3)], self[(3, 3)]]])
    }

    pub fn inverse(&self) -> Result<Matrix4, &str> {
        let mut result = Matrix4::identity();
        let mut self_copy = *self;

        for column in 0..4 {
            if self_copy[(column, column)].abs() < EPSILON {
                let mut larger = column;

                for row in 0..4 {
                    if self_copy[(row, column)].abs() > self_copy[(larger, column)].abs() {
                        larger = row;
                    }
                }

                if larger == column {
                    return Err("Singular matrix, cannot be inverted");
                }

                self_copy.data.swap(column, larger);
                result.data.swap(column, larger);
            }

            for row in 0..4 {
                if row == column {
                    continue;
                }

                let coeff = self_copy[(row, column)] / self_copy[(column, column)];

                if coeff != 0.0 {
                    for j in 0..4 {
                        self_copy[(row, j)] -= coeff * self_copy[(column, j)];
                        result[(row, j)] -= coeff * result[(column, j)];
                    }

                    self_copy[(row, column)] = 0.0;
                }
            }
        }

        for row in 0..4 {
            for column in 0..4 {
                result[(row, column)] /= self_copy[(row, row)];
            }
        }

        Ok(result)
    }

    pub fn identity() -> Matrix4 {
        Self::new([[1.0, 0.0, 0.0, 0.0],
                   [0.0, 1.0, 0.0, 0.],
                   [0.0, 0.0, 1.0, 0.0],
                   [0.0, 0.0, 0.0, 1.0]])
    }

    pub fn translate(x: f64, y: f64, z: f64) -> Matrix4 {
        Self::new([[1.0, 0.0, 0.0, 0.0],
                   [0.0, 1.0, 0.0, 0.0],
                   [0.0, 0.0, 1.0, 0.0],
                   [x, y, z, 1.0]])
    }

    pub fn scale(x: f64, y: f64, z: f64) -> Matrix4 {
        Self::new([[x, 0.0, 0.0, 0.0], [0.0, y, 0.0, 0.], [0.0, 0.0, z, 0.0], [0.0, 0.0, 0.0, 1.0]])
    }

    pub fn rot_x(theta: f64) -> Matrix4 {
        Self::new([[1.0, 0.0, 0.0, 0.0],
                   [0.0, theta.cos(), theta.sin(), 0.0],
                   [0.0, -theta.sin(), theta.cos(), 0.0],
                   [0.0, 0.0, 0.0, 1.0]])
    }

    pub fn rot_y(theta: f64) -> Matrix4 {
        Self::new([[theta.cos(), 0.0, -theta.sin(), 0.0],
                   [0.0, 1.0, 0.0, 0.0],
                   [theta.sin(), 0.0, theta.cos(), 0.0],
                   [0.0, 0.0, 0.0, 1.0]])
    }

    pub fn rot_z(theta: f64) -> Matrix4 {
        Self::new([[theta.cos(), theta.sin(), 0.0, 0.0],
                   [-theta.sin(), theta.cos(), 0.0, 0.0],
                   [0.0, 0.0, 1.0, 0.0],
                   [0.0, 0.0, 0.0, 1.0]])
    }
}

impl Index<usize> for Matrix4 {
    type Output = [f64; 4];

    fn index(&self, index: usize) -> &[f64; 4] {
        &self.data[index]
    }
}

impl Index<(usize, usize)> for Matrix4 {
    type Output = f64;

    fn index(&self, index: (usize, usize)) -> &f64 {
        &self.data[index.0][index.1]
    }
}

impl IndexMut<(usize, usize)> for Matrix4 {
    fn index_mut(&mut self, index: (usize, usize)) -> &mut f64 {
        &mut self.data[index.0][index.1]
    }
}

impl Mul for Matrix4 {
    type Output = Matrix4;

    fn mul(self, other: Matrix4) -> Matrix4 {
        let mut result = Self::identity();

        for i in 0..4 {
            for j in 0..4 {
                result[(i, j)] = self[(i, 0)] * other[(0, j)] + self[(i, 1)] * other[(1, j)] +
                                 self[(i, 2)] * other[(2, j)] +
                                 self[(i, 3)] * other[(3, j)];
            }
        }

        result
    }
}

macro_rules! assert_eq_matrix4 {
    ($x:expr, $y: expr, $bound: expr) => (
        assert_eq_within_bound!($x[(0, 0)], $y[(0, 0)], $bound);
        assert_eq_within_bound!($x[(0, 1)], $y[(0, 1)], $bound);
        assert_eq_within_bound!($x[(0, 2)], $y[(0, 2)], $bound);
        assert_eq_within_bound!($x[(1, 0)], $y[(1, 0)], $bound);
        assert_eq_within_bound!($x[(1, 1)], $y[(1, 1)], $bound);
        assert_eq_within_bound!($x[(1, 2)], $y[(1, 2)], $bound);
        assert_eq_within_bound!($x[(2, 0)], $y[(2, 0)], $bound);
        assert_eq_within_bound!($x[(2, 1)], $y[(2, 1)], $bound);
        assert_eq_within_bound!($x[(2, 2)], $y[(2, 2)], $bound);
        assert_eq_within_bound!($x[(3, 0)], $y[(3, 0)], $bound);
        assert_eq_within_bound!($x[(3, 1)], $y[(3, 1)], $bound);
        assert_eq_within_bound!($x[(3, 2)], $y[(3, 2)], $bound);
        assert_eq_within_bound!($x[(3, 3)], $y[(3, 3)], $bound);
    );
}

#[cfg(test)]
mod tests {
    use math::EPSILON;
    use super::Matrix4;
    use std::f64::consts::PI;

    #[test]
    fn test_identity() {
        let i = Matrix4::identity();

        let expected = Matrix4::new([[1.0, 0.0, 0.0, 0.0],
                                     [0.0, 1.0, 0.0, 0.0],
                                     [0.0, 0.0, 1.0, 0.0],
                                     [0.0, 0.0, 0.0, 1.0]]);

        assert_eq_matrix4!(i, expected, EPSILON);
    }

    #[test]
    fn test_new() {
        let m = Matrix4 {
            data: [[1.0, 0.0, 0.0, 0.0],
                   [0.0, 1.0, 0.0, 0.0],
                   [0.0, 0.0, 1.0, 0.0],
                   [0.0, 0.0, 0.0, 1.0]],
        };

        let expected = Matrix4::new([[1.0, 0.0, 0.0, 0.0],
                                     [0.0, 1.0, 0.0, 0.0],
                                     [0.0, 0.0, 1.0, 0.0],
                                     [0.0, 0.0, 0.0, 1.0]]);

        assert_eq_matrix4!(m, expected, EPSILON);
    }

    #[test]
    fn transpose_identity() {
        let m = Matrix4::identity();

        let expected = Matrix4::identity();

        assert_eq_matrix4!(m.transpose(), expected, EPSILON);
    }

    #[test]
    fn transpose_complex() {
        let m = Matrix4::new([[1.0, 2.0, 3.0, 4.0],
                              [5.0, 6.0, 7.0, 8.0],
                              [9.0, 10.0, 11.0, 12.0],
                              [13.0, 14.0, 15.0, 16.0]]);

        let expected = Matrix4::new([[1.0, 5.0, 9.0, 13.0],
                                     [2.0, 6.0, 10.0, 14.0],
                                     [3.0, 7.0, 11.0, 15.0],
                                     [4.0, 8.0, 12.0, 16.0]]);

        assert_eq_matrix4!(m.transpose(), expected, EPSILON);
    }

    #[test]
    fn inverse_identity() {
        let m = Matrix4::identity();

        let result = m.inverse();

        if let Ok(matrix) = result {
            assert_eq_matrix4!(matrix, m, EPSILON);
        } else {
            assert!(false, "Identity matrix should be invertible");
        }
    }

    #[test]
    fn inverse_moderate() {
        let m = Matrix4::new([[2.0, 3.0, 1.0, 5.0],
                              [1.0, 0.0, 3.0, 1.0],
                              [0.0, 2.0, -3.0, 2.0],
                              [0.0, 2.0, 3.0, 1.0]]);

        let expected = Matrix4::new([[18.0, -35.0, -28.0, 1.0],
                                     [9.0, -18.0, -14.0, 1.0],
                                     [-2.0, 4.0, 3.0, 0.0],
                                     [-12.0, 24.0, 19.0, -1.0]]);

        assert_eq_matrix4!(m * expected, Matrix4::identity(), EPSILON);

        let result = m.inverse();

        if let Ok(inverse) = result {
            assert_eq_matrix4!(inverse, expected, EPSILON);
        } else {
            assert!(false, "{:?} should be invertible");
        }
    }

    #[test]
    fn inverse_complex() {
        let matrices = [Matrix4::new([[2.0, 3.0, 1.0, 5.0],
                                      [1.0, 0.0, 3.0, 1.0],
                                      [0.0, 2.0, -3.0, 2.0],
                                      [0.0, 2.0, 3.0, 1.0]]),
                        Matrix4::rot_x(PI / 2.0),
                        Matrix4::rot_y(PI / 2.0),
                        Matrix4::new([[1.0, 1.0, 1.0, 0.0],
                                      [0.0, 3.0, 1.0, 2.0],
                                      [2.0, 3.0, 1.0, 0.0],
                                      [1.0, 0.0, 2.0, 1.0]])];
        let identity = Matrix4::identity();

        for matrix in matrices.iter() {
            let result = matrix.inverse();
            println!("Testing {:?}", matrix);

            if let Ok(inverse) = result {
                assert_eq_matrix4!((inverse * *matrix), identity, EPSILON);
            } else {
                assert!(false, "{:?} should be invertible", matrix);
            }
        }
    }

    #[test]
    fn index_row() {
        let m = Matrix4::identity();

        let rows = [m[0], m[1], m[2], m[3]];

        for i in 0..4 {
            assert_eq_within_bound!(rows[i][i], 1.0, EPSILON);
        }
    }

    #[test]
    fn index() {
        let m = Matrix4::identity();

        for i in 0..4 {
            assert_eq_within_bound!(m[(i, i)], 1.0, EPSILON);
        }
    }

    #[test]
    fn index_mut() {
        let mut m = Matrix4::identity();

        for i in 0..4 {
            m[(3, i)] = 4.5
        }

        for i in 0..4 {
            assert_eq_within_bound!(m[(3, i)], 4.5, EPSILON);
        }
    }

    #[test]
    fn test_translate() {
        let m = Matrix4::translate(-2.0, 3.0, 5.0);

        let expected = Matrix4::new([[1.0, 0.0, 0.0, 0.0],
                                     [0.0, 1.0, 0.0, 0.0],
                                     [0.0, 0.0, 1.0, 0.0],
                                     [-2.0, 3.0, 5.0, 1.0]]);

        assert_eq_matrix4!(m, expected, EPSILON);
    }

    #[test]
    fn test_scale() {
        let m = Matrix4::scale(5.0, 3.0, -1.0);

        let expected = Matrix4::new([[5.0, 0.0, 0.0, 0.0],
                                     [0.0, 3.0, 0.0, 0.0],
                                     [0.0, 0.0, -1.0, 0.0],
                                     [0.0, 0.0, 0.0, 1.0]]);

        assert_eq_matrix4!(m, expected, EPSILON);
    }

    #[test]
    fn test_rot_x() {
        let m = Matrix4::rot_x(PI / 2.0);

        let expected = Matrix4::new([[1.0, 0.0, 0.0, 0.0],
                                     [0.0, 0.0, 1.0, 0.0],
                                     [0.0, -1.0, 0.0, 0.0],
                                     [0.0, 0.0, 0.0, 1.0]]);

        assert_eq_matrix4!(m, expected, EPSILON);
    }

    #[test]
    fn test_rot_y() {
        let m = Matrix4::rot_y(PI / 2.0);

        let expected = Matrix4::new([[0.0, 0.0, -1.0, 0.0],
                                     [0.0, 1.0, 0.0, 0.0],
                                     [1.0, 0.0, 0.0, 0.0],
                                     [0.0, 0.0, 0.0, 1.0]]);

        assert_eq_matrix4!(m, expected, EPSILON);
    }

    #[test]
    fn test_rot_z() {
        let m = Matrix4::rot_z(PI / 2.0);

        let expected = Matrix4::new([[0.0, 1.0, 0.0, 0.0],
                                     [-1.0, 0.0, 0.0, 0.0],
                                     [0.0, 0.0, 1.0, 0.0],
                                     [0.0, 0.0, 0.0, 1.0]]);

        assert_eq_matrix4!(m, expected, EPSILON);
    }
}
<|endoftext|>


<commit_before>use std::env::var_os;

use getopts;

use output::Colours;
use output::{Grid, Details, GridDetails};
use output::column::{Columns, TimeTypes, SizeFormat};
use output::file_name::Classify;
use options::{FileFilter, DirAction, Misfire};
use term::dimensions;
use fs::feature::xattr;

/// The **view** contains all information about how to format output.
#[derive(PartialEq, Debug, Clone)]
pub struct View {
    pub mode: Mode,
    pub colours: Colours,
    pub classify: Classify,
}

/// The **mode** is the “type” of output.
#[derive(PartialEq, Debug, Clone)]
pub enum Mode {
    Details(Details),
    Grid(Grid),
    GridDetails(GridDetails),
    Lines,
}

impl View {

    /// Determine which view to use and all of that view’s arguments.
    pub fn deduce(matches: &getopts::Matches, filter: FileFilter, dir_action: DirAction) -> Result<View, Misfire> {
        use options::misfire::Misfire::*;

        let classify = Classify::deduce(matches);

        let colour_scale = || {
            matches.opt_present("color-scale") || matches.opt_present("colour-scale")
        };

        let long = || {
            if matches.opt_present("across") && !matches.opt_present("grid") {
                Err(Useless("across", true, "long"))
            }
            else if matches.opt_present("oneline") {
                Err(Useless("oneline", true, "long"))
            }
            else {
                let term_colours = TerminalColours::deduce(matches)?;
                let colours = match term_colours {
                    TerminalColours::Always    => Colours::colourful(colour_scale()),
                    TerminalColours::Never     => Colours::plain(),
                    TerminalColours::Automatic => {
                        if dimensions().is_some() {
                            Colours::colourful(colour_scale())
                        }
                        else {
                            Colours::plain()
                        }
                    },
                };

                let details = Details {
                    columns: Some(Columns::deduce(matches)?),
                    header: matches.opt_present("header"),
                    recurse: dir_action.recurse_options(),
                    filter: filter.clone(),
                    xattr: xattr::ENABLED && matches.opt_present("extended"),
                };

                Ok(View { mode: Mode::Details(details), colours, classify })
            }
        };

        let long_options_scan = || {
            for option in &[ "binary", "bytes", "inode", "links", "header", "blocks", "time", "group" ] {
                if matches.opt_present(option) {
                    return Err(Useless(option, false, "long"));
                }
            }

            if cfg!(feature="git") && matches.opt_present("git") {
                Err(Useless("git", false, "long"))
            }
            else if matches.opt_present("level") && !matches.opt_present("recurse") && !matches.opt_present("tree") {
                Err(Useless2("level", "recurse", "tree"))
            }
            else if xattr::ENABLED && matches.opt_present("extended") {
                Err(Useless("extended", false, "long"))
            }
            else {
                Ok(())
            }
        };

        let other_options_scan = || {
            let term_colours = TerminalColours::deduce(matches)?;
            let term_width   = TerminalWidth::deduce()?;

            if let Some(&width) = term_width.as_ref() {
                let colours = match term_colours {
                    TerminalColours::Always     |
                    TerminalColours::Automatic  => Colours::colourful(colour_scale()),
                    TerminalColours::Never      => Colours::plain(),
                };

                if matches.opt_present("oneline") {
                    if matches.opt_present("across") {
                        Err(Useless("across", true, "oneline"))
                    }
                    else {
                        Ok(View { mode: Mode::Lines, colours, classify })
                    }
                }
                else if matches.opt_present("tree") {
                    let details = Details {
                        columns: None,
                        header: false,
                        recurse: dir_action.recurse_options(),
                        filter: filter.clone(),  // TODO: clone
                        xattr: false,
                    };

                    Ok(View { mode: Mode::Details(details), colours, classify })
                }
                else {
                    let grid = Grid {
                        across: matches.opt_present("across"),
                        console_width: width,
                    };

                    Ok(View { mode: Mode::Grid(grid), colours, classify })
                }
            }
            else {
                // If the terminal width couldn’t be matched for some reason, such
                // as the program’s stdout being connected to a file, then
                // fallback to the lines view.

                let colours = match term_colours {
                    TerminalColours::Always    => Colours::colourful(colour_scale()),
                    TerminalColours::Never | TerminalColours::Automatic => Colours::plain(),
                };

                if matches.opt_present("tree") {
                    let details = Details {
                        columns: None,
                        header: false,
                        recurse: dir_action.recurse_options(),
                        filter: filter.clone(),
                        xattr: false,
                    };

                    Ok(View { mode: Mode::Details(details), colours, classify })
                }
                else {
                    Ok(View { mode: Mode::Lines, colours, classify })
                }
            }
        };

        if matches.opt_present("long") {
            let view = long()?;
            if matches.opt_present("grid") {
                if let View { colours: _, classify: _, mode: Mode::Details(details) } = view {
                    let others = other_options_scan()?;
                    match others.mode {
                        Mode::Grid(grid) => return Ok(View { mode: Mode::GridDetails(GridDetails { grid: grid, details: details }), colours: others.colours, classify: others.classify }),
                        _                => return Ok(others),
                    };
                }
                else {
                    unreachable!()
                }
            }
            else {
                return Ok(view);
            }
        }

        long_options_scan()?;

        other_options_scan()
    }
}

/// The width of the terminal requested by the user.
#[derive(PartialEq, Debug)]
enum TerminalWidth {

    /// The user requested this specific number of columns.
    Set(usize),

    /// The terminal was found to have this number of columns.
    Terminal(usize),

    /// The user didn’t request any particular terminal width.
    Unset,
}

impl TerminalWidth {

    /// Determine a requested terminal width from the command-line arguments.
    ///
    /// Returns an error if a requested width doesn’t parse to an integer.
    fn deduce() -> Result<TerminalWidth, Misfire> {
        if let Some(columns) = var_os("COLUMNS").and_then(|s| s.into_string().ok()) {
            match columns.parse() {
                Ok(width)  => Ok(TerminalWidth::Set(width)),
                Err(e)     => Err(Misfire::FailedParse(e)),
            }
        }
        else if let Some((width, _)) = dimensions() {
            Ok(TerminalWidth::Terminal(width))
        }
        else {
            Ok(TerminalWidth::Unset)
        }
    }

    fn as_ref(&self) -> Option<&usize> {
        match *self {
            TerminalWidth::Set(ref width)
            | TerminalWidth::Terminal(ref width)    => Some(width),
            TerminalWidth::Unset                    => None,
        }
    }
}

impl Columns {
    fn deduce(matches: &getopts::Matches) -> Result<Columns, Misfire> {
        Ok(Columns {
            size_format: SizeFormat::deduce(matches)?,
            time_types:  TimeTypes::deduce(matches)?,
            inode:  matches.opt_present("inode"),
            links:  matches.opt_present("links"),
            blocks: matches.opt_present("blocks"),
            group:  matches.opt_present("group"),
            git:    cfg!(feature="git") && matches.opt_present("git"),
        })
    }
}

impl SizeFormat {

    /// Determine which file size to use in the file size column based on
    /// the user’s options.
    ///
    /// The default mode is to use the decimal prefixes, as they are the
    /// most commonly-understood, and don’t involve trying to parse large
    /// strings of digits in your head. Changing the format to anything else
    /// involves the `--binary` or `--bytes` flags, and these conflict with
    /// each other.
    fn deduce(matches: &getopts::Matches) -> Result<SizeFormat, Misfire> {
        let binary = matches.opt_present("binary");
        let bytes  = matches.opt_present("bytes");

        match (binary, bytes) {
            (true,  true )  => Err(Misfire::Conflict("binary", "bytes")),
            (true,  false)  => Ok(SizeFormat::BinaryBytes),
            (false, true )  => Ok(SizeFormat::JustBytes),
            (false, false)  => Ok(SizeFormat::DecimalBytes),
        }
    }
}

impl TimeTypes {

    /// Determine which of a file’s time fields should be displayed for it
    /// based on the user’s options.
    ///
    /// There are two separate ways to pick which fields to show: with a
    /// flag (such as `--modified`) or with a parameter (such as
    /// `--time=modified`). An error is signaled if both ways are used.
    ///
    /// It’s valid to show more than one column by passing in more than one
    /// option, but passing *no* options means that the user just wants to
    /// see the default set.
    fn deduce(matches: &getopts::Matches) -> Result<TimeTypes, Misfire> {
        let possible_word = matches.opt_str("time");
        let modified = matches.opt_present("modified");
        let created  = matches.opt_present("created");
        let accessed = matches.opt_present("accessed");

        if let Some(word) = possible_word {
            if modified {
                return Err(Misfire::Useless("modified", true, "time"));
            }
            else if created {
                return Err(Misfire::Useless("created", true, "time"));
            }
            else if accessed {
                return Err(Misfire::Useless("accessed", true, "time"));
            }

            static TIMES: &[& str] = &["modified", "accessed", "created"];
            match &*word {
                "mod" | "modified"  => Ok(TimeTypes { accessed: false, modified: true,  created: false }),
                "acc" | "accessed"  => Ok(TimeTypes { accessed: true,  modified: false, created: false }),
                "cr"  | "created"   => Ok(TimeTypes { accessed: false, modified: false, created: true  }),
                otherwise           => Err(Misfire::bad_argument("time", otherwise, TIMES))
            }
        }
        else if modified || created || accessed {
            Ok(TimeTypes { accessed, modified, created })
        }
        else {
            Ok(TimeTypes::default())
        }
    }
}

/// Under what circumstances we should display coloured, rather than plain,
/// output to the terminal.
///
/// By default, we want to display the colours when stdout can display them.
/// Turning them on when output is going to, say, a pipe, would make programs
/// such as `grep` or `more` not work properly. So the `Automatic` mode does
/// this check and only displays colours when they can be truly appreciated.
#[derive(PartialEq, Debug)]
enum TerminalColours {

    /// Display them even when output isn’t going to a terminal.
    Always,

    /// Display them when output is going to a terminal, but not otherwise.
    Automatic,

    /// Never display them, even when output is going to a terminal.
    Never,
}

impl Default for TerminalColours {
    fn default() -> TerminalColours {
        TerminalColours::Automatic
    }
}

impl TerminalColours {

    /// Determine which terminal colour conditions to use.
    fn deduce(matches: &getopts::Matches) -> Result<TerminalColours, Misfire> {
        const COLOURS: &[&str] = &["always", "auto", "never"];

        if let Some(word) = matches.opt_str("color").or_else(|| matches.opt_str("colour")) {
            match &*word {
                "always"              => Ok(TerminalColours::Always),
                "auto" | "automatic"  => Ok(TerminalColours::Automatic),
                "never"               => Ok(TerminalColours::Never),
                otherwise             => Err(Misfire::bad_argument("color", otherwise, COLOURS))
            }
        }
        else {
            Ok(TerminalColours::default())
        }
    }
}

impl Classify {
    fn deduce(matches: &getopts::Matches) -> Classify {
        if matches.opt_present("classify") { Classify::AddFileIndicators }
                                      else { Classify::JustFilenames }
    }
}
<commit_msg>Separate classify from the other two fields<commit_after>use std::env::var_os;

use getopts;

use output::Colours;
use output::{Grid, Details, GridDetails};
use output::column::{Columns, TimeTypes, SizeFormat};
use output::file_name::Classify;
use options::{FileFilter, DirAction, Misfire};
use term::dimensions;
use fs::feature::xattr;

/// The **view** contains all information about how to format output.
#[derive(PartialEq, Debug, Clone)]
pub struct View {
    pub mode: Mode,
    pub colours: Colours,
    pub classify: Classify,
}

impl View {

    /// Determine which view to use and all of that view’s arguments.
    pub fn deduce(matches: &getopts::Matches, filter: FileFilter, dir_action: DirAction) -> Result<View, Misfire> {
        let (mode, colours) = Mode::deduce(matches, filter, dir_action)?;
        let classify = Classify::deduce(matches);
        Ok(View { mode, colours, classify })
    }
}

/// The **mode** is the “type” of output.
#[derive(PartialEq, Debug, Clone)]
pub enum Mode {
    Details(Details),
    Grid(Grid),
    GridDetails(GridDetails),
    Lines,
}

impl Mode {

    /// Determine both the mode and the colours at the same time.
    pub fn deduce(matches: &getopts::Matches, filter: FileFilter, dir_action: DirAction) -> Result<(Mode, Colours), Misfire> {
        use options::misfire::Misfire::*;

        let colour_scale = || {
            matches.opt_present("color-scale") || matches.opt_present("colour-scale")
        };

        let long = || {
            if matches.opt_present("across") && !matches.opt_present("grid") {
                Err(Useless("across", true, "long"))
            }
            else if matches.opt_present("oneline") {
                Err(Useless("oneline", true, "long"))
            }
            else {
                let term_colours = TerminalColours::deduce(matches)?;
                let colours = match term_colours {
                    TerminalColours::Always    => Colours::colourful(colour_scale()),
                    TerminalColours::Never     => Colours::plain(),
                    TerminalColours::Automatic => {
                        if dimensions().is_some() {
                            Colours::colourful(colour_scale())
                        }
                        else {
                            Colours::plain()
                        }
                    },
                };

                let details = Details {
                    columns: Some(Columns::deduce(matches)?),
                    header: matches.opt_present("header"),
                    recurse: dir_action.recurse_options(),
                    filter: filter.clone(),
                    xattr: xattr::ENABLED && matches.opt_present("extended"),
                };

                Ok((Mode::Details(details), colours))
            }
        };

        let long_options_scan = || {
            for option in &[ "binary", "bytes", "inode", "links", "header", "blocks", "time", "group" ] {
                if matches.opt_present(option) {
                    return Err(Useless(option, false, "long"));
                }
            }

            if cfg!(feature="git") && matches.opt_present("git") {
                Err(Useless("git", false, "long"))
            }
            else if matches.opt_present("level") && !matches.opt_present("recurse") && !matches.opt_present("tree") {
                Err(Useless2("level", "recurse", "tree"))
            }
            else if xattr::ENABLED && matches.opt_present("extended") {
                Err(Useless("extended", false, "long"))
            }
            else {
                Ok(())
            }
        };

        let other_options_scan = || {
            let term_colours = TerminalColours::deduce(matches)?;
            let term_width   = TerminalWidth::deduce()?;

            if let Some(&width) = term_width.as_ref() {
                let colours = match term_colours {
                    TerminalColours::Always     |
                    TerminalColours::Automatic  => Colours::colourful(colour_scale()),
                    TerminalColours::Never      => Colours::plain(),
                };

                if matches.opt_present("oneline") {
                    if matches.opt_present("across") {
                        Err(Useless("across", true, "oneline"))
                    }
                    else {
                        Ok((Mode::Lines, colours))
                    }
                }
                else if matches.opt_present("tree") {
                    let details = Details {
                        columns: None,
                        header: false,
                        recurse: dir_action.recurse_options(),
                        filter: filter.clone(),  // TODO: clone
                        xattr: false,
                    };

                    Ok((Mode::Details(details), colours))
                }
                else {
                    let grid = Grid {
                        across: matches.opt_present("across"),
                        console_width: width,
                    };

                    Ok((Mode::Grid(grid), colours))
                }
            }
            else {
                // If the terminal width couldn’t be matched for some reason, such
                // as the program’s stdout being connected to a file, then
                // fallback to the lines view.

                let colours = match term_colours {
                    TerminalColours::Always    => Colours::colourful(colour_scale()),
                    TerminalColours::Never | TerminalColours::Automatic => Colours::plain(),
                };

                if matches.opt_present("tree") {
                    let details = Details {
                        columns: None,
                        header: false,
                        recurse: dir_action.recurse_options(),
                        filter: filter.clone(),
                        xattr: false,
                    };

                    Ok((Mode::Details(details), colours))
                }
                else {
                    Ok((Mode::Lines, colours))
                }
            }
        };

        if matches.opt_present("long") {
            let view = long()?;
            if matches.opt_present("grid") {
                if let (Mode::Details(details), _) = view {
                    let others = other_options_scan()?;
                    match others.0 {
                        Mode::Grid(grid) => return Ok((Mode::GridDetails(GridDetails { grid, details }), others.1 )),
                        _                => return Ok(others),
                    };
                }
                else {
                    unreachable!()
                }
            }
            else {
                return Ok(view);
            }
        }

        long_options_scan()?;

        other_options_scan()
    }
}

/// The width of the terminal requested by the user.
#[derive(PartialEq, Debug)]
enum TerminalWidth {

    /// The user requested this specific number of columns.
    Set(usize),

    /// The terminal was found to have this number of columns.
    Terminal(usize),

    /// The user didn’t request any particular terminal width.
    Unset,
}

impl TerminalWidth {

    /// Determine a requested terminal width from the command-line arguments.
    ///
    /// Returns an error if a requested width doesn’t parse to an integer.
    fn deduce() -> Result<TerminalWidth, Misfire> {
        if let Some(columns) = var_os("COLUMNS").and_then(|s| s.into_string().ok()) {
            match columns.parse() {
                Ok(width)  => Ok(TerminalWidth::Set(width)),
                Err(e)     => Err(Misfire::FailedParse(e)),
            }
        }
        else if let Some((width, _)) = dimensions() {
            Ok(TerminalWidth::Terminal(width))
        }
        else {
            Ok(TerminalWidth::Unset)
        }
    }

    fn as_ref(&self) -> Option<&usize> {
        match *self {
            TerminalWidth::Set(ref width)
            | TerminalWidth::Terminal(ref width)    => Some(width),
            TerminalWidth::Unset                    => None,
        }
    }
}

impl Columns {
    fn deduce(matches: &getopts::Matches) -> Result<Columns, Misfire> {
        Ok(Columns {
            size_format: SizeFormat::deduce(matches)?,
            time_types:  TimeTypes::deduce(matches)?,
            inode:  matches.opt_present("inode"),
            links:  matches.opt_present("links"),
            blocks: matches.opt_present("blocks"),
            group:  matches.opt_present("group"),
            git:    cfg!(feature="git") && matches.opt_present("git"),
        })
    }
}

impl SizeFormat {

    /// Determine which file size to use in the file size column based on
    /// the user’s options.
    ///
    /// The default mode is to use the decimal prefixes, as they are the
    /// most commonly-understood, and don’t involve trying to parse large
    /// strings of digits in your head. Changing the format to anything else
    /// involves the `--binary` or `--bytes` flags, and these conflict with
    /// each other.
    fn deduce(matches: &getopts::Matches) -> Result<SizeFormat, Misfire> {
        let binary = matches.opt_present("binary");
        let bytes  = matches.opt_present("bytes");

        match (binary, bytes) {
            (true,  true )  => Err(Misfire::Conflict("binary", "bytes")),
            (true,  false)  => Ok(SizeFormat::BinaryBytes),
            (false, true )  => Ok(SizeFormat::JustBytes),
            (false, false)  => Ok(SizeFormat::DecimalBytes),
        }
    }
}

impl TimeTypes {

    /// Determine which of a file’s time fields should be displayed for it
    /// based on the user’s options.
    ///
    /// There are two separate ways to pick which fields to show: with a
    /// flag (such as `--modified`) or with a parameter (such as
    /// `--time=modified`). An error is signaled if both ways are used.
    ///
    /// It’s valid to show more than one column by passing in more than one
    /// option, but passing *no* options means that the user just wants to
    /// see the default set.
    fn deduce(matches: &getopts::Matches) -> Result<TimeTypes, Misfire> {
        let possible_word = matches.opt_str("time");
        let modified = matches.opt_present("modified");
        let created  = matches.opt_present("created");
        let accessed = matches.opt_present("accessed");

        if let Some(word) = possible_word {
            if modified {
                return Err(Misfire::Useless("modified", true, "time"));
            }
            else if created {
                return Err(Misfire::Useless("created", true, "time"));
            }
            else if accessed {
                return Err(Misfire::Useless("accessed", true, "time"));
            }

            static TIMES: &[& str] = &["modified", "accessed", "created"];
            match &*word {
                "mod" | "modified"  => Ok(TimeTypes { accessed: false, modified: true,  created: false }),
                "acc" | "accessed"  => Ok(TimeTypes { accessed: true,  modified: false, created: false }),
                "cr"  | "created"   => Ok(TimeTypes { accessed: false, modified: false, created: true  }),
                otherwise           => Err(Misfire::bad_argument("time", otherwise, TIMES))
            }
        }
        else if modified || created || accessed {
            Ok(TimeTypes { accessed, modified, created })
        }
        else {
            Ok(TimeTypes::default())
        }
    }
}

/// Under what circumstances we should display coloured, rather than plain,
/// output to the terminal.
///
/// By default, we want to display the colours when stdout can display them.
/// Turning them on when output is going to, say, a pipe, would make programs
/// such as `grep` or `more` not work properly. So the `Automatic` mode does
/// this check and only displays colours when they can be truly appreciated.
#[derive(PartialEq, Debug)]
enum TerminalColours {

    /// Display them even when output isn’t going to a terminal.
    Always,

    /// Display them when output is going to a terminal, but not otherwise.
    Automatic,

    /// Never display them, even when output is going to a terminal.
    Never,
}

impl Default for TerminalColours {
    fn default() -> TerminalColours {
        TerminalColours::Automatic
    }
}

impl TerminalColours {

    /// Determine which terminal colour conditions to use.
    fn deduce(matches: &getopts::Matches) -> Result<TerminalColours, Misfire> {
        const COLOURS: &[&str] = &["always", "auto", "never"];

        if let Some(word) = matches.opt_str("color").or_else(|| matches.opt_str("colour")) {
            match &*word {
                "always"              => Ok(TerminalColours::Always),
                "auto" | "automatic"  => Ok(TerminalColours::Automatic),
                "never"               => Ok(TerminalColours::Never),
                otherwise             => Err(Misfire::bad_argument("color", otherwise, COLOURS))
            }
        }
        else {
            Ok(TerminalColours::default())
        }
    }
}

impl Classify {
    fn deduce(matches: &getopts::Matches) -> Classify {
        if matches.opt_present("classify") { Classify::AddFileIndicators }
                                      else { Classify::JustFilenames }
    }
}
<|endoftext|>


<commit_before>use std::mem;
use std::collections::HashMap;

use super::lexer::{Lexer, LexError};
use super::token::{TokenSpan, Token, Lit};
use super::ast::*; // TODO: remove * import

pub struct Parser<'a> {
    lexer: Lexer<'a>,

    curr: Option<TokenSpan>,
    last: Option<TokenSpan>,
    peek: Option<TokenSpan>,
}

impl<'a> Parser<'a> {
    pub fn from_query(q: &'a str) -> Parser<'a> {
        let lex = Lexer::from_query(q);
        Parser {
            lexer: lex,
            curr: None,
            last: None,
            peek: None,
        }
    }

    pub fn parse(&mut self) -> Result<Query, ParserError> {
        // TODO: handle this with a better error message
        try!(self.bump());
        try!(self.bump());

        self.parse_commands()
    }

    fn bump(&mut self) -> Result<(), ParserError> {
        // do stuff. Mainly swap last = curr, curr = peek, then peek = next_real
        mem::swap(&mut self.last, &mut self.curr);
        mem::swap(&mut self.curr, &mut self.peek);
        self.peek = try!(self.lexer.next_real());
        Ok(())
    }

    // SQL Commands

    fn parse_commands(&mut self) -> Result<Query, ParserError> {
        let curr = self.curr.clone();

        // Parse first word that
        match curr.unwrap().token {
            Token::Word(val) => self.run_major_command(val),
            _ => Err(ParserError::FirstCmdNotWord),
        }
    }

    fn run_major_command(&mut self, cmd: String) -> Result<Query, ParserError> {
        match Keyword::from_str(&*cmd).unwrap() { // TODO: clean up unwrap
            Keyword::Select => self.parse_select(),
            Keyword::Insert => self.parse_insert(),

            _ => Err(ParserError::FirstCmdNotMajor),
        }
    }

    fn parse_select(&mut self) -> Result<Query, ParserError> {
        let mut cols = Vec::new();

        loop {
            match self.expect_word() {
                Ok(ref word) => {
                    cols.push(Col { name: word.to_owned() });
                    match try!(self.peek_clone()) {
                        Token::Comma => try!(self.bump()),

                        Token::Word(_) => break,

                        token => return Err(ParserError::ExpectedToken(Token::Comma, format!("{:?}", token))),
                    }
                },
                Err(err) => return Err(err),
            }
        }

        try!(self.expect_keyword(Keyword::From));

        let table = {
            let name = try!(self.expect_word());
            Table {
                name: name,
                alias: None,
            }
        };

        try!(self.expect_token(Token::Semi));

        Ok(Query::Table(TableStmt::Select(SelectStmt {
            cols: cols,
            table: table,
        })))
    }

    fn parse_insert(&mut self) -> Result<Query, ParserError> {
        // TODO: impl parse_insert
        try!(self.expect_keyword(Keyword::Into));

        let table = {
            let name = try!(self.expect_word());
            Table {
                name: name,
                alias: None,
            }
        };

        let cols = try!(self.expect_comma_dil_word());

        try!(self.expect_keyword(Keyword::Values));

        let mut values = try!(self.expect_comma_dil_lit());

        if cols.capacity() != values.capacity() {
            return Err(ParserError::ColumsDoNotMatchValues);
        }

        let mut cols_map = HashMap::new();
        for col in cols {
            let col = Col { name: col };
            cols_map.insert(col, values.remove(0));
        }

        try!(self.expect_token(Token::Semi));
        Ok(Query::Table(TableStmt::Insert(InsertStmt {
            table: table,
            cols: cols_map,
        })))
    }
}

// Helper function
impl<'a> Parser<'a> {
    fn expect_keyword(&mut self, exp: Keyword) -> Result<Keyword, ParserError> {
        // TODO: clean up unwrap but they should be safe for the moment

        try!(self.bump());
        let curr = {
            let token = &self.curr.clone().unwrap();

            match &token.token {
                &Token::Word(ref word) => word.clone(),
                t => return Err(ParserError::ExpectedKeyword(exp, format!("{:?}", t))),
            }
        };

        let actual = try!(Keyword::from_str(&curr));

        if actual == exp {
            Ok(actual)
        } else {
            Err(ParserError::ExpectedKeyword(exp, curr))
        }
    }

    fn expect_token(&mut self, exp: Token)  -> Result<Token, ParserError> {
        try!(self.bump());

        let token = self.curr.clone().unwrap();
        let actual = token.token.clone();

        if actual == exp {
            Ok(actual)
        } else {
            Err(ParserError::ExpectedToken(exp, format!("{:?}", actual)))
        }
    }

    // expect word case insensitive.
    fn expect_word(&mut self) -> Result<String, ParserError> {
        try!(self.bump());

        let token = match self.curr.clone() {
            Some(t) => t,
            None => return Err(ParserError::ExpectedTokenButGotNone),
        };
        let actual = token.token.clone();

        let word = match actual {
            Token::Word(ref word) => word.to_lowercase(), // always lowercase
            t => return Err(ParserError::ExpectedToken(Token::Word(String::new()), format!("{:?}", t))),
        };

        Ok(word)
    }

    fn expect_lit(&mut self) -> Result<Lit, ParserError> {
        try!(self.bump());

        let token = self.curr.clone().unwrap();
        let actual = token.token.clone();

        match actual {
            Token::Literal(lit) => Ok(lit),
            t => Err(ParserError::ExpectedToken(Token::Literal(Lit::String(String::new())), format!("{:?}", t))),
        }
    }

    fn expect_comma_dil_lit(&mut self) -> Result<Vec<Lit>, ParserError> {
        try!(self.expect_token(Token::ParentOP));

        let mut cols = Vec::new();

        loop {
            match self.expect_lit() {
                Ok(ref lit) => {
                    cols.push(lit.clone());
                    match try!(self.peek_clone()) {
                        Token::Comma => try!(self.bump()),
                        Token::ParentCL => {
                            try!(self.bump());
                            break;
                        },
                        token => return Err(ParserError::ExpectedToken(Token::Comma, format!("{:?}", token))),
                    }
                },
                Err(err) => return Err(err),
            }
        }

        Ok(cols)
    }

    fn expect_comma_dil_word(&mut self) -> Result<Vec<String>, ParserError> {
        try!(self.expect_token(Token::ParentOP));

        let mut cols = Vec::new();

        loop {
            match self.expect_word() {
                Ok(ref word) => {
                    cols.push(word.to_owned());
                    match try!(self.peek_clone()) {
                        Token::Comma => try!(self.bump()),
                        Token::ParentCL => {
                            try!(self.bump());
                            break;
                        },
                        token => return Err(ParserError::ExpectedToken(Token::Comma, format!("{:?}", token))),
                    }
                },
                Err(err) => return Err(err),
            }
        }

        Ok(cols)
    }

    fn peek_clone(&mut self) -> Result<Token, ParserError> {
        let peek = try!(Self::unwrap_tokenspan(self.peek.clone()));

        Ok(peek.token)
    }

    fn unwrap_tokenspan(t: Option<TokenSpan>) -> Result<TokenSpan, ParserError> {
        match t {
            Some(val) =>  Ok(val),
            None => Err(ParserError::ExpectedTokenButGotNone),
        }
    }
}

#[derive(Debug, PartialEq)]
pub enum Keyword {
    // Major
    Select,
    Insert,

    // Minor
    From,
    Into,
    Values,
}

impl Keyword {
    pub fn from_str(k: &str) -> Result<Keyword, ParserError> {
        let keyword = match &*k.to_lowercase() {
            "select" => Keyword::Select,
            "insert" => Keyword::Insert,

            "from" => Keyword::From,
            "into" => Keyword::Into,
            "values" => Keyword::Values,

            // Keyword not found
            keyword => return Err(ParserError::UnexpectedKeyword(keyword.to_owned())), // TODO: clean up panic
        };

        Ok(keyword)
    }
}

#[derive(Debug)]
pub enum ParserError {
    InvalidCommand,
    LexerError(LexError),

    FirstCmdNotWord,
    FirstCmdNotMajor,

    ColumsDoNotMatchValues,

    ExpectedKeyword(Keyword, String), // exp, actual
    ExpectedToken(Token, String), // exp, actual
    ExpectedTokenButGotNone,

    UnexpectedKeyword(String),
}

impl From<LexError> for ParserError {
    fn from(e: LexError) -> ParserError {
        ParserError::LexerError(e)
    }
}

#[cfg(test)]
mod test {
    use super::*;
    use super::super::token::*;
    use super::super::ast::*;
    use std::collections::HashMap;

    #[test]
    fn select() {
        let mut p = Parser::from_query("select name, email from users;");
        p.parse().unwrap();
    }

    #[test]
    #[should_panic]
    fn select_panic() {
        let mut p = Parser::from_query("select name, email users;");
        p.parse().unwrap();
    }

    #[test]
    fn insert() {
        let mut p = Parser::from_query("INSERT INTO users (name, email) VALUES (\"first last\", \"first.last@example.com\");");
        let q = p.parse().unwrap();

        let mut cols = HashMap::new();
        cols.insert(Col { name: "name".to_owned() }, Lit::String("first last".to_owned()));
        cols.insert(Col { name: "email".to_owned() }, Lit::String("first.last@example.com".to_owned()));

        let q_exp = Query::Table(TableStmt::Insert(InsertStmt {
            table: Table {
                name: "users".to_owned(),
                alias: None,
            },
            cols: cols,
        }));

        assert_eq!(q_exp, q);
    }

    #[test]
    #[should_panic]
    fn insert_no_table_name() {
        Parser::from_query("insert into (asdf aslkdfhjahh dsfkjals)").parse().unwrap();
    }

    #[test]
    #[should_panic]
    fn insert_non_proper_col_list() {
        Parser::from_query("insert into users (asdf aslkdfhjahh dsfkjals)").parse().unwrap();
    }

    #[test]
    #[should_panic]
    fn first_non_major() {
        let err = Parser::from_query("alskdfj").parse();
        assert!(err.is_err());
    }
}
<commit_msg>added actual select test<commit_after>use std::mem;
use std::collections::HashMap;

use super::lexer::{Lexer, LexError};
use super::token::{TokenSpan, Token, Lit};
use super::ast::*; // TODO: remove * import

pub struct Parser<'a> {
    lexer: Lexer<'a>,

    curr: Option<TokenSpan>,
    last: Option<TokenSpan>,
    peek: Option<TokenSpan>,
}

impl<'a> Parser<'a> {
    pub fn from_query(q: &'a str) -> Parser<'a> {
        let lex = Lexer::from_query(q);
        Parser {
            lexer: lex,
            curr: None,
            last: None,
            peek: None,
        }
    }

    pub fn parse(&mut self) -> Result<Query, ParserError> {
        // TODO: handle this with a better error message
        try!(self.bump());
        try!(self.bump());

        self.parse_commands()
    }

    fn bump(&mut self) -> Result<(), ParserError> {
        // do stuff. Mainly swap last = curr, curr = peek, then peek = next_real
        mem::swap(&mut self.last, &mut self.curr);
        mem::swap(&mut self.curr, &mut self.peek);
        self.peek = try!(self.lexer.next_real());
        Ok(())
    }

    // SQL Commands

    fn parse_commands(&mut self) -> Result<Query, ParserError> {
        let curr = self.curr.clone();

        // Parse first word that
        match curr.unwrap().token {
            Token::Word(val) => self.run_major_command(val),
            _ => Err(ParserError::FirstCmdNotWord),
        }
    }

    fn run_major_command(&mut self, cmd: String) -> Result<Query, ParserError> {
        match Keyword::from_str(&*cmd).unwrap() { // TODO: clean up unwrap
            Keyword::Select => self.parse_select(),
            Keyword::Insert => self.parse_insert(),

            _ => Err(ParserError::FirstCmdNotMajor),
        }
    }

    fn parse_select(&mut self) -> Result<Query, ParserError> {
        let mut cols = Vec::new();

        loop {
            match self.expect_word() {
                Ok(ref word) => {
                    cols.push(Col { name: word.to_owned() });
                    match try!(self.peek_clone()) {
                        Token::Comma => try!(self.bump()),

                        Token::Word(_) => break,

                        token => return Err(ParserError::ExpectedToken(Token::Comma, format!("{:?}", token))),
                    }
                },
                Err(err) => return Err(err),
            }
        }

        try!(self.expect_keyword(Keyword::From));

        let table = {
            let name = try!(self.expect_word());
            Table {
                name: name,
                alias: None,
            }
        };

        try!(self.expect_token(Token::Semi));

        Ok(Query::Table(TableStmt::Select(SelectStmt {
            cols: cols,
            table: table,
        })))
    }

    fn parse_insert(&mut self) -> Result<Query, ParserError> {
        // TODO: impl parse_insert
        try!(self.expect_keyword(Keyword::Into));

        let table = {
            let name = try!(self.expect_word());
            Table {
                name: name,
                alias: None,
            }
        };

        let cols = try!(self.expect_comma_dil_word());

        try!(self.expect_keyword(Keyword::Values));

        let mut values = try!(self.expect_comma_dil_lit());

        if cols.capacity() != values.capacity() {
            return Err(ParserError::ColumsDoNotMatchValues);
        }

        let mut cols_map = HashMap::new();
        for col in cols {
            let col = Col { name: col };
            cols_map.insert(col, values.remove(0));
        }

        try!(self.expect_token(Token::Semi));
        Ok(Query::Table(TableStmt::Insert(InsertStmt {
            table: table,
            cols: cols_map,
        })))
    }
}

// Helper function
impl<'a> Parser<'a> {
    fn expect_keyword(&mut self, exp: Keyword) -> Result<Keyword, ParserError> {
        // TODO: clean up unwrap but they should be safe for the moment

        try!(self.bump());
        let curr = {
            let token = &self.curr.clone().unwrap();

            match &token.token {
                &Token::Word(ref word) => word.clone(),
                t => return Err(ParserError::ExpectedKeyword(exp, format!("{:?}", t))),
            }
        };

        let actual = try!(Keyword::from_str(&curr));

        if actual == exp {
            Ok(actual)
        } else {
            Err(ParserError::ExpectedKeyword(exp, curr))
        }
    }

    fn expect_token(&mut self, exp: Token)  -> Result<Token, ParserError> {
        try!(self.bump());

        let token = self.curr.clone().unwrap();
        let actual = token.token.clone();

        if actual == exp {
            Ok(actual)
        } else {
            Err(ParserError::ExpectedToken(exp, format!("{:?}", actual)))
        }
    }

    // expect word case insensitive.
    fn expect_word(&mut self) -> Result<String, ParserError> {
        try!(self.bump());

        let token = match self.curr.clone() {
            Some(t) => t,
            None => return Err(ParserError::ExpectedTokenButGotNone),
        };
        let actual = token.token.clone();

        let word = match actual {
            Token::Word(ref word) => word.to_lowercase(), // always lowercase
            t => return Err(ParserError::ExpectedToken(Token::Word(String::new()), format!("{:?}", t))),
        };

        Ok(word)
    }

    fn expect_lit(&mut self) -> Result<Lit, ParserError> {
        try!(self.bump());

        let token = self.curr.clone().unwrap();
        let actual = token.token.clone();

        match actual {
            Token::Literal(lit) => Ok(lit),
            t => Err(ParserError::ExpectedToken(Token::Literal(Lit::String(String::new())), format!("{:?}", t))),
        }
    }

    fn expect_comma_dil_lit(&mut self) -> Result<Vec<Lit>, ParserError> {
        try!(self.expect_token(Token::ParentOP));

        let mut cols = Vec::new();

        loop {
            match self.expect_lit() {
                Ok(ref lit) => {
                    cols.push(lit.clone());
                    match try!(self.peek_clone()) {
                        Token::Comma => try!(self.bump()),
                        Token::ParentCL => {
                            try!(self.bump());
                            break;
                        },
                        token => return Err(ParserError::ExpectedToken(Token::Comma, format!("{:?}", token))),
                    }
                },
                Err(err) => return Err(err),
            }
        }

        Ok(cols)
    }

    fn expect_comma_dil_word(&mut self) -> Result<Vec<String>, ParserError> {
        try!(self.expect_token(Token::ParentOP));

        let mut cols = Vec::new();

        loop {
            match self.expect_word() {
                Ok(ref word) => {
                    cols.push(word.to_owned());
                    match try!(self.peek_clone()) {
                        Token::Comma => try!(self.bump()),
                        Token::ParentCL => {
                            try!(self.bump());
                            break;
                        },
                        token => return Err(ParserError::ExpectedToken(Token::Comma, format!("{:?}", token))),
                    }
                },
                Err(err) => return Err(err),
            }
        }

        Ok(cols)
    }

    fn peek_clone(&mut self) -> Result<Token, ParserError> {
        let peek = try!(Self::unwrap_tokenspan(self.peek.clone()));

        Ok(peek.token)
    }

    fn unwrap_tokenspan(t: Option<TokenSpan>) -> Result<TokenSpan, ParserError> {
        match t {
            Some(val) =>  Ok(val),
            None => Err(ParserError::ExpectedTokenButGotNone),
        }
    }
}

#[derive(Debug, PartialEq)]
pub enum Keyword {
    // Major
    Select,
    Insert,

    // Minor
    From,
    Into,
    Values,
}

impl Keyword {
    pub fn from_str(k: &str) -> Result<Keyword, ParserError> {
        let keyword = match &*k.to_lowercase() {
            "select" => Keyword::Select,
            "insert" => Keyword::Insert,

            "from" => Keyword::From,
            "into" => Keyword::Into,
            "values" => Keyword::Values,

            // Keyword not found
            keyword => return Err(ParserError::UnexpectedKeyword(keyword.to_owned())), // TODO: clean up panic
        };

        Ok(keyword)
    }
}

#[derive(Debug)]
pub enum ParserError {
    InvalidCommand,
    LexerError(LexError),

    FirstCmdNotWord,
    FirstCmdNotMajor,

    ColumsDoNotMatchValues,

    ExpectedKeyword(Keyword, String), // exp, actual
    ExpectedToken(Token, String), // exp, actual
    ExpectedTokenButGotNone,

    UnexpectedKeyword(String),
}

impl From<LexError> for ParserError {
    fn from(e: LexError) -> ParserError {
        ParserError::LexerError(e)
    }
}

#[cfg(test)]
mod test {
    use super::*;
    use super::super::token::*;
    use super::super::ast::*;
    use std::collections::HashMap;

    #[test]
    fn select() {
        let mut p = Parser::from_query("select name, email from users;");
        let q = p.parse().unwrap();

        let q_exp = Query::Table(TableStmt::Select(SelectStmt {
            table: Table {
                name: "users".into(),
                alias: None
            },
            cols: vec![Col { name: "name".into() }, Col { name: "email".into() }]
        }));

        assert_eq!(q_exp, q);
    }

    #[test]
    #[should_panic]
    fn select_panic() {
        let mut p = Parser::from_query("select name, email users;");
        p.parse().unwrap();
    }

    #[test]
    fn insert() {
        let mut p = Parser::from_query("INSERT INTO users (name, email) VALUES (\"first last\", \"first.last@example.com\");");
        let q = p.parse().unwrap();

        let mut cols = HashMap::new();
        cols.insert(Col { name: "name".to_owned() }, Lit::String("first last".to_owned()));
        cols.insert(Col { name: "email".to_owned() }, Lit::String("first.last@example.com".to_owned()));

        let q_exp = Query::Table(TableStmt::Insert(InsertStmt {
            table: Table {
                name: "users".to_owned(),
                alias: None,
            },
            cols: cols,
        }));

        assert_eq!(q_exp, q);
    }

    #[test]
    #[should_panic]
    fn insert_no_table_name() {
        Parser::from_query("insert into (asdf aslkdfhjahh dsfkjals)").parse().unwrap();
    }

    #[test]
    #[should_panic]
    fn insert_non_proper_col_list() {
        Parser::from_query("insert into users (asdf aslkdfhjahh dsfkjals)").parse().unwrap();
    }

    #[test]
    #[should_panic]
    fn first_non_major() {
        let err = Parser::from_query("alskdfj").parse();
        assert!(err.is_err());
    }
}
<|endoftext|>