<commit_before>use bvh::*;
use bounding_hierarchy::BHShape;
use aabb::AABB;
use std::collections::HashSet;

// TODO Consider: Instead of getting the scene's shapes passed, let leaf nodes store an AABB
// that is updated from the outside, perhaps by passing not only the indices of the changed
// shapes, but also their new AABBs into optimize().

impl BVH {
    /// Optimizes the `BVH` by batch-reorganizing updated nodes.
    /// Based on https://github.com/jeske/SimpleScene/blob/master/SimpleScene/Util/ssBVH/ssBVH.cs
    ///
    /// Needs all the scene's shapes, plus the indices of the shapes that were updated.
    ///
    pub fn optimize<Shape: BHShape>(&mut self,
                                    refit_shape_indices: &HashSet<usize>,
                                    shapes: &[Shape]) {
        let mut refit_node_indices: HashSet<usize> = refit_shape_indices.iter()
            .map(|x| shapes[*x].bh_node_index())
            .collect();

        // As long as we have refit nodes left, take the list of refit nodes
        // with the highest depth (sweep nodes) and try to rotate them all
        while refit_node_indices.len() > 0 {
            let mut max_depth = 0;
            let mut sweep_node_indices: Vec<usize> = Vec::new();

            // Find max_depth and sweep_node_indices in one iteration
            for refit_node_index in refit_node_indices.iter() {
                let depth = match self.nodes[*refit_node_index] {
                    BVHNode::Node { depth, .. } => depth,
                    BVHNode::Leaf { depth, .. } => depth,
                    BVHNode::Dummy => panic!("Dummy node found during BVH optimization!"),
                };

                if depth > max_depth {
                    max_depth = depth;
                    sweep_node_indices.clear();
                    sweep_node_indices.push(*refit_node_index);
                } else if depth == max_depth {
                    sweep_node_indices.push(*refit_node_index);
                }
            }

            // Try to find a useful tree rotation with all nodes previously found
            for sweep_node_index in sweep_node_indices.iter() {
                // This node does not need to be checked again
                refit_node_indices.remove(&sweep_node_index);

                // TODO There might be multithreading potential here
                let new_refit_node_index = self.try_rotate(*sweep_node_index, shapes);

                // Instead of finding a useful tree rotation, we found another node
                // that we should check, so we add its index to the refit_node_indices.
                if let Some(index) = new_refit_node_index {
                    refit_node_indices.insert(index);
                }
            }
        }
    }

    /// Checks if there is a way to rotate a child and a grandchild (or two grandchildren) of
    /// the given node (specified by `node_index`) that would improve the `BVH`.
    /// If there is, the best rotation found is performed.
    /// Relies on the children and transitive children of the given node having correct AABBs.
    ///
    /// Returns Some(index_of_node) if a new node was found that should be used for optimization.
    ///
    fn try_rotate<Shape: BHShape>(&mut self, node_index: usize, shapes: &[Shape]) -> Option<usize> {
        let mut nodes = &mut self.nodes;

        let node_clone = nodes[node_index].clone();

        // Contains the surface area that would result from applying the currently favored rotation.
        // The rotation with the smallest SA will be applied in the end.
        // The value is calculated by child_l_aabb.surface_area() + child_r_aabb.surface_area()
        let mut best_surface_area = 0f32;

        // TODO Re-implement without mutability
        let mut parent_index: usize = 0;

        #[derive(Debug, Copy, Clone)]
        struct NodeData {
            index: usize,
            aabb: AABB,
        }

        macro_rules! get_children_node_data {
            ($node_index:expr) => ({
                let node = nodes[$node_index].clone();
                match node {
                    BVHNode::Node { child_l, child_r, child_l_aabb, child_r_aabb, .. } => {
                        Some((NodeData{ index: child_l, aabb: child_l_aabb },
                            NodeData{ index: child_r, aabb: child_r_aabb }))
                    }
                    BVHNode::Leaf { .. } => {
                        None
                    }
                    BVHNode::Dummy => panic!("Dummy node found during BVH optimization!"),
                }
            });
        }

        macro_rules! get_node_aabb {
            ($node_index:expr) => ({
                let node = &nodes[$node_index];
                match *node {
                    BVHNode::Node { child_l_aabb, child_r_aabb, .. } => {
                        child_l_aabb.join(&child_r_aabb)
                    }
                    BVHNode::Leaf { shape, .. } => {
                        shapes[shape].aabb()
                    }
                    BVHNode::Dummy => panic!("Dummy node found during BVH optimization!"),
                }
            });
        }

        // If this node is not a grandparent, update the AABB,
        // queue the parent for refitting, and bail out.
        // If it is a grandparent, calculate the current best_surface_area.
        let (child_l, child_r) = match node_clone {
            BVHNode::Node { parent, child_l, child_r, child_l_aabb, child_r_aabb, .. } => {
                if let BVHNode::Leaf { shape, .. } = nodes[child_l] {
                    let shape_l_index = shape;
                    if let BVHNode::Leaf { shape, .. } = nodes[child_r] {
                        let shape_r_index = shape;

                        // Update the AABBs saved for the children
                        // since at least one of them changed.
                        let mut node = &mut nodes[node_index];
                        match node {
                            &mut BVHNode::Node { ref mut child_l_aabb,
                                                 ref mut child_r_aabb,
                                                 .. } => {
                                *child_l_aabb = shapes[shape_l_index].aabb();
                                *child_r_aabb = shapes[shape_r_index].aabb();
                            }
                            _ => unreachable!(),
                        }

                        return Some(parent);
                    }
                }

                // Recalculate AABBs for the children since at least one of them changed.
                // Don't update the AABBs yet because they're still subject to change
                // during potential upcoming rotations.
                let aabb_l = get_node_aabb!(child_l);
                let aabb_r = get_node_aabb!(child_r);

                parent_index = parent;
                best_surface_area = child_l_aabb.surface_area() + child_r_aabb.surface_area();
                (NodeData {
                     index: child_l,
                     aabb: aabb_l,
                 },
                 NodeData {
                     index: child_r,
                     aabb: aabb_r,
                 })
            }
            BVHNode::Leaf { parent, .. } => {
                return Some(parent);
            }
            BVHNode::Dummy => panic!("Dummy node found during BVH optimization!"),
        };

        // Stores the Rotation that would result in the surface area best_surface_area,
        // thus being the favored rotation that will be executed after considering all rotations.
        let mut best_rotation: Option<(usize, usize)> = None;

        macro_rules! consider_rotation {
            ($a:expr, $b:expr, $sa:expr) => {
                if $sa < best_surface_area {
                    best_surface_area = $sa;
                    best_rotation = Some(($a.index, $b.index));
                }
            };
        }

        // Get indices and AABBs of all grandchildren
        let left_children_nodes = {
            let node_data: NodeData = child_l;
            let index = node_data.index;
            get_children_node_data!(index)
        };
        let right_children_nodes = {
            let node_data: NodeData = child_r;
            let index = node_data.index;
            get_children_node_data!(index)
        };

        // Child to grandchild rotations
        if let Some((child_rl, child_rr)) = right_children_nodes {
            consider_rotation!(child_l,
                               child_rl,
                               child_rl.aabb.surface_area() +
                               child_l.aabb.join(&child_rr.aabb).surface_area());
            consider_rotation!(child_l,
                               child_rr,
                               child_rr.aabb.surface_area() +
                               child_l.aabb.join(&child_rl.aabb).surface_area());
        }
        if let Some((child_ll, child_lr)) = left_children_nodes {
            consider_rotation!(child_r,
                               child_ll,
                               child_ll.aabb.surface_area() +
                               child_r.aabb.join(&child_lr.aabb).surface_area());
            consider_rotation!(child_r,
                               child_lr,
                               child_lr.aabb.surface_area() +
                               child_r.aabb.join(&child_ll.aabb).surface_area());

            // Grandchild to grandchild rotations
            if let Some((child_rl, child_rr)) = right_children_nodes {
                consider_rotation!(child_ll,
                                   child_rl,
                                   child_rl.aabb.join(&child_lr.aabb).surface_area() +
                                   child_ll.aabb.join(&child_rr.aabb).surface_area());
                consider_rotation!(child_ll,
                                   child_rr,
                                   child_ll.aabb.join(&child_rl.aabb).surface_area() +
                                   child_lr.aabb.join(&child_rr.aabb).surface_area());
            }
        }

        let new_refit_node_index = if parent_index > 0 {
            Some(parent_index)
        } else {
            None
        };

        if let Some(rotation) = best_rotation {
            BVH::rotate(nodes, rotation.0, rotation.1);

            // TODO Update all changed node AABBs

            // Return parent node's index for upcoming refitting,
            // since this node just changed its AABB
            new_refit_node_index
        } else {
            // Update this node's AABBs (child_l_aabb, child_r_aabb)
            // according to the children nodes' AABBs.
            let mut node = &mut nodes[node_index];
            match node {
                &mut BVHNode::Node { ref mut child_l_aabb, ref mut child_r_aabb, .. } => {
                    *child_l_aabb = child_l.aabb;
                    *child_r_aabb = child_r.aabb;
                }
                _ => unreachable!(),
            }

            // Even with no rotation being useful for this node, a parent node's rotation
            // could be beneficial, so queue the parent sometimes.
            // TODO Return None most of the time, randomly
            // (see https://github.com/jeske/SimpleScene/blob/master/SimpleScene/Util/ssBVH/ssBVH_Node.cs#L307)
            new_refit_node_index
        }
    }

    /// Switch two nodes by rewiring the involved indices (not by moving them in the nodes slice).
    fn rotate(nodes: &mut Vec<BVHNode>, node_a_index: usize, node_b_index: usize) {
        // This function is not defined with a self parameter to make it easier to call
        // without running into borrow checker issues.
        macro_rules! should_not_happen {
            () => ( panic!("While rotating BVH nodes, something unexpected happened."); );
            ($s:expr) => ( panic!("While rotating BVH nodes, something unexpected happened: {}", $s); );
        }

        #[allow(dead_code)] // The compiler falsely detects dead code here
        fn get_parent_index(nodes: &Vec<BVHNode>, node_index: usize) -> usize {
            let node = &nodes[node_index];

            match *node {
                BVHNode::Node { parent, .. } |
                BVHNode::Leaf { parent, .. } => parent,
                _ => should_not_happen!(),
            }
        }

        // Get parent indices
        let node_a_parent_index = get_parent_index(nodes, node_a_index);
        let node_b_parent_index = get_parent_index(nodes, node_b_index);

        #[allow(dead_code)] // The compiler falsely detects dead code here
        fn get_is_left_child(nodes: &Vec<BVHNode>,
                             node_index: usize,
                             node_parent_index: usize)
                             -> bool {
            let node_parent = &nodes[node_parent_index];

            match *node_parent {
                BVHNode::Node { child_l, .. } => child_l == node_index,
                _ => should_not_happen!(),
            }
        }

        // Get info about the nodes being a left or right child
        let node_a_is_left_child = get_is_left_child(nodes, node_a_index, node_a_parent_index);
        let node_b_is_left_child = get_is_left_child(nodes, node_b_index, node_b_parent_index);

        #[allow(dead_code)] // The compiler falsely detects dead code here
        fn connect_nodes(nodes: &mut Vec<BVHNode>,
                         child_index: usize,
                         parent_index: usize,
                         left_child: bool) {
            // Set parent's child and get its depth
            let parent_depth = {
                let mut parent = &mut nodes[parent_index];
                match parent {
                    &mut BVHNode::Node { ref mut child_l, ref mut child_r, depth, .. } => {
                        if left_child {
                            *child_l = child_index;
                        } else {
                            *child_r = child_index;
                        }
                        depth
                    }
                    _ => should_not_happen!(),
                }
            };

            // Set child's parent and depth
            {
                let mut child = &mut nodes[child_index];
                match child {
                    &mut BVHNode::Node { ref mut parent, ref mut depth, .. } |
                    &mut BVHNode::Leaf { ref mut parent, ref mut depth, .. } => {
                        *parent = parent_index;
                        *depth = parent_depth + 1;
                    }
                    _ => should_not_happen!(),
                };
            }
        }

        // Perform the switch
        connect_nodes(nodes,
                      node_a_index,
                      node_b_parent_index,
                      node_b_is_left_child);
        connect_nodes(nodes,
                      node_b_index,
                      node_a_parent_index,
                      node_a_is_left_child);
    }
}

#[cfg(test)]
pub mod tests {
    use bvh::BVH;
    use std::collections::HashSet;
    use testbase::build_some_bh;

    #[test]
    /// Tests if the optimize function tries to change a fresh BVH even though it shouldn't
    fn test_optimizing_new_bvh() {
        let (mut shapes, mut bvh) = build_some_bh::<BVH>();

        let refit_shape_indices: HashSet<usize> = (0..shapes.len()).collect();
        bvh.optimize(&refit_shape_indices, &mut shapes);
    }

    // TODO Add tests for:
    // * correct parent
    // * correct depth
    // * correct BVH after optimizing
    // * correct parent and depth after optimizing
}
<commit_msg>Update AABBs when rotating nodes<commit_after>use bvh::*;
use bounding_hierarchy::BHShape;
use aabb::AABB;
use std::collections::HashSet;

// TODO Consider: Instead of getting the scene's shapes passed, let leaf nodes store an AABB
// that is updated from the outside, perhaps by passing not only the indices of the changed
// shapes, but also their new AABBs into optimize().

// TODO Replace macros with (closure form) functions.

impl BVH {
    /// Optimizes the `BVH` by batch-reorganizing updated nodes.
    /// Based on https://github.com/jeske/SimpleScene/blob/master/SimpleScene/Util/ssBVH/ssBVH.cs
    ///
    /// Needs all the scene's shapes, plus the indices of the shapes that were updated.
    ///
    pub fn optimize<Shape: BHShape>(&mut self,
                                    refit_shape_indices: &HashSet<usize>,
                                    shapes: &[Shape]) {
        let mut refit_node_indices: HashSet<usize> = refit_shape_indices.iter()
            .map(|x| shapes[*x].bh_node_index())
            .collect();

        // As long as we have refit nodes left, take the list of refit nodes
        // with the highest depth (sweep nodes) and try to rotate them all
        while refit_node_indices.len() > 0 {
            let mut max_depth = 0;
            let mut sweep_node_indices: Vec<usize> = Vec::new();

            // Find max_depth and sweep_node_indices in one iteration
            for refit_node_index in refit_node_indices.iter() {
                let depth = match self.nodes[*refit_node_index] {
                    BVHNode::Node { depth, .. } => depth,
                    BVHNode::Leaf { depth, .. } => depth,
                    BVHNode::Dummy => panic!("Dummy node found during BVH optimization!"),
                };

                if depth > max_depth {
                    max_depth = depth;
                    sweep_node_indices.clear();
                    sweep_node_indices.push(*refit_node_index);
                } else if depth == max_depth {
                    sweep_node_indices.push(*refit_node_index);
                }
            }

            // Try to find a useful tree rotation with all nodes previously found
            for sweep_node_index in sweep_node_indices.iter() {
                // This node does not need to be checked again
                refit_node_indices.remove(&sweep_node_index);

                // TODO There might be multithreading potential here
                let new_refit_node_index = self.try_rotate(*sweep_node_index, shapes);

                // Instead of finding a useful tree rotation, we found another node
                // that we should check, so we add its index to the refit_node_indices.
                if let Some(index) = new_refit_node_index {
                    refit_node_indices.insert(index);
                }
            }
        }
    }

    /// Checks if there is a way to rotate a child and a grandchild (or two grandchildren) of
    /// the given node (specified by `node_index`) that would improve the `BVH`.
    /// If there is, the best rotation found is performed.
    /// Relies on the children and transitive children of the given node having correct AABBs.
    ///
    /// Returns Some(index_of_node) if a new node was found that should be used for optimization.
    ///
    fn try_rotate<Shape: BHShape>(&mut self, node_index: usize, shapes: &[Shape]) -> Option<usize> {
        let mut nodes = &mut self.nodes;

        let node_clone = nodes[node_index].clone();

        // Contains the surface area that would result from applying the currently favored rotation.
        // The rotation with the smallest SA will be applied in the end.
        // The value is calculated by child_l_aabb.surface_area() + child_r_aabb.surface_area()
        let mut best_surface_area = 0f32;

        // TODO Re-implement without mutability
        let mut parent_index: usize = 0;

        #[derive(Debug, Copy, Clone)]
        struct NodeData {
            index: usize,
            aabb: AABB,
        }

        macro_rules! get_children_node_data {
            ($node_index:expr) => ({
                let node = nodes[$node_index].clone();
                match node {
                    BVHNode::Node { child_l, child_r, child_l_aabb, child_r_aabb, .. } => {
                        Some((NodeData{ index: child_l, aabb: child_l_aabb },
                            NodeData{ index: child_r, aabb: child_r_aabb }))
                    }
                    BVHNode::Leaf { .. } => {
                        None
                    }
                    BVHNode::Dummy => panic!("Dummy node found during BVH optimization!"),
                }
            });
        }

        // TODO Replace with non-embedded function for reuse.
        macro_rules! get_node_aabb {
            ($node_index:expr) => ({
                let node = &nodes[$node_index];
                match *node {
                    BVHNode::Node { child_l_aabb, child_r_aabb, .. } => {
                        child_l_aabb.join(&child_r_aabb)
                    }
                    BVHNode::Leaf { shape, .. } => {
                        shapes[shape].aabb()
                    }
                    BVHNode::Dummy => panic!("Dummy node found during BVH optimization!"),
                }
            });
        }

        // If this node is not a grandparent, update the AABB,
        // queue the parent for refitting, and bail out.
        // If it is a grandparent, calculate the current best_surface_area.
        let (child_l, child_r) = match node_clone {
            BVHNode::Node { parent, child_l, child_r, child_l_aabb, child_r_aabb, .. } => {
                if let BVHNode::Leaf { shape, .. } = nodes[child_l] {
                    let shape_l_index = shape;
                    if let BVHNode::Leaf { shape, .. } = nodes[child_r] {
                        let shape_r_index = shape;

                        // Update the AABBs saved for the children
                        // since at least one of them changed.
                        let mut node = &mut nodes[node_index];
                        match node {
                            &mut BVHNode::Node { ref mut child_l_aabb,
                                                 ref mut child_r_aabb,
                                                 .. } => {
                                *child_l_aabb = shapes[shape_l_index].aabb();
                                *child_r_aabb = shapes[shape_r_index].aabb();
                            }
                            _ => unreachable!(),
                        }

                        return Some(parent);
                    }
                }

                // Recalculate AABBs for the children since at least one of them changed.
                // Don't update the AABBs yet because they're still subject to change
                // during potential upcoming rotations.
                let aabb_l = get_node_aabb!(child_l);
                let aabb_r = get_node_aabb!(child_r);

                parent_index = parent;
                best_surface_area = child_l_aabb.surface_area() + child_r_aabb.surface_area();
                (NodeData {
                     index: child_l,
                     aabb: aabb_l,
                 },
                 NodeData {
                     index: child_r,
                     aabb: aabb_r,
                 })
            }
            BVHNode::Leaf { parent, .. } => {
                return Some(parent);
            }
            BVHNode::Dummy => panic!("Dummy node found during BVH optimization!"),
        };

        // Stores the Rotation that would result in the surface area best_surface_area,
        // thus being the favored rotation that will be executed after considering all rotations.
        let mut best_rotation: Option<(usize, usize)> = None;
        let mut perform_grandchild_rotation = false;

        macro_rules! consider_rotation {
            ($a:expr, $b:expr, $sa:expr) => {
                if $sa < best_surface_area {
                    best_surface_area = $sa;
                    best_rotation = Some(($a.index, $b.index));
                    perform_grandchild_rotation = false;
                }
            };
            (grandchildren, $a:expr, $b:expr, $sa:expr) => {
                if $sa < best_surface_area {
                    best_surface_area = $sa;
                    best_rotation = Some(($a.index, $b.index));
                    perform_grandchild_rotation = true;
                }
            };
        }

        // Get indices and AABBs of all grandchildren
        let left_children_nodes = {
            let node_data: NodeData = child_l;
            let index = node_data.index;
            get_children_node_data!(index)
        };
        let right_children_nodes = {
            let node_data: NodeData = child_r;
            let index = node_data.index;
            get_children_node_data!(index)
        };

        // Child to grandchild rotations
        if let Some((child_rl, child_rr)) = right_children_nodes {
            consider_rotation!(child_l,
                               child_rl,
                               child_rl.aabb.surface_area() +
                               child_l.aabb.join(&child_rr.aabb).surface_area());
            consider_rotation!(child_l,
                               child_rr,
                               child_rr.aabb.surface_area() +
                               child_l.aabb.join(&child_rl.aabb).surface_area());
        }
        if let Some((child_ll, child_lr)) = left_children_nodes {
            consider_rotation!(child_r,
                               child_ll,
                               child_ll.aabb.surface_area() +
                               child_r.aabb.join(&child_lr.aabb).surface_area());
            consider_rotation!(child_r,
                               child_lr,
                               child_lr.aabb.surface_area() +
                               child_r.aabb.join(&child_ll.aabb).surface_area());

            // Grandchild to grandchild rotations
            if let Some((child_rl, child_rr)) = right_children_nodes {
                consider_rotation!(grandchildren,
                                   child_ll,
                                   child_rl,
                                   child_rl.aabb.join(&child_lr.aabb).surface_area() +
                                   child_ll.aabb.join(&child_rr.aabb).surface_area());
                consider_rotation!(grandchildren,
                                   child_ll,
                                   child_rr,
                                   child_ll.aabb.join(&child_rl.aabb).surface_area() +
                                   child_lr.aabb.join(&child_rr.aabb).surface_area());
            }
        }

        let new_refit_node_index = if parent_index > 0 {
            Some(parent_index)
        } else {
            None
        };

        if let Some(rotation) = best_rotation {
            BVH::rotate(nodes, rotation.0, rotation.1, shapes);

            if perform_grandchild_rotation {
                // Update this node's AABBs (child_l_aabb, child_r_aabb)
                // according to the children nodes' AABBs.

                // The AABBs of the children have changed, so we need to get the new ones.
                // The children are still the same though, so we can use the indices we already have.
                let new_child_l_aabb = get_node_aabb!(child_l.index);
                let new_child_r_aabb = get_node_aabb!(child_r.index);

                let mut node = &mut nodes[node_index];
                match node {
                    &mut BVHNode::Node { ref mut child_l_aabb, ref mut child_r_aabb, .. } => {
                        *child_l_aabb = new_child_l_aabb;
                        *child_r_aabb = new_child_r_aabb;
                    }
                    _ => unreachable!(),
                }
            }

            // Return parent node's index for upcoming refitting,
            // since this node just changed its AABB
            new_refit_node_index
        } else {
            // Update this node's AABBs (child_l_aabb, child_r_aabb)
            // according to the children nodes' AABBs.
            let mut node = &mut nodes[node_index];
            match node {
                &mut BVHNode::Node { ref mut child_l_aabb, ref mut child_r_aabb, .. } => {
                    *child_l_aabb = child_l.aabb;
                    *child_r_aabb = child_r.aabb;
                }
                _ => unreachable!(),
            }

            // Even with no rotation being useful for this node, a parent node's rotation
            // could be beneficial, so queue the parent sometimes.
            // TODO Return None most of the time, randomly
            // (see https://github.com/jeske/SimpleScene/blob/master/SimpleScene/Util/ssBVH/ssBVH_Node.cs#L307)
            new_refit_node_index
        }
    }

    /// Switch two nodes by rewiring the involved indices (not by moving them in the nodes slice).
    /// Also updates the AABBs of the parents.
    fn rotate<Shape: BHShape>(nodes: &mut Vec<BVHNode>, node_a_index: usize, node_b_index: usize, shapes: &[Shape]) {
        // This function is not defined with a self parameter to make it easier to call
        // without running into borrow checker issues.
        macro_rules! should_not_happen {
            () => ( panic!("While rotating BVH nodes, something unexpected happened."); );
            ($s:expr) => ( panic!("While rotating BVH nodes, something unexpected happened: {}", $s); );
        }

        #[allow(dead_code)] // The compiler falsely detects dead code here
        fn get_parent_index(nodes: &Vec<BVHNode>, node_index: usize) -> usize {
            let node = &nodes[node_index];

            match *node {
                BVHNode::Node { parent, .. } |
                BVHNode::Leaf { parent, .. } => parent,
                _ => should_not_happen!(),
            }
        }

        // Get parent indices
        let node_a_parent_index = get_parent_index(nodes, node_a_index);
        let node_b_parent_index = get_parent_index(nodes, node_b_index);

        #[allow(dead_code)] // The compiler falsely detects dead code here
        fn get_is_left_child(nodes: &Vec<BVHNode>,
                             node_index: usize,
                             node_parent_index: usize)
                             -> bool {
            let node_parent = &nodes[node_parent_index];

            match *node_parent {
                BVHNode::Node { child_l, .. } => child_l == node_index,
                _ => should_not_happen!(),
            }
        }

        // Get info about the nodes being a left or right child
        let node_a_is_left_child = get_is_left_child(nodes, node_a_index, node_a_parent_index);
        let node_b_is_left_child = get_is_left_child(nodes, node_b_index, node_b_parent_index);

        #[allow(dead_code)] // The compiler falsely detects dead code here
        fn connect_nodes<Shape: BHShape>(nodes: &mut Vec<BVHNode>,
                                         child_index: usize,
                                         parent_index: usize,
                                         left_child: bool,
                                         shapes: &[Shape]) {
            // TODO Replace with non-embedded function for reuse.
            macro_rules! get_node_aabb {
                 ($node_index:expr) => ({
                     let node = &nodes[$node_index];
                     match *node {
                         BVHNode::Node { child_l_aabb, child_r_aabb, .. } => {
                             child_l_aabb.join(&child_r_aabb)
                         }
                         BVHNode::Leaf { shape, .. } => {
                             shapes[shape].aabb()
                         }
                         BVHNode::Dummy => panic!("Dummy node found during BVH optimization!"),
                     }
                 });
             }

            let child_aabb = get_node_aabb!(child_index);

            // Set parent's child and child_aabb; and get its depth
            let parent_depth = {
                let mut parent = &mut nodes[parent_index];
                match parent {
                    &mut BVHNode::Node { ref mut child_l,
                                         ref mut child_r,
                                         ref mut child_l_aabb,
                                         ref mut child_r_aabb,
                                         depth,
                                         .. } => {
                        if left_child {
                            *child_l = child_index;
                            *child_l_aabb = child_aabb;
                        } else {
                            *child_r = child_index;
                            *child_r_aabb = child_aabb;
                        }
                        depth
                    }
                    _ => should_not_happen!(),
                }
            };

            // Set child's parent and depth
            {
                let mut child = &mut nodes[child_index];
                match child {
                    &mut BVHNode::Node { ref mut parent, ref mut depth, .. } |
                    &mut BVHNode::Leaf { ref mut parent, ref mut depth, .. } => {
                        *parent = parent_index;
                        *depth = parent_depth + 1;
                    }
                    _ => should_not_happen!(),
                };
            }
        }

        // Perform the switch
        connect_nodes(nodes,
                      node_a_index,
                      node_b_parent_index,
                      node_b_is_left_child,
                      shapes);
        connect_nodes(nodes,
                      node_b_index,
                      node_a_parent_index,
                      node_a_is_left_child,
                      shapes);
    }
}

#[cfg(test)]
pub mod tests {
    use bvh::BVH;
    use std::collections::HashSet;
    use testbase::build_some_bh;

    #[test]
    /// Tests if the optimize function tries to change a fresh BVH even though it shouldn't
    fn test_optimizing_new_bvh() {
        let (mut shapes, mut bvh) = build_some_bh::<BVH>();

        let refit_shape_indices: HashSet<usize> = (0..shapes.len()).collect();
        bvh.optimize(&refit_shape_indices, &mut shapes);
    }

    // TODO Add tests for:
    // * correct parent
    // * correct depth
    // * correct BVH after optimizing
    // * correct parent and depth after optimizing
}
<|endoftext|>


<commit_before>// Copyright 2013 The CGMath Developers. For a full listing of the authors,
// refer to the AUTHORS file at the top-level directory of this distribution.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use std::{fmt,num};

use matrix::{Matrix, Mat4, ToMat4};
use point::{Point, Point3};
use ray::Ray;
use rotation::{Rotation, Rotation3};
use quaternion::Quat;
use vector::{Vector, Vec3};

/// A trait of affine transformation, that can be applied to points or vectors
pub trait Transform
<
    S: Primitive,
    Slice,
    V: Vector<S,Slice>,
    P: Point<S,V,Slice>
>
{
    fn identity() -> Self;

    fn transform_vec(&self, vec: &V) -> V;
    fn transform_point(&self, point: &P) -> P;

    #[inline]
    fn transform_ray(&self, ray: &Ray<P,V>) -> Ray<P,V>    {
        Ray::new( self.transform_point(&ray.origin), self.transform_vec(&ray.direction) )
    }

    fn concat(&self, other: &Self) -> Self;
    fn invert(&self) -> Option<Self>;

    #[inline]
    fn concat_self(&mut self, other: &Self) {
        *self = self.concat(other);
    }

    #[inline]
    fn invert_self(&mut self)-> bool    {
        match self.invert() {
            Some(t) => {*self = t; true},
            None    => false,
        }
    }
}

/// A generic transformation consisting of a rotation,
/// displacement vector and scale amount.
pub struct Decomposed<S,V,R>    {
    scale: S,
    rot: R,
    disp: V,
}

impl
<
    S: Float,
    Slice,
    V: Vector<S, Slice>,
    P: Point<S, V, Slice>,
    R: Rotation<S, Slice, V, P>
>
Transform<S, Slice, V, P> for Decomposed<S,V,R>    {
    #[inline]
    fn identity() -> Decomposed<S,V,R>  {
        Decomposed {
            scale: num::one(),
            rot: Rotation::identity(),
            disp: num::zero(),
        }
    }

    #[inline]
    fn transform_vec(&self, vec: &V) -> V   {
        self.rot.rotate_vec( &vec.mul_s( self.scale.clone() ))
    }

    #[inline]
    fn transform_point(&self, point: &P) -> P   {
        self.rot.rotate_point( &point.mul_s( self.scale.clone() )).add_v( &self.disp )
    }

    fn concat(&self, other: &Decomposed<S,V,R>) -> Decomposed<S,V,R>    {
        let p = Point::from_vec( &other.disp );
        Decomposed {
            scale: self.scale * other.scale,
            rot: self.rot.concat( &other.rot ),
            disp: self.transform_point( &p ).to_vec(),
        }
    }

    fn invert(&self) -> Option<Decomposed<S,V,R>>   {
        if self.scale.approx_eq( &num::zero() )   {
            None
        }else   {
            let _1 : S = num::one();
            let s = _1 / self.scale;
            let r = self.rot.invert();
            let d = r.rotate_vec( &self.disp ).mul_s( -s );
            Some( Decomposed {
                scale: s,
                rot: r,
                disp: d,
            })
        }
    }
}

pub trait Transform3<S>
: Transform<S, [S, ..3], Vec3<S>, Point3<S>>
+ ToMat4<S>
{}

impl<S: Float + Clone, R: Rotation3<S>>
ToMat4<S> for Decomposed<S, Vec3<S>, R> {
    fn to_mat4(&self) -> Mat4<S>   {
        let mut m = self.rot.to_mat3().mul_s( self.scale.clone() ).to_mat4();
        m.w = self.disp.extend( num::one() );
        m
    }
}

impl<S: Float, R: Rotation3<S>>
Transform3<S> for Decomposed<S,Vec3<S>,R>   {}

impl<S: fmt::Default + Float, R: ToStr + Rotation3<S>>
ToStr for Decomposed<S,Vec3<S>,R> {
    fn to_str(&self) -> ~str {
        format!("(scale({}), rot({:s}), disp{:s})",
            self.scale, self.rot.to_str(), self.disp.to_str())
    }
}

/// A homogeneous transformation matrix.
pub struct AffineMatrix3<S> {
    mat: Mat4<S>,
}

impl<S : Clone + Float>
Transform<S, [S, ..3], Vec3<S>, Point3<S>> for AffineMatrix3<S>  {
    #[inline]
    fn identity() -> AffineMatrix3<S>  {
       AffineMatrix3 { mat: Mat4::identity() }
    }

    #[inline]
    fn transform_vec(&self, vec: &Vec3<S>) -> Vec3<S>  {
        self.mat.mul_v( &vec.extend(num::zero()) ).truncate()
    }

    #[inline]
    fn transform_point(&self, point: &Point3<S>) -> Point3<S>   {
        Point3::from_homogeneous( &self.mat.mul_v( &point.to_homogeneous() ))
    }

    #[inline]
    fn concat(&self, other: &AffineMatrix3<S>) -> AffineMatrix3<S>    {
        AffineMatrix3 { mat: self.mat.mul_m( &other.mat ) }
    }

    #[inline]
    fn invert(&self) -> Option<AffineMatrix3<S>>   {
        self.mat.invert().map(|m| AffineMatrix3{ mat: m })
    }
}

impl<S: Clone + Primitive>
ToMat4<S> for AffineMatrix3<S>  {
    #[inline] fn to_mat4(&self) -> Mat4<S>    { self.mat.clone() }
}

impl<S: Float>
Transform3<S> for AffineMatrix3<S>   {}

/// A transformation in three dimensions consisting of a rotation,
/// displacement vector and scale amount.
pub struct Transform3D<S>( Decomposed<S,Vec3<S>,Quat<S>> );

impl<S: Float> Transform3D<S> {
    #[inline]
    pub fn new(scale: S, rot: Quat<S>, disp: Vec3<S>) -> Transform3D<S> {
       Transform3D( Decomposed { scale: scale, rot: rot, disp: disp })
    }
}
<commit_msg>Added transform_as_point<commit_after>// Copyright 2013 The CGMath Developers. For a full listing of the authors,
// refer to the AUTHORS file at the top-level directory of this distribution.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use std::{fmt,num};

use matrix::{Matrix, Mat4, ToMat4};
use point::{Point, Point3};
use ray::Ray;
use rotation::{Rotation, Rotation3};
use quaternion::Quat;
use vector::{Vector, Vec3};

/// A trait of affine transformation, that can be applied to points or vectors
pub trait Transform
<
    S: Primitive,
    Slice,
    V: Vector<S,Slice>,
    P: Point<S,V,Slice>
>
{
    fn identity() -> Self;

    fn transform_vec(&self, vec: &V) -> V;
    fn transform_point(&self, point: &P) -> P;

    #[inline]
    fn transform_ray(&self, ray: &Ray<P,V>) -> Ray<P,V>    {
        Ray::new( self.transform_point(&ray.origin), self.transform_vec(&ray.direction) )
    }

    #[inline]
    fn transform_as_point(&self, vec: &V)-> V   {
        self.transform_point( &Point::from_vec(vec) ).to_vec()
    }

    fn concat(&self, other: &Self) -> Self;
    fn invert(&self) -> Option<Self>;

    #[inline]
    fn concat_self(&mut self, other: &Self) {
        *self = self.concat(other);
    }

    #[inline]
    fn invert_self(&mut self)-> bool    {
        match self.invert() {
            Some(t) => {*self = t; true},
            None    => false,
        }
    }
}

/// A generic transformation consisting of a rotation,
/// displacement vector and scale amount.
pub struct Decomposed<S,V,R>    {
    scale: S,
    rot: R,
    disp: V,
}

impl
<
    S: Float,
    Slice,
    V: Vector<S, Slice>,
    P: Point<S, V, Slice>,
    R: Rotation<S, Slice, V, P>
>
Transform<S, Slice, V, P> for Decomposed<S,V,R>    {
    #[inline]
    fn identity() -> Decomposed<S,V,R>  {
        Decomposed {
            scale: num::one(),
            rot: Rotation::identity(),
            disp: num::zero(),
        }
    }

    #[inline]
    fn transform_vec(&self, vec: &V) -> V   {
        self.rot.rotate_vec( &vec.mul_s( self.scale.clone() ))
    }

    #[inline]
    fn transform_point(&self, point: &P) -> P   {
        self.rot.rotate_point( &point.mul_s( self.scale.clone() )).add_v( &self.disp )
    }

    fn concat(&self, other: &Decomposed<S,V,R>) -> Decomposed<S,V,R>    {
        Decomposed {
            scale: self.scale * other.scale,
            rot: self.rot.concat( &other.rot ),
            disp: self.transform_as_point( &other.disp ),
        }
    }

    fn invert(&self) -> Option<Decomposed<S,V,R>>   {
        if self.scale.approx_eq( &num::zero() )   {
            None
        }else   {
            let _1 : S = num::one();
            let s = _1 / self.scale;
            let r = self.rot.invert();
            let d = r.rotate_vec( &self.disp ).mul_s( -s );
            Some( Decomposed {
                scale: s,
                rot: r,
                disp: d,
            })
        }
    }
}

pub trait Transform3<S>
: Transform<S, [S, ..3], Vec3<S>, Point3<S>>
+ ToMat4<S>
{}

impl<S: Float + Clone, R: Rotation3<S>>
ToMat4<S> for Decomposed<S, Vec3<S>, R> {
    fn to_mat4(&self) -> Mat4<S>   {
        let mut m = self.rot.to_mat3().mul_s( self.scale.clone() ).to_mat4();
        m.w = self.disp.extend( num::one() );
        m
    }
}

impl<S: Float, R: Rotation3<S>>
Transform3<S> for Decomposed<S,Vec3<S>,R>   {}

impl<S: fmt::Default + Float, R: ToStr + Rotation3<S>>
ToStr for Decomposed<S,Vec3<S>,R> {
    fn to_str(&self) -> ~str {
        format!("(scale({}), rot({:s}), disp{:s})",
            self.scale, self.rot.to_str(), self.disp.to_str())
    }
}

/// A homogeneous transformation matrix.
pub struct AffineMatrix3<S> {
    mat: Mat4<S>,
}

impl<S : Clone + Float>
Transform<S, [S, ..3], Vec3<S>, Point3<S>> for AffineMatrix3<S>  {
    #[inline]
    fn identity() -> AffineMatrix3<S>  {
       AffineMatrix3 { mat: Mat4::identity() }
    }

    #[inline]
    fn transform_vec(&self, vec: &Vec3<S>) -> Vec3<S>  {
        self.mat.mul_v( &vec.extend(num::zero()) ).truncate()
    }

    #[inline]
    fn transform_point(&self, point: &Point3<S>) -> Point3<S>   {
        Point3::from_homogeneous( &self.mat.mul_v( &point.to_homogeneous() ))
    }

    #[inline]
    fn concat(&self, other: &AffineMatrix3<S>) -> AffineMatrix3<S>    {
        AffineMatrix3 { mat: self.mat.mul_m( &other.mat ) }
    }

    #[inline]
    fn invert(&self) -> Option<AffineMatrix3<S>>   {
        self.mat.invert().map(|m| AffineMatrix3{ mat: m })
    }
}

impl<S: Clone + Primitive>
ToMat4<S> for AffineMatrix3<S>  {
    #[inline] fn to_mat4(&self) -> Mat4<S>    { self.mat.clone() }
}

impl<S: Float>
Transform3<S> for AffineMatrix3<S>   {}

/// A transformation in three dimensions consisting of a rotation,
/// displacement vector and scale amount.
pub struct Transform3D<S>( Decomposed<S,Vec3<S>,Quat<S>> );

impl<S: Float> Transform3D<S> {
    #[inline]
    pub fn new(scale: S, rot: Quat<S>, disp: Vec3<S>) -> Transform3D<S> {
       Transform3D( Decomposed { scale: scale, rot: rot, disp: disp })
    }
}
<|endoftext|>


<commit_before>fn main() {
    println!("Hello, world!");
}
<commit_msg>Initial commit of sufficient solution<commit_after>use std::collections::{HashMap, HashSet, VecDeque};
use std::error::Error;
use std::fs::File;
use std::io::{BufRead, BufReader};
use std::vec::Vec;

fn collect_matching_words(mut words: Vec<String>) -> HashMap<String, Vec<String>> {
    let mut collected_words = HashMap::new();
    for word in words.drain(..) {
        let (prefix, suffix) = word.split_at(1);
        collected_words.entry(suffix.to_owned()).or_insert(Vec::new()).push(prefix.to_owned())
    }
    collected_words
}

fn find_row(letter: &str) -> i8 {
    static first: [&'static str; 10] = ["q", "w", "e", "r", "t", "y", "u", "i", "o", "p"];
    static second: [&'static str; 9] = ["a", "s", "d", "f", "g", "h", "j", "k", "l"];
    static third: [&'static str; 7] = ["z", "x", "c", "v", "b", "n", "m"];
    if first.contains(&letter) {
        return 1;
    } else if second.contains(&letter) {
        return 2;
    } else if third.contains(&letter) {
        return 3;
    }
    unreachable!()
}

fn main() {
    let f = match File::open("some_words.txt") {
        Ok(f) => f,
        Err(why) => panic!("Failed to open links.txt: {}", Error::description(&why)),
    };
    let words: Vec<String> = BufReader::new(f)
                                 .lines()
                                 .map(|s| s.unwrap())
                                 .filter(|s| s.len() == 4)
                                 .map(|s| s)
                                 .collect();
    let suffix_to_prefix: HashMap<String, Vec<String>> = collect_matching_words(words)
                                                             .into_iter()
                                                             .filter(|&(_, ref v)| v.len() > 4)
                                                             .collect();
    for (suffix, prefixes) in suffix_to_prefix {
        let mut rows_map: HashMap<i8, i8> = HashMap::new();
        for prefix in prefixes.iter() {
            *rows_map.entry(find_row(&prefix)).or_insert(0) += 1;
        }
        for (row, count) in rows_map {
            if count >= 5 {
                println!("Found:");
                for prefix in prefixes.iter() {
                    if find_row(&prefix) == row {
                        println!("{}{}", prefix, suffix);
                    }
                }
            }
        }
    }
}
<|endoftext|>


<commit_before>use crate::utils::printing;
use std::fs;
use std::path::PathBuf;
use std::process::Command;

pub fn clone(path: &PathBuf, url: &str) {
    printing::progress(format!("Cloning {} repo.", url));

    match Command::new("git")
        .current_dir(&path)
        .arg("clone")
        .arg(url)
        .output()
    {
        Ok(_) => (),
        Err(error) => panic!("There was a problem: {:?}", error),
    }
}

pub fn pull_all(path: &PathBuf) {
    printing::progress(format!(
        "Upgrading all repos in {}.",
        path.to_string_lossy()
    ));
    match fs::read_dir(path) {
        Ok(dirs) => {
            for dir in dirs {
                match Command::new("git")
                    .current_dir(match dir {
                        Ok(dir) => dir.path(),
                        Err(error) => panic!("There was a problem: {:?}", error),
                    })
                    .arg("pull")
                    .output()
                {
                    Ok(_) => (),
                    Err(error) => panic!("There was a problem: {:?}", error),
                }
            }
        }
        Err(error) => panic!("There was a problem: {:?}", error),
    };
}

pub fn check_all(path: &PathBuf) {
    printing::progress(format!("Checking all repos in {}.", path.to_string_lossy()));
    match fs::read_dir(path) {
        Ok(dirs) => {
            let mut all_repos_clean = true;

            for dir in dirs {
                let dir = match &dir {
                    Ok(dir) => dir.path(),
                    Err(error) => panic!("There was a problem: {:?}", error),
                };

                let status_check = match Command::new("git")
                    .current_dir(&dir)
                    .arg("status")
                    .arg("--porcelain")
                    .output()
                {
                    Ok(output) => output.stdout,
                    Err(error) => panic!("There was a problem: {:?}", error),
                };

                let unpushed_check = match Command::new("git")
                    .current_dir(&dir)
                    .arg("log")
                    .arg("@{u}..")
                    .output()
                {
                    Ok(output) => output.stdout,
                    Err(error) => panic!("There was a problem: {:?}", error),
                };

                if !status_check.is_empty() || !unpushed_check.is_empty() {
                    all_repos_clean = false;
                    printing::error(format!("A dirty repo was found: {}", &dir.display()));
                }
            }

            if !all_repos_clean {
                panic!("Repos are dirty.");
            }
        }
        Err(error) => panic!("There was a problem: {:?}", error),
    };
}
<commit_msg>Clarify iterator variable names<commit_after>use crate::utils::printing;
use std::fs;
use std::path::PathBuf;
use std::process::Command;

pub fn clone(path: &PathBuf, url: &str) {
    printing::progress(format!("Cloning {} repo.", url));

    match Command::new("git")
        .current_dir(&path)
        .arg("clone")
        .arg(url)
        .output()
    {
        Ok(_) => (),
        Err(error) => panic!("There was a problem: {:?}", error),
    }
}

pub fn pull_all(path: &PathBuf) {
    printing::progress(format!(
        "Upgrading all repos in {}.",
        path.to_string_lossy()
    ));
    match fs::read_dir(path) {
        Ok(repos) => {
            for repo in repos {
                match Command::new("git")
                    .current_dir(match repo {
                        Ok(repo) => repo.path(),
                        Err(error) => panic!("There was a problem: {:?}", error),
                    })
                    .arg("pull")
                    .output()
                {
                    Ok(_) => (),
                    Err(error) => panic!("There was a problem: {:?}", error),
                }
            }
        }
        Err(error) => panic!("There was a problem: {:?}", error),
    };
}

pub fn check_all(path: &PathBuf) {
    printing::progress(format!("Checking all repos in {}.", path.to_string_lossy()));
    match fs::read_dir(path) {
        Ok(repos) => {
            let mut all_repos_clean = true;

            for repo in repos {
                let repo = match &repo {
                    Ok(repo) => repo.path(),
                    Err(error) => panic!("There was a problem: {:?}", error),
                };

                let status_check = match Command::new("git")
                    .current_dir(&repo)
                    .arg("status")
                    .arg("--porcelain")
                    .output()
                {
                    Ok(output) => output.stdout,
                    Err(error) => panic!("There was a problem: {:?}", error),
                };

                let unpushed_check = match Command::new("git")
                    .current_dir(&repo)
                    .arg("log")
                    .arg("@{u}..")
                    .output()
                {
                    Ok(output) => output.stdout,
                    Err(error) => panic!("There was a problem: {:?}", error),
                };

                if !status_check.is_empty() || !unpushed_check.is_empty() {
                    all_repos_clean = false;
                    printing::error(format!("A dirty repo was found: {}", &repo.display()));
                }
            }

            if !all_repos_clean {
                panic!("Repos are dirty.");
            }
        }
        Err(error) => panic!("There was a problem: {:?}", error),
    };
}
<|endoftext|>


<commit_before>//! This module implements the root command of the CLI tool.

use std::env;
use std::fmt;
use std::process;

use clap::{App, AppSettings, Arg, ArgMatches};
use failure::{bail, Error};
use log::{debug, info};

use crate::api::Api;
use crate::config::{prepare_environment, Auth, Config};
use crate::constants::VERSION;
use crate::utils::system::{print_error, QuietExit};
use crate::utils::update::run_sentrycli_update_nagger;

const ABOUT: &str = "
Command line utility for Sentry.

This tool helps you manage remote resources on a Sentry server like
sourcemaps, debug symbols or releases.  Use `--help` on the subcommands
to learn more about them.";

macro_rules! each_subcommand {
    ($mac:ident) => {
        $mac!(upload_dif);
        $mac!(upload_dsym);
        $mac!(upload_proguard);
        $mac!(releases);
        $mac!(issues);
        $mac!(repos);
        $mac!(projects);
        #[cfg(not(feature = "managed"))]
        $mac!(update);
        #[cfg(not(feature = "managed"))]
        $mac!(uninstall);
        $mac!(info);
        $mac!(login);
        $mac!(send_event);
        $mac!(react_native);
        $mac!(difutil);
        $mac!(bash_hook);

        // these here exist for legacy reasons only.  They were moved
        // to subcommands of the react-native command.  Note that
        // codepush was never available on that level.
        #[cfg(target_os = "macos")]
        $mac!(react_native_xcode);
        $mac!(react_native_gradle);
    };
}

// commands we want to run the update nagger on
const UPDATE_NAGGER_CMDS: &[&str] = &[
    "releases", "issues", "repos", "projects", "info", "login", "difutil",
];

// it would be great if this could be a macro expansion as well
// but rust bug #37663 breaks location information then.
pub mod bash_hook;
pub mod info;
pub mod issues;
pub mod login;
pub mod projects;
pub mod releases;
pub mod repos;
pub mod send_event;
pub mod uninstall;
pub mod update;
pub mod upload_dif;
pub mod upload_dsym;
pub mod upload_proguard;

pub mod react_native;
pub mod react_native_appcenter;
pub mod react_native_codepush;
pub mod react_native_gradle;
#[cfg(target_os = "macos")]
pub mod react_native_xcode;

pub mod difutil;
pub mod difutil_check;
pub mod difutil_find;
pub mod difutil_id;

fn preexecute_hooks() -> Result<bool, Error> {
    return sentry_react_native_xcode_wrap();

    #[cfg(target_os = "macos")]
    fn sentry_react_native_xcode_wrap() -> Result<bool, Error> {
        if let Ok(val) = env::var("__SENTRY_RN_WRAP_XCODE_CALL") {
            env::remove_var("__SENTRY_RN_WRAP_XCODE_CALL");
            if &val == "1" {
                react_native_xcode::wrap_call()?;
                return Ok(true);
            }
        }
        Ok(false)
    }

    #[cfg(not(target_os = "macos"))]
    fn sentry_react_native_xcode_wrap() -> Result<bool, Error> {
        Ok(false)
    }
}

fn configure_args(config: &mut Config, matches: &ArgMatches<'_>) -> Result<(), Error> {
    if let Some(url) = matches.value_of("url") {
        config.set_base_url(url);
    }

    if let Some(api_key) = matches.value_of("api_key") {
        config.set_auth(Auth::Key(api_key.to_owned()));
    }

    if let Some(auth_token) = matches.value_of("auth_token") {
        config.set_auth(Auth::Token(auth_token.to_owned()));
    }

    if let Some(level_str) = matches.value_of("log_level") {
        match level_str.parse() {
            Ok(level) => {
                config.set_log_level(level);
            }
            Err(_) => {
                bail!("Unknown log level: {}", level_str);
            }
        }
    }

    Ok(())
}

fn add_commands<'a, 'b>(mut app: App<'a, 'b>) -> App<'a, 'b> {
    macro_rules! add_subcommand {
        ($name:ident) => {{
            let mut cmd = $name::make_app(App::new(stringify!($name).replace("_", "-").as_str()));

            // for legacy reasons
            if stringify!($name).starts_with("react_native_") {
                cmd = cmd.setting(AppSettings::Hidden);
            }

            app = app.subcommand(cmd);
        }};
    }

    each_subcommand!(add_subcommand);
    app
}

#[allow(clippy::cyclomatic_complexity)]
fn run_command(matches: &ArgMatches<'_>) -> Result<(), Error> {
    macro_rules! execute_subcommand {
        ($name:ident) => {{
            let cmd = stringify!($name).replace("_", "-");
            if let Some(sub_matches) = matches.subcommand_matches(&cmd) {
                let rv = $name::execute(&sub_matches)?;
                if UPDATE_NAGGER_CMDS.iter().any(|x| x == &cmd) {
                    run_sentrycli_update_nagger();
                }
                return Ok(rv);
            }
        }};
    }

    each_subcommand!(execute_subcommand);
    unreachable!();
}

struct DebugArgs<'a>(Vec<&'a str>);

impl<'a> fmt::Display for DebugArgs<'a> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        for (idx, arg) in self.0.iter().enumerate() {
            if idx > 0 {
                write!(f, " ")?;
            }
            write!(f, "{:?}", arg)?;
        }
        Ok(())
    }
}

/// Given an argument vector and a `Config` this executes the
/// command line and returns the result.
pub fn execute(args: &[String]) -> Result<(), Error> {
    let mut config = Config::from_cli_config()?;

    // special case for the xcode integration for react native.  For more
    // information see commands/react_native_xcode.rs
    if preexecute_hooks()? {
        return Ok(());
    }

    let mut app = App::new("sentry-cli")
        .help_message("Print this help message.")
        .version(VERSION)
        .version_message("Print version information.")
        .about(ABOUT)
        .max_term_width(100)
        .setting(AppSettings::VersionlessSubcommands)
        .setting(AppSettings::SubcommandRequiredElseHelp)
        .global_setting(AppSettings::UnifiedHelpMessage)
        .arg(Arg::with_name("url").value_name("URL").long("url").help(
            "Fully qualified URL to the Sentry server.{n}\
             [defaults to https://sentry.io/]",
        ))
        .arg(
            Arg::with_name("auth_token")
                .value_name("AUTH_TOKEN")
                .long("auth-token")
                .help("Use the given Sentry auth token."),
        )
        .arg(
            Arg::with_name("api_key")
                .value_name("API_KEY")
                .long("api-key")
                .help("The given Sentry API key."),
        )
        .arg(
            Arg::with_name("log_level")
                .value_name("LOG_LEVEL")
                .long("log-level")
                .global(true)
                .help(
                    "Set the log output verbosity.{n}\
                     [valid levels: TRACE, DEBUG, INFO, WARN, ERROR]",
                ),
        );

    app = add_commands(app);
    let matches = app.get_matches_from_safe(&args[..])?;
    configure_args(&mut config, &matches)?;

    // bind the config to the process and fetch an immutable reference to it
    config.bind_to_process();
    info!(
        "Loaded config from {}",
        Config::get_current().get_filename().display()
    );

    info!(
        "sentry-cli was invoked with the following command line: {}",
        DebugArgs(args.iter().map(|x| x.as_str()).collect())
    );

    run_command(&matches)
}

fn run() -> Result<(), Error> {
    prepare_environment();
    match execute(&env::args().collect::<Vec<String>>()) {
        Ok(()) => Ok(()),
        Err(err) => {
            // if the user hit an error, it might be time to run the update
            // nagger because maybe they tried to do something only newer
            // versions support.
            debug!("error: running update nagger");
            run_sentrycli_update_nagger();
            Err(err)
        }
    }
}

fn setup() {
    use crate::utils::logging::Logger;

    crate::utils::system::init_backtrace();
    crate::utils::system::load_dotenv();

    // we use debug internally but our log handler then rejects to a lower limit.
    // This is okay for our uses but not as efficient.
    log::set_max_level(log::LevelFilter::Debug);

    // if we work with crash reporting we initialize the sentry system.  This
    // also configures the logger.
    #[cfg(feature = "with_crash_reporting")]
    {
        crate::utils::crashreporting::setup(Box::new(Logger));
    }
    #[cfg(not(feature = "with_crash_reporting"))]
    {
        static LOGGER: Logger = Logger;
        log::set_logger(&Logger);
    }
}

/// Executes the command line application and exists the process.
pub fn main() {
    setup();
    let result = run();

    if let Some(api) = Api::get_current_opt() {
        api.reset();
    }

    match result {
        Ok(()) => process::exit(0),
        Err(err) => {
            if let Some(&QuietExit(code)) = err.downcast_ref() {
                process::exit(code);
            } else {
                print_error(&err);
                #[cfg(feature = "with_crash_reporting")]
                {
                    crate::utils::crashreporting::try_report_to_sentry(&err);
                }
                process::exit(1);
            }
        }
    }
}
<commit_msg>feat: Log platform and arch (#450)<commit_after>//! This module implements the root command of the CLI tool.

use std::env;
use std::fmt;
use std::process;

use clap::{App, AppSettings, Arg, ArgMatches};
use failure::{bail, Error};
use log::{debug, info};

use crate::api::Api;
use crate::config::{prepare_environment, Auth, Config};
use crate::constants::{ARCH, PLATFORM, VERSION};
use crate::utils::system::{print_error, QuietExit};
use crate::utils::update::run_sentrycli_update_nagger;

const ABOUT: &str = "
Command line utility for Sentry.

This tool helps you manage remote resources on a Sentry server like
sourcemaps, debug symbols or releases.  Use `--help` on the subcommands
to learn more about them.";

macro_rules! each_subcommand {
    ($mac:ident) => {
        $mac!(upload_dif);
        $mac!(upload_dsym);
        $mac!(upload_proguard);
        $mac!(releases);
        $mac!(issues);
        $mac!(repos);
        $mac!(projects);
        #[cfg(not(feature = "managed"))]
        $mac!(update);
        #[cfg(not(feature = "managed"))]
        $mac!(uninstall);
        $mac!(info);
        $mac!(login);
        $mac!(send_event);
        $mac!(react_native);
        $mac!(difutil);
        $mac!(bash_hook);

        // these here exist for legacy reasons only.  They were moved
        // to subcommands of the react-native command.  Note that
        // codepush was never available on that level.
        #[cfg(target_os = "macos")]
        $mac!(react_native_xcode);
        $mac!(react_native_gradle);
    };
}

// commands we want to run the update nagger on
const UPDATE_NAGGER_CMDS: &[&str] = &[
    "releases", "issues", "repos", "projects", "info", "login", "difutil",
];

// it would be great if this could be a macro expansion as well
// but rust bug #37663 breaks location information then.
pub mod bash_hook;
pub mod info;
pub mod issues;
pub mod login;
pub mod projects;
pub mod releases;
pub mod repos;
pub mod send_event;
pub mod uninstall;
pub mod update;
pub mod upload_dif;
pub mod upload_dsym;
pub mod upload_proguard;

pub mod react_native;
pub mod react_native_appcenter;
pub mod react_native_codepush;
pub mod react_native_gradle;
#[cfg(target_os = "macos")]
pub mod react_native_xcode;

pub mod difutil;
pub mod difutil_check;
pub mod difutil_find;
pub mod difutil_id;

fn preexecute_hooks() -> Result<bool, Error> {
    return sentry_react_native_xcode_wrap();

    #[cfg(target_os = "macos")]
    fn sentry_react_native_xcode_wrap() -> Result<bool, Error> {
        if let Ok(val) = env::var("__SENTRY_RN_WRAP_XCODE_CALL") {
            env::remove_var("__SENTRY_RN_WRAP_XCODE_CALL");
            if &val == "1" {
                react_native_xcode::wrap_call()?;
                return Ok(true);
            }
        }
        Ok(false)
    }

    #[cfg(not(target_os = "macos"))]
    fn sentry_react_native_xcode_wrap() -> Result<bool, Error> {
        Ok(false)
    }
}

fn configure_args(config: &mut Config, matches: &ArgMatches<'_>) -> Result<(), Error> {
    if let Some(url) = matches.value_of("url") {
        config.set_base_url(url);
    }

    if let Some(api_key) = matches.value_of("api_key") {
        config.set_auth(Auth::Key(api_key.to_owned()));
    }

    if let Some(auth_token) = matches.value_of("auth_token") {
        config.set_auth(Auth::Token(auth_token.to_owned()));
    }

    if let Some(level_str) = matches.value_of("log_level") {
        match level_str.parse() {
            Ok(level) => {
                config.set_log_level(level);
            }
            Err(_) => {
                bail!("Unknown log level: {}", level_str);
            }
        }
    }

    Ok(())
}

fn add_commands<'a, 'b>(mut app: App<'a, 'b>) -> App<'a, 'b> {
    macro_rules! add_subcommand {
        ($name:ident) => {{
            let mut cmd = $name::make_app(App::new(stringify!($name).replace("_", "-").as_str()));

            // for legacy reasons
            if stringify!($name).starts_with("react_native_") {
                cmd = cmd.setting(AppSettings::Hidden);
            }

            app = app.subcommand(cmd);
        }};
    }

    each_subcommand!(add_subcommand);
    app
}

#[allow(clippy::cyclomatic_complexity)]
fn run_command(matches: &ArgMatches<'_>) -> Result<(), Error> {
    macro_rules! execute_subcommand {
        ($name:ident) => {{
            let cmd = stringify!($name).replace("_", "-");
            if let Some(sub_matches) = matches.subcommand_matches(&cmd) {
                let rv = $name::execute(&sub_matches)?;
                if UPDATE_NAGGER_CMDS.iter().any(|x| x == &cmd) {
                    run_sentrycli_update_nagger();
                }
                return Ok(rv);
            }
        }};
    }

    each_subcommand!(execute_subcommand);
    unreachable!();
}

struct DebugArgs<'a>(Vec<&'a str>);

impl<'a> fmt::Display for DebugArgs<'a> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        for (idx, arg) in self.0.iter().enumerate() {
            if idx > 0 {
                write!(f, " ")?;
            }
            write!(f, "{:?}", arg)?;
        }
        Ok(())
    }
}

/// Given an argument vector and a `Config` this executes the
/// command line and returns the result.
pub fn execute(args: &[String]) -> Result<(), Error> {
    let mut config = Config::from_cli_config()?;

    // special case for the xcode integration for react native.  For more
    // information see commands/react_native_xcode.rs
    if preexecute_hooks()? {
        return Ok(());
    }

    let mut app = App::new("sentry-cli")
        .help_message("Print this help message.")
        .version(VERSION)
        .version_message("Print version information.")
        .about(ABOUT)
        .max_term_width(100)
        .setting(AppSettings::VersionlessSubcommands)
        .setting(AppSettings::SubcommandRequiredElseHelp)
        .global_setting(AppSettings::UnifiedHelpMessage)
        .arg(Arg::with_name("url").value_name("URL").long("url").help(
            "Fully qualified URL to the Sentry server.{n}\
             [defaults to https://sentry.io/]",
        ))
        .arg(
            Arg::with_name("auth_token")
                .value_name("AUTH_TOKEN")
                .long("auth-token")
                .help("Use the given Sentry auth token."),
        )
        .arg(
            Arg::with_name("api_key")
                .value_name("API_KEY")
                .long("api-key")
                .help("The given Sentry API key."),
        )
        .arg(
            Arg::with_name("log_level")
                .value_name("LOG_LEVEL")
                .long("log-level")
                .global(true)
                .help(
                    "Set the log output verbosity.{n}\
                     [valid levels: TRACE, DEBUG, INFO, WARN, ERROR]",
                ),
        );

    app = add_commands(app);
    let matches = app.get_matches_from_safe(&args[..])?;
    configure_args(&mut config, &matches)?;

    // bind the config to the process and fetch an immutable reference to it
    config.bind_to_process();
    info!(
        "Loaded config from {}",
        Config::get_current().get_filename().display()
    );

    debug!(
        "sentry-cli version: {}, platform: \"{}\", architecture: \"{}\"",
        VERSION, PLATFORM, ARCH
    );

    info!(
        "sentry-cli was invoked with the following command line: {}",
        DebugArgs(args.iter().map(|x| x.as_str()).collect())
    );

    run_command(&matches)
}

fn run() -> Result<(), Error> {
    prepare_environment();
    match execute(&env::args().collect::<Vec<String>>()) {
        Ok(()) => Ok(()),
        Err(err) => {
            // if the user hit an error, it might be time to run the update
            // nagger because maybe they tried to do something only newer
            // versions support.
            debug!("error: running update nagger");
            run_sentrycli_update_nagger();
            Err(err)
        }
    }
}

fn setup() {
    use crate::utils::logging::Logger;

    crate::utils::system::init_backtrace();
    crate::utils::system::load_dotenv();

    // we use debug internally but our log handler then rejects to a lower limit.
    // This is okay for our uses but not as efficient.
    log::set_max_level(log::LevelFilter::Debug);

    // if we work with crash reporting we initialize the sentry system.  This
    // also configures the logger.
    #[cfg(feature = "with_crash_reporting")]
    {
        crate::utils::crashreporting::setup(Box::new(Logger));
    }
    #[cfg(not(feature = "with_crash_reporting"))]
    {
        static LOGGER: Logger = Logger;
        log::set_logger(&Logger);
    }
}

/// Executes the command line application and exits the process.
pub fn main() {
    setup();
    let result = run();

    if let Some(api) = Api::get_current_opt() {
        api.reset();
    }

    match result {
        Ok(()) => process::exit(0),
        Err(err) => {
            if let Some(&QuietExit(code)) = err.downcast_ref() {
                process::exit(code);
            } else {
                print_error(&err);
                #[cfg(feature = "with_crash_reporting")]
                {
                    crate::utils::crashreporting::try_report_to_sentry(&err);
                }
                process::exit(1);
            }
        }
    }
}
<|endoftext|>


<commit_before>//! Type represents a data type. Its implementation is pluggable.
//! So, Types are loaded from the packages.
//!

use std::fmt;
use std::mem;
use std::cmp::Ordering;
use std::rc::Rc;

use itertools::Itertools;

use err::Result;
use page::Chunk;

pub type TypeFactory = Rc<Fn(&str) -> Result<Ty>>;

pub mod name {
  pub static BOOL: &'static str = "bool";
  pub static U8: &'static str = "u8";
  pub static I8: &'static str = "i8";
  pub static I16: &'static str = "i16";
  pub static I32: &'static str = "i32";
  pub static I64: &'static str = "i64";
  pub static F32: &'static str = "f32";
  pub static F64: &'static str = "f64";
  pub static Array: &'static str = "Array";
  pub static Tuple: &'static str = "Tuple";
}

pub static BOOL: &'static Ty = &Ty::Bool;
pub static U8: &'static Ty = &Ty::U8;
pub static I8: &'static Ty = &Ty::I8;
pub static I16: &'static Ty = &Ty::I16;
pub static I32: &'static Ty = &Ty::I32;
pub static I64: &'static Ty = &Ty::I64;
pub static F32: &'static Ty = &Ty::F32;
pub static F64: &'static Ty = &Ty::F64;

/// Types
///
/// * Bool
/// * U8 : unsigned 8 bit integer
/// * I8 : signed 8 bit integer
/// * I16: signed 16 bit integer
/// * I32: signed 32 bit integer
/// * I64: signed 64 bit integer
/// * F32: single precision floating type
/// * F64: double precision floating type
/// * Array: multi-dimensional array of same elements
/// * Tuple: an ordered list of arbtrary type values
#[derive(Clone, PartialEq, PartialOrd, Eq, Ord, Debug)]
pub enum Ty {
  Bool,
  U8,
  I8,
  I16,
  I32,
  I64,
  F32,
  F64,
  Array(Box<Ty>, Vec<usize>),
  Tuple(Vec<Ty>),
}

impl Ty {
  /// Get a base name
  pub fn base(&self) -> &str {
    match *self {
      Ty::Bool => name::BOOL,
      Ty::U8 => name::U8,
      Ty::I8 => name::I8,
      Ty::I16 => name::I16,
      Ty::I32 => name::I32,
      Ty::I64 => name::I64,
      Ty::F32 => name::F32,
      Ty::F64 => name::F64,
      Ty::Array(..) => panic!("unsupported type: vector"),
      Ty::Tuple(_) => panic!("unsupported type: tuple"),
    }
  }

  #[inline(always)]
  pub fn is_int(&self) -> bool {
    self.is_sint() || self.is_uint()
  }

  pub fn is_sint(&self) -> bool {
    match *self {
      Ty::Bool |
      Ty::I8 |
      Ty::I16 |
      Ty::I32 |
      Ty::I64 => true,
      _ => false,
    }
  }

  pub fn is_uint(&self) -> bool {
    *self == Ty::U8
  }

  pub fn is_float(&self) -> bool {
    *self == Ty::F32 || *self == Ty::F64
  }

  pub fn size_of(&self) -> usize {
    match *self {
      Ty::Bool => 1,
      Ty::U8 | Ty::I8 => 1,
      Ty::I16 => 2,
      Ty::I32 | Ty::F32 => 4,
      Ty::I64 | Ty::F64 => 8,
      Ty::Array(ref ty, ref dims) => dims.iter().fold(0, |acc, dim| acc + (ty.size_of() * dim)),
      Ty::Tuple(ref tys) => tys.iter().fold(0, |acc, ty| acc + ty.size_of()),
    }
  }

  pub fn is_variable(&self) -> bool {
    false
  }
}

pub fn u(size: u32) -> &'static Ty {
  match size {
    8 => U8,
    _ => panic!("unsupported integer type: i{}", size),
  }
}

pub fn i(size: u32) -> &'static Ty {
  match size {
    8 => I8,
    16 => I16,
    32 => I32,
    64 => I64,
    _ => panic!("unsupported integer type: i{}", size),
  }
}

pub fn f(size: u32) -> &'static Ty {
  match size {
    32 => F32,
    64 => F64,
    _ => panic!("unsupported integer type: f{}", size),
  }
}

/// Typed implementation
pub trait HasType {
  fn ty(&self) -> &Ty;
}

impl fmt::Display for Ty {
  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
    match *self {
      Ty::Bool => write!(f, "bool"),
      Ty::I8 => write!(f, "i8"),
      Ty::U8 => write!(f, "u8"),
      Ty::I16 => write!(f, "i16"),
      Ty::I32 => write!(f, "i32"),
      Ty::I64 => write!(f, "i64"),
      Ty::F32 => write!(f, "f32"),
      Ty::F64 => write!(f, "f64"),
      Ty::Array(ref ty, ref dims) => {
        write!(f,
               "Array({},{})",
               ty,
               dims.iter().map(|dim| format!("{}", dim)).join(","))
      }
      Ty::Tuple(ref types) => {
        write!(f,
               "({})",
               types.iter().map(|dim| format!("{}", dim)).join(","))
      }
    }
  }
}

// Determine a result data type from two expression data types.
// #[allow(unused_variables)]
// pub fn result_data_ty(lhs_ty: &Ty, rhs_ty: &Ty) -> Ty {
// match *lhs_ty {
// Ty::Bool => {
// match *rhs_ty {
// Ty::Bool => rhs_ty.clone(),
// _ => panic!("Undefined Operator")
// }
// },
//
//
// Ty::Int1 => {
// match *rhs_ty {
// Ty::Int1   |
// Ty::Int2   |
// Ty::Int4   |
// Ty::Int8   |
// Ty::Float4 |
// Ty::Float8 => rhs_ty.clone(),
// _ => panic!("Undefined Operator")
// }
// },
//
// Ty::Int2 => {
// match *rhs_ty {
// Ty::Int2   |
// Ty::Int4   |
// Ty::Int8   |
// Ty::Float4 |
// Ty::Float8 => rhs_ty.clone(),
//
// Ty::Int1   => lhs_ty.clone(),
// _ => panic!("Undefined Operator")
// }
// },
//
// Ty::Int4 => {
// match *rhs_ty {
// Ty::Int4   |
// Ty::Int8   |
// Ty::Float4 |
// Ty::Float8 => rhs_ty.clone(),
//
// Ty::Int1 |
// Ty::Int2 => lhs_ty.clone(),
//
// _ => panic!("Undefined Operator")
// }
// },
//
// Ty::Int8 => {
// match *rhs_ty {
// Ty::Int8   |
// Ty::Float4 |
// Ty::Float8 => rhs_ty.clone(),
//
// Ty::Int1 |
// Ty::Int2 |
// Ty::Int4 => lhs_ty.clone(),
//
// _ => panic!("Undefined Operator")
// }
// },
//
// Ty::Float4 => {
// match *rhs_ty {
// Ty::Float4 |
// Ty::Float8 => rhs_ty.clone(),
//
// Ty::Int1 |
// Ty::Int2 |
// Ty::Int4 |
// Ty::Int8 => lhs_ty.clone(),
//
// _ => panic!("Undefined Operator")
// }
// },
//
// Ty::Float8 => {
// match *rhs_ty {
// Ty::Float8 => rhs_ty.clone(),
//
// Ty::Int1   |
// Ty::Int2   |
// Ty::Int4   |
// Ty::Int8   |
// Ty::Float4 => lhs_ty.clone(),
//
// _ => panic!("Undefined Operator")
// }
// },
//
// Ty::Numeric(ref p, ref s) => {
// panic!("Undefined operator")
// },
//
// Ty::Date => {
// panic!("Undefined Operator")
// },
//
// Ty::Time => {
// panic!("Undefined Operator")
// },
//
// Ty::Timez => {
// panic!("Undefined Operator")
// },
//
// Ty::Timestamp => {
// panic!("Undefined Operator")
// },
//
// Ty::Timestampz => {
// panic!("Undefined Operator")
// },
//
// Ty::Interval => {
// panic!("Undefined Operator")
// },
//
// Ty::Char(ref len) => {
// panic!("Undefined Operator")
// },
//
// Ty::Binary(ref len) => {
// panic!("Undefined Operator")
// },
//
// Ty::Clob => {
// match *rhs_ty {
// Ty::Clob => rhs_ty.clone(),
// _ => panic!("Undefined Operator")
// }
// },
//
// Ty::Blob => {
// match *rhs_ty {
// Ty::Blob => rhs_ty.clone(),
// _ => panic!("Undefined Operator")
// }
// },
//
// Ty::Array(ref ty) => {
// panic!("Undefined Operator")
// }
//
// Ty::Struct(ref tys) => {
// panic!("Undefined Operator")
// },
//
// Ty::Map(ref kty, ref vty) => {
// panic!("Undefined Operator")
// }
// }
// }
//

// pub fn bool_ty() -> Ty {
// let handler = TypeHandler {
// create_minipage: Rc::new(|| -> Box<MiniPage> {
// Box::new(FMiniPage::new(mem::size_of::<bool>()))
// })
// };
//
// Ty::new(BOOL_STR, true, true, handler)
// }
//
// pub fn i8_ty() -> Ty {
// let handler = TypeHandler {
// create_minipage: Rc::new(|| -> Box<MiniPage> {
// Box::new(FMiniPage::new(mem::size_of::<i8>()))
// })
// };
//
// Ty::new(I8_STR, true, true, handler)
// }
//
// pub fn i16_ty() -> Ty {
// let handler = TypeHandler {
// create_minipage: Rc::new(|| -> Box<MiniPage> {
// Box::new(FMiniPage::new(mem::size_of::<i16>()))
// })
// };
//
// Ty::new(I32_STR, true, true, handler)
// }
//
// pub fn i32_ty() -> Ty {
// let handler = TypeHandler {
// create_minipage: Rc::new(|| -> Box<MiniPage> {
// Box::new(FMiniPage::new(mem::size_of::<i32>()))
// })
// };
//
// Ty::new(I32_STR, true, true, handler)
// }
//
// pub fn i64_ty() -> Ty {
// let handler = TypeHandler {
// create_minipage: Rc::new(|| -> Box<MiniPage> {
// Box::new(FMiniPage::new(mem::size_of::<i64>()))
// })
// };
//
// Ty::new(I64_STR, true, true, handler)
// }
//
// pub fn f32_ty() -> Ty {
// let handler = TypeHandler {
// create_minipage: Rc::new(|| -> Box<MiniPage> {
// Box::new(FMiniPage::new(mem::size_of::<f32>()))
// })
// };
//
// Ty::new(F32_STR, true, true, handler)
// }
//
// pub fn f64_ty() -> Ty {
// let handler = TypeHandler {
// create_minipage: Rc::new(|| -> Box<MiniPage> {
// Box::new(FMiniPage::new(mem::size_of::<f64>()))
// })
// };
//
// Ty::new(F64_STR, true, true, handler)
// }
//

#[cfg(test)]
mod tests {
  use super::*;
  use libc::c_void;

  fn assert_display(ty: Ty, expected: &str) {
    assert_eq!(&format!("{}", ty), expected)
  }

  #[test]
  fn test_display_agg_types() {
    assert_display(Ty::Array(Box::new(Ty::F64), vec![4, 4]), "Array(f64,4,4)");
    assert_display(Ty::Tuple(vec![Ty::F64, Ty::F64, Ty::I64]), "(f64,f64,i64)");
  }
}
<commit_msg>Implement Array and Tuple factor functions.<commit_after>//! Type represents a data type. Its implementation is pluggable.
//! So, Types are loaded from the packages.
//!

use std::fmt;
use std::mem;
use std::cmp::Ordering;
use std::rc::Rc;

use itertools::Itertools;

use err::Result;
use page::Chunk;

pub type TypeFactory = Rc<Fn(&str) -> Result<Ty>>;

pub mod name {
  pub static BOOL: &'static str = "bool";
  pub static U8: &'static str = "u8";
  pub static I8: &'static str = "i8";
  pub static I16: &'static str = "i16";
  pub static I32: &'static str = "i32";
  pub static I64: &'static str = "i64";
  pub static F32: &'static str = "f32";
  pub static F64: &'static str = "f64";
  pub static Array: &'static str = "Array";
  pub static Tuple: &'static str = "Tuple";
}

pub static BOOL: &'static Ty = &Ty::Bool;
pub static U8: &'static Ty = &Ty::U8;
pub static I8: &'static Ty = &Ty::I8;
pub static I16: &'static Ty = &Ty::I16;
pub static I32: &'static Ty = &Ty::I32;
pub static I64: &'static Ty = &Ty::I64;
pub static F32: &'static Ty = &Ty::F32;
pub static F64: &'static Ty = &Ty::F64;

/// Types
///
/// * Bool
/// * U8 : unsigned 8 bit integer
/// * I8 : signed 8 bit integer
/// * I16: signed 16 bit integer
/// * I32: signed 32 bit integer
/// * I64: signed 64 bit integer
/// * F32: single precision floating type
/// * F64: double precision floating type
/// * Array: multi-dimensional array of same elements
/// * Tuple: an ordered list of arbtrary type values
#[derive(Clone, PartialEq, PartialOrd, Eq, Ord, Debug)]
pub enum Ty {
  Bool,
  U8,
  I8,
  I16,
  I32,
  I64,
  F32,
  F64,
  Array(Box<Ty>, Vec<usize>),
  Tuple(Vec<Ty>),
}

impl Ty {
  /// Get a base name
  pub fn base(&self) -> &str {
    match *self {
      Ty::Bool => name::BOOL,
      Ty::U8 => name::U8,
      Ty::I8 => name::I8,
      Ty::I16 => name::I16,
      Ty::I32 => name::I32,
      Ty::I64 => name::I64,
      Ty::F32 => name::F32,
      Ty::F64 => name::F64,
      Ty::Array(..) => panic!("unsupported type: vector"),
      Ty::Tuple(_) => panic!("unsupported type: tuple"),
    }
  }

  #[inline(always)]
  pub fn is_int(&self) -> bool {
    self.is_sint() || self.is_uint()
  }

  pub fn is_sint(&self) -> bool {
    match *self {
      Ty::Bool |
      Ty::I8 |
      Ty::I16 |
      Ty::I32 |
      Ty::I64 => true,
      _ => false,
    }
  }

  pub fn is_uint(&self) -> bool {
    *self == Ty::U8
  }

  pub fn is_float(&self) -> bool {
    *self == Ty::F32 || *self == Ty::F64
  }

  pub fn size_of(&self) -> usize {
    match *self {
      Ty::Bool => 1,
      Ty::U8 | Ty::I8 => 1,
      Ty::I16 => 2,
      Ty::I32 | Ty::F32 => 4,
      Ty::I64 | Ty::F64 => 8,
      Ty::Array(ref ty, ref dims) => dims.iter().fold(0, |acc, dim| acc + (ty.size_of() * dim)),
      Ty::Tuple(ref tys) => tys.iter().fold(0, |acc, ty| acc + ty.size_of()),
    }
  }

  pub fn is_variable(&self) -> bool {
    false
  }
}

pub fn u(size: u32) -> &'static Ty {
  match size {
    8 => U8,
    _ => panic!("unsupported integer type: i{}", size),
  }
}

pub fn i(size: u32) -> &'static Ty {
  match size {
    8 => I8,
    16 => I16,
    32 => I32,
    64 => I64,
    _ => panic!("unsupported integer type: i{}", size),
  }
}

pub fn f(size: u32) -> &'static Ty {
  match size {
    32 => F32,
    64 => F64,
    _ => panic!("unsupported integer type: f{}", size),
  }
}

pub fn Array(ty: &Ty, dims: &[usize]) -> Ty {
  Ty::Array(Box::new(ty.clone()), dims.to_vec())
}

pub fn Tuple(types: &[&Ty]) -> Ty {
  Ty::Tuple(types.iter().map(|ty| (*ty).clone()).collect::<Vec<Ty>>())
}

/// Typed implementation
pub trait HasType {
  fn ty(&self) -> &Ty;
}

impl fmt::Display for Ty {
  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
    match *self {
      Ty::Bool => write!(f, "bool"),
      Ty::I8 => write!(f, "i8"),
      Ty::U8 => write!(f, "u8"),
      Ty::I16 => write!(f, "i16"),
      Ty::I32 => write!(f, "i32"),
      Ty::I64 => write!(f, "i64"),
      Ty::F32 => write!(f, "f32"),
      Ty::F64 => write!(f, "f64"),
      Ty::Array(ref ty, ref dims) => {
        write!(f,
               "Array({},{})",
               ty,
               dims.iter().map(|dim| format!("{}", dim)).join(","))
      }
      Ty::Tuple(ref types) => {
        write!(f,
               "({})",
               types.iter().map(|dim| format!("{}", dim)).join(","))
      }
    }
  }
}

// Determine a result data type from two expression data types.
// #[allow(unused_variables)]
// pub fn result_data_ty(lhs_ty: &Ty, rhs_ty: &Ty) -> Ty {
// match *lhs_ty {
// Ty::Bool => {
// match *rhs_ty {
// Ty::Bool => rhs_ty.clone(),
// _ => panic!("Undefined Operator")
// }
// },
//
//
// Ty::Int1 => {
// match *rhs_ty {
// Ty::Int1   |
// Ty::Int2   |
// Ty::Int4   |
// Ty::Int8   |
// Ty::Float4 |
// Ty::Float8 => rhs_ty.clone(),
// _ => panic!("Undefined Operator")
// }
// },
//
// Ty::Int2 => {
// match *rhs_ty {
// Ty::Int2   |
// Ty::Int4   |
// Ty::Int8   |
// Ty::Float4 |
// Ty::Float8 => rhs_ty.clone(),
//
// Ty::Int1   => lhs_ty.clone(),
// _ => panic!("Undefined Operator")
// }
// },
//
// Ty::Int4 => {
// match *rhs_ty {
// Ty::Int4   |
// Ty::Int8   |
// Ty::Float4 |
// Ty::Float8 => rhs_ty.clone(),
//
// Ty::Int1 |
// Ty::Int2 => lhs_ty.clone(),
//
// _ => panic!("Undefined Operator")
// }
// },
//
// Ty::Int8 => {
// match *rhs_ty {
// Ty::Int8   |
// Ty::Float4 |
// Ty::Float8 => rhs_ty.clone(),
//
// Ty::Int1 |
// Ty::Int2 |
// Ty::Int4 => lhs_ty.clone(),
//
// _ => panic!("Undefined Operator")
// }
// },
//
// Ty::Float4 => {
// match *rhs_ty {
// Ty::Float4 |
// Ty::Float8 => rhs_ty.clone(),
//
// Ty::Int1 |
// Ty::Int2 |
// Ty::Int4 |
// Ty::Int8 => lhs_ty.clone(),
//
// _ => panic!("Undefined Operator")
// }
// },
//
// Ty::Float8 => {
// match *rhs_ty {
// Ty::Float8 => rhs_ty.clone(),
//
// Ty::Int1   |
// Ty::Int2   |
// Ty::Int4   |
// Ty::Int8   |
// Ty::Float4 => lhs_ty.clone(),
//
// _ => panic!("Undefined Operator")
// }
// },
//
// Ty::Numeric(ref p, ref s) => {
// panic!("Undefined operator")
// },
//
// Ty::Date => {
// panic!("Undefined Operator")
// },
//
// Ty::Time => {
// panic!("Undefined Operator")
// },
//
// Ty::Timez => {
// panic!("Undefined Operator")
// },
//
// Ty::Timestamp => {
// panic!("Undefined Operator")
// },
//
// Ty::Timestampz => {
// panic!("Undefined Operator")
// },
//
// Ty::Interval => {
// panic!("Undefined Operator")
// },
//
// Ty::Char(ref len) => {
// panic!("Undefined Operator")
// },
//
// Ty::Binary(ref len) => {
// panic!("Undefined Operator")
// },
//
// Ty::Clob => {
// match *rhs_ty {
// Ty::Clob => rhs_ty.clone(),
// _ => panic!("Undefined Operator")
// }
// },
//
// Ty::Blob => {
// match *rhs_ty {
// Ty::Blob => rhs_ty.clone(),
// _ => panic!("Undefined Operator")
// }
// },
//
// Ty::Array(ref ty) => {
// panic!("Undefined Operator")
// }
//
// Ty::Struct(ref tys) => {
// panic!("Undefined Operator")
// },
//
// Ty::Map(ref kty, ref vty) => {
// panic!("Undefined Operator")
// }
// }
// }
//

// pub fn bool_ty() -> Ty {
// let handler = TypeHandler {
// create_minipage: Rc::new(|| -> Box<MiniPage> {
// Box::new(FMiniPage::new(mem::size_of::<bool>()))
// })
// };
//
// Ty::new(BOOL_STR, true, true, handler)
// }
//
// pub fn i8_ty() -> Ty {
// let handler = TypeHandler {
// create_minipage: Rc::new(|| -> Box<MiniPage> {
// Box::new(FMiniPage::new(mem::size_of::<i8>()))
// })
// };
//
// Ty::new(I8_STR, true, true, handler)
// }
//
// pub fn i16_ty() -> Ty {
// let handler = TypeHandler {
// create_minipage: Rc::new(|| -> Box<MiniPage> {
// Box::new(FMiniPage::new(mem::size_of::<i16>()))
// })
// };
//
// Ty::new(I32_STR, true, true, handler)
// }
//
// pub fn i32_ty() -> Ty {
// let handler = TypeHandler {
// create_minipage: Rc::new(|| -> Box<MiniPage> {
// Box::new(FMiniPage::new(mem::size_of::<i32>()))
// })
// };
//
// Ty::new(I32_STR, true, true, handler)
// }
//
// pub fn i64_ty() -> Ty {
// let handler = TypeHandler {
// create_minipage: Rc::new(|| -> Box<MiniPage> {
// Box::new(FMiniPage::new(mem::size_of::<i64>()))
// })
// };
//
// Ty::new(I64_STR, true, true, handler)
// }
//
// pub fn f32_ty() -> Ty {
// let handler = TypeHandler {
// create_minipage: Rc::new(|| -> Box<MiniPage> {
// Box::new(FMiniPage::new(mem::size_of::<f32>()))
// })
// };
//
// Ty::new(F32_STR, true, true, handler)
// }
//
// pub fn f64_ty() -> Ty {
// let handler = TypeHandler {
// create_minipage: Rc::new(|| -> Box<MiniPage> {
// Box::new(FMiniPage::new(mem::size_of::<f64>()))
// })
// };
//
// Ty::new(F64_STR, true, true, handler)
// }
//

#[cfg(test)]
mod tests {
  use super::*;
  use libc::c_void;

  fn assert_display(ty: Ty, expected: &str) {
    assert_eq!(&format!("{}", ty), expected)
  }

  #[test]
  fn test_display_agg_types() {
    assert_display(Array(&Ty::F64, &[4, 4]), "Array(f64,4,4)");
    assert_display(Tuple(&[&Ty::F64, &Ty::F64, &Ty::I64]), "(f64,f64,i64)");
  }
}
<|endoftext|>