<commit_before>//! Main module to handle the layout.
//! This is where the i3-specific code is.

pub mod layout {
    use super::super::container::{Container, Handle, ContainerType};
    use super::super::tree::Node;
    use super::super::super::rustwlc::handle::{WlcView, WlcOutput};

    use std::sync::*;//{Mutex, MutexGuard, TryLockError};

    use std::ptr;

    pub type TreeResult = Result<(), TryLockError<MutexGuard<'static, Tree>>>;

    pub struct Tree {
        root: Node,
        active_container: *const Node,
    }

    impl Tree {
        fn get_active_container(&self) -> Option<&Node> {
            if self.active_container.is_null() {
                None
            } else {
                unsafe {
                    Some(&*self.active_container)
                }
            }
        }
    }

    unsafe impl Send for Tree {}

    lazy_static! {
        static ref TREE: Mutex<Tree> = {
            Mutex::new(Tree{
                root: Node::new(Container::new_root()),
                active_container: ptr::null(),
            })
        };
    }

    pub fn add_output(wlc_output: WlcOutput) -> TreeResult {
        {
            let mut root = try!(TREE.try_lock());
            let output = Container::new_output(wlc_output);
            root.root.new_child(output);
        }
        try!(add_workspace(&"1"));
        try!(switch_workspace(&"1"));
        Ok(())
    }

    pub fn add_workspace(name: &str) -> TreeResult {
        trace!("Adding new workspace to root");
        let mut root = try!(TREE.lock());
        let workspace = Container::new_workspace(name.to_string());
        // NOTE handle multiple outputs
        root.root.get_children_mut()[0].new_child(workspace);
        Ok(())
    }

    pub fn add_view(wlc_view: WlcView) -> TreeResult {
        let root = try!(TREE.lock());
        if let Some(current_workspace) = get_current_workspace(&root) {
            trace!("Adding view {:?} to {:?}", wlc_view, current_workspace);
            current_workspace.new_child(Container::new_view(wlc_view));
        }
        Ok(())
    }

    pub fn remove_view(wlc_view: &WlcView) -> TreeResult {
        let root = try!(TREE.lock());
        if let Some(view) = root.root.find_view_by_handle(&wlc_view) {
            let parent = view.get_parent().unwrap();
            parent.remove_child(view);
        }
        Ok(())
    }

    pub fn switch_workspace(name: &str) -> TreeResult {
        trace!("Switching to workspace {}", name);
        let mut root = try!(TREE.lock());
        if let Some(old_workspace) = get_current_workspace(&root) {
            // Make all the views in the original workspace to be invisible
            for view in old_workspace.get_children_mut() {
                trace!("Setting {:?} invisible", view);
                match view.get_val().get_handle().unwrap() {
                    Handle::View(view) => view.set_mask(0),
                    _ => {},
                }
            }
        }
        let current_workspace: *const Node;
        {
            let new_current_workspace: &mut Node;
            if let Some(_) = get_workspace_by_name(&root, name) {
                trace!("Found workspace {}", name);
                new_current_workspace = get_workspace_by_name_mut(&mut root, name).unwrap();
            } else {
                drop(root);
                try!(add_workspace(name));
                root = try!(TREE.lock());
                new_current_workspace = get_workspace_by_name_mut(&mut root, name).unwrap();
            }
            for view in new_current_workspace.get_children_mut() {
                trace!("Setting {:?} visible", view);
                match view.get_val().get_handle().unwrap() {
                    Handle::View(view) => view.set_mask(1),
                    _ => {},
                }
            }
            // Set the first view to be focused, so that the view is updated to this new workspace
            if new_current_workspace.get_children().len() > 0 {
                trace!("Focusing view");
                match new_current_workspace.get_children_mut()[0].get_val().get_handle().unwrap() {
                    Handle::View(view) => view.focus(),
                    _ => {},
                }
            } else {
                WlcView::root().focus();
            }
            current_workspace = new_current_workspace as *const Node;
        }
        root.active_container = current_workspace;
        Ok(())
    }

    /// Finds the WlcOutput associated with the WlcView from the tree
    pub fn get_output_of_view(wlc_view: &WlcView) -> Option<WlcOutput> {
        let root = TREE.lock().unwrap();
        if let Some(view_node) = root.root.find_view_by_handle(wlc_view) {
            if let Some(output_node) = view_node.get_ancestor_of_type(ContainerType::Output) {
                if let Some(handle) =  output_node.get_val().get_handle() {
                    return match handle {
                        Handle::Output(output) => Some(output),
                        _ => None
                    }
                }
            }
        }
        return None;
    }

    fn get_focused_workspace<'a>(root: &'a Tree) -> Option<&'a Node> {
        for output in root.root.get_children() {
            if output.get_val().is_focused() {
                for workspace in output.get_children() {
                    if workspace.get_val().is_focused() {
                        return Some(workspace);
                    }
                }
            }
        }
        None

    }

    fn get_current_workspace<'a>(root: &'a Tree) -> Option<&'a mut Node> {
        if let Some(container) = root.get_active_container() {
            //if let Some(child) = container.get_ancestor_of_type(ContainerType::Workspace) {
                //return child.get_children()[0].get_parent()

            //}
            // NOTE hack here, remove commented code above to make this work properly
            let parent = container.get_parent().unwrap();
            for child in parent.get_children_mut() {
                if child == container {
                    return Some(child);
                }
            }
        }
        return None
    }

    fn get_workspace_by_name<'a, 'b>(root: &'a Tree, name: &'b str) -> Option<&'a Node> {
        for child in root.root.get_children()[0].get_children() {
            if child.get_val().get_name().unwrap() != name {
                continue
            }
            return Some(child);
        }
        return None
    }

    fn get_workspace_by_name_mut<'a, 'b>(root: &'a mut Tree, name: &'b str) -> Option<&'a mut Node> {
        for child in root.root.get_children_mut()[0].get_children_mut() {
            if child.get_val().get_name().unwrap() != name {
                continue
            }
            return Some(child);
        }
        return None
    }
 }

<commit_msg>Update layout.rs<commit_after>//! Main module to handle the layout.
//! This is where the i3-specific code is.

pub mod layout {
    use super::super::container::{Container, Handle, ContainerType};
    use super::super::tree::Node;
    use super::super::super::rustwlc::handle::{WlcView, WlcOutput};

    use std::sync::{Mutex, MutexGuard, TryLockError};

    use std::ptr;

    pub type TreeResult = Result<(), TryLockError<MutexGuard<'static, Tree>>>;

    pub struct Tree {
        root: Node,
        active_container: *const Node,
    }

    impl Tree {
        fn get_active_container(&self) -> Option<&Node> {
            if self.active_container.is_null() {
                None
            } else {
                unsafe {
                    Some(&*self.active_container)
                }
            }
        }
    }

    unsafe impl Send for Tree {}

    lazy_static! {
        static ref TREE: Mutex<Tree> = {
            Mutex::new(Tree{
                root: Node::new(Container::new_root()),
                active_container: ptr::null(),
            })
        };
    }

    pub fn add_output(wlc_output: WlcOutput) -> TreeResult {
        {
            let mut root = try!(TREE.try_lock());
            let output = Container::new_output(wlc_output);
            root.root.new_child(output);
        }
        try!(add_workspace(&"1"));
        try!(switch_workspace(&"1"));
        Ok(())
    }

    pub fn add_workspace(name: &str) -> TreeResult {
        trace!("Adding new workspace to root");
        let mut root = try!(TREE.lock());
        let workspace = Container::new_workspace(name.to_string());
        // NOTE handle multiple outputs
        root.root.get_children_mut()[0].new_child(workspace);
        Ok(())
    }

    pub fn add_view(wlc_view: WlcView) -> TreeResult {
        let root = try!(TREE.lock());
        if let Some(current_workspace) = get_current_workspace(&root) {
            trace!("Adding view {:?} to {:?}", wlc_view, current_workspace);
            current_workspace.new_child(Container::new_view(wlc_view));
        }
        Ok(())
    }

    pub fn remove_view(wlc_view: &WlcView) -> TreeResult {
        let root = try!(TREE.lock());
        if let Some(view) = root.root.find_view_by_handle(&wlc_view) {
            let parent = view.get_parent().unwrap();
            parent.remove_child(view);
        }
        Ok(())
    }

    pub fn switch_workspace(name: &str) -> TreeResult {
        trace!("Switching to workspace {}", name);
        let mut root = try!(TREE.lock());
        if let Some(old_workspace) = get_current_workspace(&root) {
            // Make all the views in the original workspace to be invisible
            for view in old_workspace.get_children_mut() {
                trace!("Setting {:?} invisible", view);
                match view.get_val().get_handle().unwrap() {
                    Handle::View(view) => view.set_mask(0),
                    _ => {},
                }
            }
        }
        let current_workspace: *const Node;
        {
            let new_current_workspace: &mut Node;
            if let Some(_) = get_workspace_by_name(&root, name) {
                trace!("Found workspace {}", name);
                new_current_workspace = get_workspace_by_name_mut(&mut root, name).unwrap();
            } else {
                drop(root);
                try!(add_workspace(name));
                root = try!(TREE.lock());
                new_current_workspace = get_workspace_by_name_mut(&mut root, name).unwrap();
            }
            for view in new_current_workspace.get_children_mut() {
                trace!("Setting {:?} visible", view);
                match view.get_val().get_handle().unwrap() {
                    Handle::View(view) => view.set_mask(1),
                    _ => {},
                }
            }
            // Set the first view to be focused, so that the view is updated to this new workspace
            if new_current_workspace.get_children().len() > 0 {
                trace!("Focusing view");
                match new_current_workspace.get_children_mut()[0].get_val().get_handle().unwrap() {
                    Handle::View(view) => view.focus(),
                    _ => {},
                }
            } else {
                WlcView::root().focus();
            }
            current_workspace = new_current_workspace as *const Node;
        }
        root.active_container = current_workspace;
        Ok(())
    }

    /// Finds the WlcOutput associated with the WlcView from the tree
    pub fn get_output_of_view(wlc_view: &WlcView) -> Option<WlcOutput> {
        let root = TREE.lock().unwrap();
        if let Some(view_node) = root.root.find_view_by_handle(wlc_view) {
            if let Some(output_node) = view_node.get_ancestor_of_type(ContainerType::Output) {
                if let Some(handle) =  output_node.get_val().get_handle() {
                    return match handle {
                        Handle::Output(output) => Some(output),
                        _ => None
                    }
                }
            }
        }
        return None;
    }

    fn get_focused_workspace<'a>(root: &'a Tree) -> Option<&'a Node> {
        for output in root.root.get_children() {
            if output.get_val().is_focused() {
                for workspace in output.get_children() {
                    if workspace.get_val().is_focused() {
                        return Some(workspace);
                    }
                }
            }
        }
        None

    }

    fn get_current_workspace<'a>(root: &'a Tree) -> Option<&'a mut Node> {
        if let Some(container) = root.get_active_container() {
            //if let Some(child) = container.get_ancestor_of_type(ContainerType::Workspace) {
                //return child.get_children()[0].get_parent()

            //}
            // NOTE hack here, remove commented code above to make this work properly
            let parent = container.get_parent().unwrap();
            for child in parent.get_children_mut() {
                if child == container {
                    return Some(child);
                }
            }
        }
        return None
    }

    fn get_workspace_by_name<'a, 'b>(root: &'a Tree, name: &'b str) -> Option<&'a Node> {
        for child in root.root.get_children()[0].get_children() {
            if child.get_val().get_name().unwrap() != name {
                continue
            }
            return Some(child);
        }
        return None
    }

    fn get_workspace_by_name_mut<'a, 'b>(root: &'a mut Tree, name: &'b str) -> Option<&'a mut Node> {
        for child in root.root.get_children_mut()[0].get_children_mut() {
            if child.get_val().get_name().unwrap() != name {
                continue
            }
            return Some(child);
        }
        return None
    }
 }

<|endoftext|>


<commit_before>// See LICENSE file for copyright and license details.

use std::iter::{repeat};
use std::num::{Float, SignedInt};
use cgmath::{Vector};
use common::types::{Size2, ZInt, MapPos};
use dir::{Dir, DirIter, dirs};

#[derive(Clone)]
pub enum Terrain {
    Plain,
    Trees,
    // Swamp,
    Building,
}

// TODO: Replace with generic map: Map<PathfinderTile>
pub struct Map<T> {
    tiles: Vec<T>,
    size: Size2<ZInt>,
}

impl<T: Clone> Map<T> {
    // TODO: remove 'empty'
    pub fn new(size: &Size2<ZInt>, empty: T) -> Map<T> {
        let tiles_count = size.w * size.h;
        let tiles = repeat(empty).take(tiles_count as usize).collect();
        Map {
            tiles: tiles,
            size: size.clone(),
        }
    }

    pub fn size(&self) -> &Size2<ZInt> {
        &self.size
    }

    pub fn tile_mut(&mut self, pos: &MapPos) -> &mut T {
        assert!(self.is_inboard(pos));
        let index = self.size.w * pos.v.y + pos.v.x;
        &mut self.tiles[index as usize]
    }

    pub fn tile(&self, pos: &MapPos) -> &T {
        assert!(self.is_inboard(pos));
        let index = self.size.w * pos.v.y + pos.v.x;
        &self.tiles[index as usize]
    }

    pub fn is_inboard(&self, pos: &MapPos) -> bool {
        let x = pos.v.x;
        let y = pos.v.y;
        x >= 0 && y >= 0 && x < self.size.w && y < self.size.h
    }

    pub fn get_iter(&self) -> MapPosIter {
        MapPosIter::new(self.size())
    }
}

pub struct MapPosIter {
    cursor: MapPos,
    map_size: Size2<ZInt>,
}

impl MapPosIter {
    fn new(map_size: &Size2<ZInt>) -> MapPosIter {
        MapPosIter {
            cursor: MapPos{v: Vector::from_value(0)},
            map_size: map_size.clone(),
        }
    }
}

impl Iterator for MapPosIter {
    type Item = MapPos;

    fn next(&mut self) -> Option<MapPos> {
        let current_pos = if self.cursor.v.y >= self.map_size.h {
            None
        } else {
            Some(self.cursor.clone())
        };
        self.cursor.v.x += 1;
        if self.cursor.v.x >= self.map_size.w {
            self.cursor.v.x = 0;
            self.cursor.v.y += 1;
        }
        current_pos
    }
}

pub struct RingIter {
    cursor: MapPos,
    segment_index: ZInt,
    dir_iter: DirIter,
    radius: ZInt,
    dir: Dir,
}

pub fn ring_iter(pos: &MapPos, radius: ZInt) -> RingIter {
    let mut pos = pos.clone();
    pos.v.x -= radius;
    let mut dir_iter = dirs();
    let dir = dir_iter.next().unwrap();
    assert_eq!(dir, Dir::SouthEast);
    RingIter {
        cursor: pos.clone(),
        radius: radius,
        segment_index: 0,
        dir_iter: dir_iter,
        dir: dir,
    }
}

impl RingIter {
    fn simple_step(&mut self) -> Option<MapPos> {
        self.cursor = Dir::get_neighbour_pos(
            &self.cursor, &self.dir);
        self.segment_index += 1;
        Some(self.cursor.clone())
    }

    fn rotate(&mut self, dir: Dir) -> Option<MapPos> {
        self.segment_index = 0;
        self.cursor = Dir::get_neighbour_pos(&self.cursor, &self.dir);
        self.dir = dir;
        Some(self.cursor.clone())
    }
}

impl Iterator for RingIter {
    type Item = MapPos;

    fn next(&mut self) -> Option<MapPos> {
        if self.segment_index >= self.radius - 1 {
            if let Some(dir) = self.dir_iter.next() {
                self.rotate(dir)
            } else {
                if self.segment_index == self.radius {
                    None
                } else {
                    // last pos
                    self.simple_step()
                }
            }
        } else {
            self.simple_step()
        }
    }
}

pub struct SpiralIter {
    ring_iter: RingIter,
    radius: ZInt,
    last_radius: ZInt,
    origin: MapPos,
}

pub fn spiral_iter(pos: &MapPos, radius: ZInt) -> SpiralIter {
    assert!(radius >= 1);
    SpiralIter {
        ring_iter: ring_iter(pos, 1),
        radius: 1,
        last_radius: radius,
        origin: pos.clone(),
    }
}

impl Iterator for SpiralIter {
    type Item = MapPos;

    fn next(&mut self) -> Option<MapPos> {
        let pos = self.ring_iter.next();
        if pos.is_some() {
            pos
        } else {
            self.radius += 1;
            if self.radius > self.last_radius {
                None
            } else {
                self.ring_iter = ring_iter(
                    &self.origin, self.radius);
                self.ring_iter.next()
            }
        }
    }
}

pub fn distance(from: &MapPos, to: &MapPos) -> ZInt {
    let to = to.v;
    let from = from.v;
    let dx = (to.x + to.y / 2) - (from.x + from.y / 2);
    let dy = to.y - from.y;
    (dx.abs() + dy.abs() + (dx - dy).abs()) / 2
}

#[cfg(test)]
mod tests {
    use cgmath::{Vector2};
    use types::{MapPos};
    use super::{ring_iter, spiral_iter};

    #[test]
    fn test_ring_1() {
        let radius = 1;
        let start_pos = MapPos{v: Vector2{x: 0, y: 0}};
        let expected = [
            (0, -1), (1, -1), (1, 0), (1, 1), (0, 1), (-1, 0) ];
        let mut expected = expected.iter();
        for p in ring_iter(&start_pos, radius) {
            let expected = expected.next().expect(
                "Can not get next element from expected vector");
            assert_eq!(*expected, (p.v.x, p.v.y));
        }
        assert!(expected.next().is_none());
    }

    #[test]
    fn test_ring_2() {
        let radius = 2;
        let start_pos = MapPos{v: Vector2{x: 0, y: 0}};
        let expected = [
            (-1, -1),
            (-1, -2),
            (0, -2),
            (1, -2),
            (2, -1),
            (2, 0),
            (2, 1),
            (1, 2),
            (0, 2),
            (-1, 2),
            (-1, 1),
            (-2, 0),
        ];
        let mut expected = expected.iter();
        for p in ring_iter(&start_pos, radius) {
            let expected = expected.next().expect(
                "Can not get next element from expected vector");
            assert_eq!(*expected, (p.v.x, p.v.y));
        }
        assert!(expected.next().is_none());
    }

    #[test]
    fn test_spiral_1() {
        let radius = 2;
        let start_pos = MapPos{v: Vector2{x: 0, y: 0}};
        let expected = [
            // ring 1
            (0, -1),
            (1, -1),
            (1, 0),
            (1, 1),
            (0, 1),
            (-1, 0),
            // ring 2
            (-1, -1),
            (-1, -2),
            (0, -2),
            (1, -2),
            (2, -1),
            (2, 0),
            (2, 1),
            (1, 2),
            (0, 2),
            (-1, 2),
            (-1, 1),
            (-2, 0),
        ];
        let mut expected = expected.iter();
        for p in spiral_iter(&start_pos, radius) {
            let expected = expected.next().expect(
                "Can not get next element from expected vector");
            assert_eq!(*expected, (p.v.x, p.v.y));
        }
        assert!(expected.next().is_none());
    }
}

// vim: set tabstop=4 shiftwidth=4 softtabstop=4 expandtab:
<commit_msg>Fixed tests<commit_after>// See LICENSE file for copyright and license details.

use std::iter::{repeat};
use std::num::{Float, SignedInt};
use cgmath::{Vector};
use common::types::{Size2, ZInt, MapPos};
use dir::{Dir, DirIter, dirs};

#[derive(Clone)]
pub enum Terrain {
    Plain,
    Trees,
    // Swamp,
    Building,
}

// TODO: Replace with generic map: Map<PathfinderTile>
pub struct Map<T> {
    tiles: Vec<T>,
    size: Size2<ZInt>,
}

impl<T: Clone> Map<T> {
    // TODO: remove 'empty'
    pub fn new(size: &Size2<ZInt>, empty: T) -> Map<T> {
        let tiles_count = size.w * size.h;
        let tiles = repeat(empty).take(tiles_count as usize).collect();
        Map {
            tiles: tiles,
            size: size.clone(),
        }
    }

    pub fn size(&self) -> &Size2<ZInt> {
        &self.size
    }

    pub fn tile_mut(&mut self, pos: &MapPos) -> &mut T {
        assert!(self.is_inboard(pos));
        let index = self.size.w * pos.v.y + pos.v.x;
        &mut self.tiles[index as usize]
    }

    pub fn tile(&self, pos: &MapPos) -> &T {
        assert!(self.is_inboard(pos));
        let index = self.size.w * pos.v.y + pos.v.x;
        &self.tiles[index as usize]
    }

    pub fn is_inboard(&self, pos: &MapPos) -> bool {
        let x = pos.v.x;
        let y = pos.v.y;
        x >= 0 && y >= 0 && x < self.size.w && y < self.size.h
    }

    pub fn get_iter(&self) -> MapPosIter {
        MapPosIter::new(self.size())
    }
}

pub struct MapPosIter {
    cursor: MapPos,
    map_size: Size2<ZInt>,
}

impl MapPosIter {
    fn new(map_size: &Size2<ZInt>) -> MapPosIter {
        MapPosIter {
            cursor: MapPos{v: Vector::from_value(0)},
            map_size: map_size.clone(),
        }
    }
}

impl Iterator for MapPosIter {
    type Item = MapPos;

    fn next(&mut self) -> Option<MapPos> {
        let current_pos = if self.cursor.v.y >= self.map_size.h {
            None
        } else {
            Some(self.cursor.clone())
        };
        self.cursor.v.x += 1;
        if self.cursor.v.x >= self.map_size.w {
            self.cursor.v.x = 0;
            self.cursor.v.y += 1;
        }
        current_pos
    }
}

pub struct RingIter {
    cursor: MapPos,
    segment_index: ZInt,
    dir_iter: DirIter,
    radius: ZInt,
    dir: Dir,
}

pub fn ring_iter(pos: &MapPos, radius: ZInt) -> RingIter {
    let mut pos = pos.clone();
    pos.v.x -= radius;
    let mut dir_iter = dirs();
    let dir = dir_iter.next().unwrap();
    assert_eq!(dir, Dir::SouthEast);
    RingIter {
        cursor: pos.clone(),
        radius: radius,
        segment_index: 0,
        dir_iter: dir_iter,
        dir: dir,
    }
}

impl RingIter {
    fn simple_step(&mut self) -> Option<MapPos> {
        self.cursor = Dir::get_neighbour_pos(
            &self.cursor, &self.dir);
        self.segment_index += 1;
        Some(self.cursor.clone())
    }

    fn rotate(&mut self, dir: Dir) -> Option<MapPos> {
        self.segment_index = 0;
        self.cursor = Dir::get_neighbour_pos(&self.cursor, &self.dir);
        self.dir = dir;
        Some(self.cursor.clone())
    }
}

impl Iterator for RingIter {
    type Item = MapPos;

    fn next(&mut self) -> Option<MapPos> {
        if self.segment_index >= self.radius - 1 {
            if let Some(dir) = self.dir_iter.next() {
                self.rotate(dir)
            } else {
                if self.segment_index == self.radius {
                    None
                } else {
                    // last pos
                    self.simple_step()
                }
            }
        } else {
            self.simple_step()
        }
    }
}

pub struct SpiralIter {
    ring_iter: RingIter,
    radius: ZInt,
    last_radius: ZInt,
    origin: MapPos,
}

pub fn spiral_iter(pos: &MapPos, radius: ZInt) -> SpiralIter {
    assert!(radius >= 1);
    SpiralIter {
        ring_iter: ring_iter(pos, 1),
        radius: 1,
        last_radius: radius,
        origin: pos.clone(),
    }
}

impl Iterator for SpiralIter {
    type Item = MapPos;

    fn next(&mut self) -> Option<MapPos> {
        let pos = self.ring_iter.next();
        if pos.is_some() {
            pos
        } else {
            self.radius += 1;
            if self.radius > self.last_radius {
                None
            } else {
                self.ring_iter = ring_iter(
                    &self.origin, self.radius);
                self.ring_iter.next()
            }
        }
    }
}

pub fn distance(from: &MapPos, to: &MapPos) -> ZInt {
    let to = to.v;
    let from = from.v;
    let dx = (to.x + to.y / 2) - (from.x + from.y / 2);
    let dy = to.y - from.y;
    (dx.abs() + dy.abs() + (dx - dy).abs()) / 2
}

#[cfg(test)]
mod tests {
    use cgmath::{Vector2};
    use common::types::{MapPos};
    use super::{ring_iter, spiral_iter};

    #[test]
    fn test_ring_1() {
        let radius = 1;
        let start_pos = MapPos{v: Vector2{x: 0, y: 0}};
        let expected = [
            (0, -1), (1, -1), (1, 0), (1, 1), (0, 1), (-1, 0) ];
        let mut expected = expected.iter();
        for p in ring_iter(&start_pos, radius) {
            let expected = expected.next().expect(
                "Can not get next element from expected vector");
            assert_eq!(*expected, (p.v.x, p.v.y));
        }
        assert!(expected.next().is_none());
    }

    #[test]
    fn test_ring_2() {
        let radius = 2;
        let start_pos = MapPos{v: Vector2{x: 0, y: 0}};
        let expected = [
            (-1, -1),
            (-1, -2),
            (0, -2),
            (1, -2),
            (2, -1),
            (2, 0),
            (2, 1),
            (1, 2),
            (0, 2),
            (-1, 2),
            (-1, 1),
            (-2, 0),
        ];
        let mut expected = expected.iter();
        for p in ring_iter(&start_pos, radius) {
            let expected = expected.next().expect(
                "Can not get next element from expected vector");
            assert_eq!(*expected, (p.v.x, p.v.y));
        }
        assert!(expected.next().is_none());
    }

    #[test]
    fn test_spiral_1() {
        let radius = 2;
        let start_pos = MapPos{v: Vector2{x: 0, y: 0}};
        let expected = [
            // ring 1
            (0, -1),
            (1, -1),
            (1, 0),
            (1, 1),
            (0, 1),
            (-1, 0),
            // ring 2
            (-1, -1),
            (-1, -2),
            (0, -2),
            (1, -2),
            (2, -1),
            (2, 0),
            (2, 1),
            (1, 2),
            (0, 2),
            (-1, 2),
            (-1, 1),
            (-2, 0),
        ];
        let mut expected = expected.iter();
        for p in spiral_iter(&start_pos, radius) {
            let expected = expected.next().expect(
                "Can not get next element from expected vector");
            assert_eq!(*expected, (p.v.x, p.v.y));
        }
        assert!(expected.next().is_none());
    }
}

// vim: set tabstop=4 shiftwidth=4 softtabstop=4 expandtab:
<|endoftext|>


<commit_before>use image::*;
use num::NumCast;
use std::ops::Index;
use num::traits::Bounded;
use math::utils::*;
use math::affine::Affine2D;
use num::traits::ToPrimitive;

pub enum InterplateType {
    Nearest,
    Bilinear
}

pub fn resize_nearest<T: Pixel>(src: &Image<T>, width: u32, height: u32) -> Image<T> {
    let mut dst = Image::new(width, height);
    let yscale: f32 = src.height() as f32 / height as f32;
    let xscale: f32 = src.width() as f32 / width as f32;
    let mut xidx: Vec<usize> = Vec::with_capacity(width as usize);
    for w in 0..width as usize {
        xidx.push(clipped_round(w as f32 * xscale, 0,
                src.width() as i32 - 1) as usize);
    }
    for h in 0..height {
        let pdst = dst.row_mut(h);
        let psrc = src.row(clipped_round(h as f32 * yscale, 0,
                src.height() as i32 - 1) as u32);
        for w in 0..width as usize {
            pdst[w] = psrc[xidx[w]];
        }
    }
    dst
}

pub fn resize_bilinear<T: Pixel>(src: &Image<T>, width: u32, height: u32) -> Image<T> {
    let mut dst = Image::new(width, height);
    let yscale: f32 = src.height() as f32 / height as f32;
    let xscale: f32 = src.width() as f32 / width as f32;

    let mut x_0: Vec<usize> = Vec::with_capacity(width as usize);
    let mut x_1: Vec<usize> = Vec::with_capacity(width as usize);
    let mut d_0: Vec<f32> = Vec::with_capacity(width as usize);

    for w in 0..width as usize {
        let mid = w as f32 * xscale;
        let l = mid.floor();
        let r = mid.ceil();
        let d = r - mid;
        x_0.push(clipped_round(l, 0, src.width() as i32 - 1) as usize);
        x_1.push(clipped_round(r, 0, src.width() as i32 - 1) as usize);
        d_0.push(d);
    }
    for h in 0..height {
        let pdst = dst.row_mut(h);

        let mid = h as f32 * yscale;
        let t = mid.floor();
        let b = mid.ceil();
        let dy = b - mid;

        let psrc0 = src.row(clipped_round(t, 0, src.height() as i32 - 1) as u32);
        let psrc1 = src.row(clipped_round(b, 0, src.height() as i32 - 1) as u32);
        for w in 0..width as usize {
            let x0 = x_0[w];
            let x1 = x_1[w];
            //let a = psrc0[x0].blend(psrc0[x1], d_0[w]);
            //let b = psrc1[x0].blend(psrc1[x1], d_0[w]);
            //pdst[w] = a.blend(b, dy);
            pdst[w] = psrc0[x0].blend4(
                psrc0[x1],
                psrc1[x0],
                psrc1[x1],
                d_0[w], dy);
        }
    }
    dst
}

pub fn resize<T: Pixel>(src: &Image<T>, width: u32, height: u32, interp: InterplateType) -> Image<T> {
    match interp {
        InterplateType::Nearest => resize_nearest(src, width, height),
        InterplateType::Bilinear => resize_bilinear(src, width, height)
    }
}

pub fn warp_perspective<T: Pixel>(src: &Image<T>, width: u32, height: u32, affine :&Affine2D, interp :InterplateType) -> Image<T> {
    let mut dst: Image<T> = Image::new(width, height);
    for h in 0..height {
        let pdst = dst.row_mut(h);
        for w in 0..width {
            let coord = affine.apply_inv([w as f32, h as f32, 1f32]);
            let sx = coord[0] / coord[2];
            let sy = coord[1] / coord[2];
            match interp {
                InterplateType::Nearest => {
                    let ix = sx.round() as i32;
                    let iy = sy.round() as i32;
                    if ix >= 0 && ix < src.width() as i32
                        && iy >= 0 && iy < src.height() as i32 {
                            pdst[w as usize] = src[(ix as u32, iy as u32)];
                        }
                },
                InterplateType::Bilinear => {
                    let u = sx.ceil() - sx;
                    let v = sy.ceil() - sy;
                    let x0 = clip(sx.floor() as i32, 0, src.width() as i32 - 1) as u32;
                    let y0 = clip(sy.floor() as i32, 0, src.height() as i32 -1) as u32;
                    let x1 = clip(sx.ceil() as i32, 0, src.width() as i32 -1) as u32;
                    let y1 = clip(sy.ceil() as i32, 0, src.height() as i32 - 1) as u32;
                    pdst[w as usize] = src[(x0, y0)].blend4(
                        src[(x1, y0)],
                        src[(x0, y1)],
                        src[(x1, y1)],
                        u, v);
                }
            }
        }
    }
    dst
}

pub fn flip_vertical<T: Pixel>(src: &Image<T>) -> Image<T> {
    let mut dst = Image::new(src.width(), src.height());
    for h in 0..src.height() {
        for (a, b) in dst.row_mut(src.height() - h - 1).iter_mut().zip(src.row(h)) {
            *a = *b;
        }
    }
    dst
}

pub fn flip_horizontal<T: Pixel>(src: &Image<T>) -> Image<T> {
    let mut dst = Image::new(src.width(), src.height());
    for h in 0..src.height() {
        for (a, b) in dst.row_mut(h).iter_mut().zip(src.row(h).iter().rev()) {
            *a = *b;
        }
    }
    dst
}

pub fn min<T, U>(src: &Image<T>) -> U
    where T: Pixel,
          U: Pixel,
          T: Index<usize, Output=U::Subpixel>
{
    let mut t = [U::Subpixel::max_value(); MAX_CHANNEL_COUNT];
    for (_, _, p) in src.iter() {
        for c in 0..T::channels() {
            if t[c] > p[c] {
                t[c] = p[c];
            }
        }
    }
    U::from_raw(&t)
}

pub fn max<T, U>(src: &Image<T>) -> U
    where T: Pixel,
          U: Pixel,
          T: Index<usize, Output=U::Subpixel>
{
    let mut t = [U::Subpixel::min_value(); MAX_CHANNEL_COUNT];
    for (_, _, p) in src.iter() {
        for c in 0..T::channels() {
            if t[c] < p[c] {
                t[c] = p[c];
            }
        }
    }
    U::from_raw(&t)
}

pub fn normalize<U, V, M>(src: &Image<U>, alpha: f32, beta: f32) -> Image<V>
    where U: Pixel,
          V: Pixel,
          M: Pixel,
          U: Index<usize, Output=M::Subpixel>
{
    let mut dst = Image::<V>::new(src.width(), src.height());
    let mut mins = [0f32; MAX_CHANNEL_COUNT];
    let mut maxs = [0f32; MAX_CHANNEL_COUNT];
    let min_p: M = min(&src);
    let max_p: M = max(&src);
    let s = beta - alpha;
    for c in 0..U::channels() {
        mins[c] = min_p.raw()[c].to_f32().unwrap();
        maxs[c] = max_p.raw()[c].to_f32().unwrap();
    }
    for ((_, _, p), (_, _, q)) in dst.iter_mut().zip(src.iter()) {
        for c in 0..U::channels() {
            let d = maxs[c] - mins[c];
            let t = (q.raw()[c].to_f32().unwrap() - mins[c]) / d * s + alpha;
            p.raw_mut()[c] = NumCast::from(t).unwrap();
        }
    }
    dst
}

//pub fn max<T: Pixel>(src: &Image<T>) -> (u32, u32, T) {
//}

#[derive(Debug, Clone)]
pub enum RotateType{
    Cw0,
    Cw90,
    Cw180,
    Cw270
}

pub fn rotate_cw90<T: Pixel>(src: &Image<T>) -> Image<T> {
    let mut dst = Image::new(src.height(), src.width());
    for h in 0..src.height() {
        let psrc = src.row(h);
        let k = (src.height() - h - 1) as usize;
        for w in 0..src.width() {
            dst.row_mut(w)[k] = psrc[w as usize];
        }
    }
    dst
}

pub fn rotate_cw180<T: Pixel>(src: &Image<T>) -> Image<T> {
    let mut dst = Image::new(src.width(), src.height());
    for h in 0..src.height() {
        let psrc = src.row(h);
        let pdst = dst.row_mut(src.height() - h - 1);
        for w in 0..src.width() as usize {
            pdst[w] = psrc[src.width() as usize - w - 1];
        }
    }

    dst
}

pub fn rotate_cw270<T: Pixel>(src: &Image<T>) -> Image<T> {
    let mut dst = Image::new(src.height(), src.width());
    for h in 0..src.height() {
        let psrc = src.row(h);
        for w in 0..src.width() {
            dst.row_mut(src.width() - w - 1)[h as usize] = psrc[w as usize];
        }
    }
    dst
}

fn rotate<T: Pixel>(src: &Image<T>, rtype: RotateType) -> Image<T> {
    match rtype {
        RotateType::Cw0 => src.clone(),
        RotateType::Cw90 => rotate_cw90(src),
        RotateType::Cw180 => rotate_cw180(src),
        RotateType::Cw270 => rotate_cw270(src)
    }
}

#[cfg(test)]
mod test {
    use super::*;
    use std::path::Path;
    use image::ImageBgra;
    use imageio::ImageIO;
    use imageio::FreeImageIO;
    use geo::*;
    use math::affine::Affine2D;

    #[test]
    fn test_resize() {
        let path = Path::new("./tests/cat.jpg");
        let img: ImageBgra = FreeImageIO::from_path(&path).unwrap();

        let dst = resize_nearest(&img, 600, 400);
        assert_eq!(dst.width(), 600);
        assert_eq!(dst.height(), 400);
        let target = Path::new("/tmp/test-resize-out1.jpg");
        FreeImageIO::save(&target, &dst).unwrap();

        let dst = resize_bilinear(&img, 600, 400);
        assert_eq!(dst.width(), 600);
        assert_eq!(dst.height(), 400);
        let target = Path::new("/tmp/test-resize-out2.jpg");
        FreeImageIO::save(&target, &dst).unwrap();
    }

    #[test]
    fn test_warp() {
        let path = Path::new("./tests/cat.jpg");
        let img: ImageBgra = FreeImageIO::from_path(&path).unwrap();
        let src = vec![Pointf::new(0f32, 0f32), Pointf::new(1f32, 0f32)];
        let dst = vec![Pointf::new(0f32, 0f32), Pointf::new(1f32, 1f32)];

        let aff = Affine2D::nonreflect_similarity_from_points(&src, &dst).unwrap();
        let out = warp_perspective(&img, 200, 200, &aff, InterplateType::Nearest);
        let target = Path::new("/tmp/test-affine-out1.jpg");
        FreeImageIO::save(&target, &out).unwrap();

        let out = warp_perspective(&img, 300, 300, &aff, InterplateType::Bilinear);
        let target = Path::new("/tmp/test-affine-out2.jpg");
        FreeImageIO::save(&target, &out).unwrap();
    }

    #[test]
    fn test_flip() {
        let path = Path::new("./tests/cat.jpg");
        let img: ImageBgra = FreeImageIO::from_path(&path).unwrap();

        let out = flip_vertical(&img);
        let target = Path::new("/tmp/test-flip-out1.jpg");
        FreeImageIO::save(&target, &out).unwrap();

        let out = flip_horizontal(&img);
        let target = Path::new("/tmp/test-flip-out2.jpg");
        FreeImageIO::save(&target, &out).unwrap();
    }

    #[test]
    fn test_rotate() {
        let path = Path::new("./tests/cat.jpg");
        let img: ImageBgra = FreeImageIO::from_path(&path).unwrap();

        let out = rotate_cw90(&img);
        let target = Path::new("/tmp/test-rotate-out1.jpg");
        FreeImageIO::save(&target, &out).unwrap();

        let out = rotate_cw180(&img);
        let target = Path::new("/tmp/test-rotate-out2.jpg");
        FreeImageIO::save(&target, &out).unwrap();

        let out = rotate_cw270(&img);
        let target = Path::new("/tmp/test-rotate-out3.jpg");
        FreeImageIO::save(&target, &out).unwrap();
    }
}

<commit_msg>fix rotate access<commit_after>use image::*;
use num::NumCast;
use std::ops::Index;
use num::traits::Bounded;
use math::utils::*;
use math::affine::Affine2D;
use num::traits::ToPrimitive;

pub enum InterplateType {
    Nearest,
    Bilinear
}

pub fn resize_nearest<T: Pixel>(src: &Image<T>, width: u32, height: u32) -> Image<T> {
    let mut dst = Image::new(width, height);
    let yscale: f32 = src.height() as f32 / height as f32;
    let xscale: f32 = src.width() as f32 / width as f32;
    let mut xidx: Vec<usize> = Vec::with_capacity(width as usize);
    for w in 0..width as usize {
        xidx.push(clipped_round(w as f32 * xscale, 0,
                src.width() as i32 - 1) as usize);
    }
    for h in 0..height {
        let pdst = dst.row_mut(h);
        let psrc = src.row(clipped_round(h as f32 * yscale, 0,
                src.height() as i32 - 1) as u32);
        for w in 0..width as usize {
            pdst[w] = psrc[xidx[w]];
        }
    }
    dst
}

pub fn resize_bilinear<T: Pixel>(src: &Image<T>, width: u32, height: u32) -> Image<T> {
    let mut dst = Image::new(width, height);
    let yscale: f32 = src.height() as f32 / height as f32;
    let xscale: f32 = src.width() as f32 / width as f32;

    let mut x_0: Vec<usize> = Vec::with_capacity(width as usize);
    let mut x_1: Vec<usize> = Vec::with_capacity(width as usize);
    let mut d_0: Vec<f32> = Vec::with_capacity(width as usize);

    for w in 0..width as usize {
        let mid = w as f32 * xscale;
        let l = mid.floor();
        let r = mid.ceil();
        let d = r - mid;
        x_0.push(clipped_round(l, 0, src.width() as i32 - 1) as usize);
        x_1.push(clipped_round(r, 0, src.width() as i32 - 1) as usize);
        d_0.push(d);
    }
    for h in 0..height {
        let pdst = dst.row_mut(h);

        let mid = h as f32 * yscale;
        let t = mid.floor();
        let b = mid.ceil();
        let dy = b - mid;

        let psrc0 = src.row(clipped_round(t, 0, src.height() as i32 - 1) as u32);
        let psrc1 = src.row(clipped_round(b, 0, src.height() as i32 - 1) as u32);
        for w in 0..width as usize {
            let x0 = x_0[w];
            let x1 = x_1[w];
            //let a = psrc0[x0].blend(psrc0[x1], d_0[w]);
            //let b = psrc1[x0].blend(psrc1[x1], d_0[w]);
            //pdst[w] = a.blend(b, dy);
            pdst[w] = psrc0[x0].blend4(
                psrc0[x1],
                psrc1[x0],
                psrc1[x1],
                d_0[w], dy);
        }
    }
    dst
}

pub fn resize<T: Pixel>(src: &Image<T>, width: u32, height: u32, interp: InterplateType) -> Image<T> {
    match interp {
        InterplateType::Nearest => resize_nearest(src, width, height),
        InterplateType::Bilinear => resize_bilinear(src, width, height)
    }
}

pub fn warp_perspective<T: Pixel>(src: &Image<T>, width: u32, height: u32, affine :&Affine2D, interp :InterplateType) -> Image<T> {
    let mut dst: Image<T> = Image::new(width, height);
    for h in 0..height {
        let pdst = dst.row_mut(h);
        for w in 0..width {
            let coord = affine.apply_inv([w as f32, h as f32, 1f32]);
            let sx = coord[0] / coord[2];
            let sy = coord[1] / coord[2];
            match interp {
                InterplateType::Nearest => {
                    let ix = sx.round() as i32;
                    let iy = sy.round() as i32;
                    if ix >= 0 && ix < src.width() as i32
                        && iy >= 0 && iy < src.height() as i32 {
                            pdst[w as usize] = src[(ix as u32, iy as u32)];
                        }
                },
                InterplateType::Bilinear => {
                    let u = sx.ceil() - sx;
                    let v = sy.ceil() - sy;
                    let x0 = clip(sx.floor() as i32, 0, src.width() as i32 - 1) as u32;
                    let y0 = clip(sy.floor() as i32, 0, src.height() as i32 -1) as u32;
                    let x1 = clip(sx.ceil() as i32, 0, src.width() as i32 -1) as u32;
                    let y1 = clip(sy.ceil() as i32, 0, src.height() as i32 - 1) as u32;
                    pdst[w as usize] = src[(x0, y0)].blend4(
                        src[(x1, y0)],
                        src[(x0, y1)],
                        src[(x1, y1)],
                        u, v);
                }
            }
        }
    }
    dst
}

pub fn flip_vertical<T: Pixel>(src: &Image<T>) -> Image<T> {
    let mut dst = Image::new(src.width(), src.height());
    for h in 0..src.height() {
        for (a, b) in dst.row_mut(src.height() - h - 1).iter_mut().zip(src.row(h)) {
            *a = *b;
        }
    }
    dst
}

pub fn flip_horizontal<T: Pixel>(src: &Image<T>) -> Image<T> {
    let mut dst = Image::new(src.width(), src.height());
    for h in 0..src.height() {
        for (a, b) in dst.row_mut(h).iter_mut().zip(src.row(h).iter().rev()) {
            *a = *b;
        }
    }
    dst
}

pub fn min<T, U>(src: &Image<T>) -> U
    where T: Pixel,
          U: Pixel,
          T: Index<usize, Output=U::Subpixel>
{
    let mut t = [U::Subpixel::max_value(); MAX_CHANNEL_COUNT];
    for (_, _, p) in src.iter() {
        for c in 0..T::channels() {
            if t[c] > p[c] {
                t[c] = p[c];
            }
        }
    }
    U::from_raw(&t)
}

pub fn max<T, U>(src: &Image<T>) -> U
    where T: Pixel,
          U: Pixel,
          T: Index<usize, Output=U::Subpixel>
{
    let mut t = [U::Subpixel::min_value(); MAX_CHANNEL_COUNT];
    for (_, _, p) in src.iter() {
        for c in 0..T::channels() {
            if t[c] < p[c] {
                t[c] = p[c];
            }
        }
    }
    U::from_raw(&t)
}

pub fn normalize<U, V, M>(src: &Image<U>, alpha: f32, beta: f32) -> Image<V>
    where U: Pixel,
          V: Pixel,
          M: Pixel,
          U: Index<usize, Output=M::Subpixel>
{
    let mut dst = Image::<V>::new(src.width(), src.height());
    let mut mins = [0f32; MAX_CHANNEL_COUNT];
    let mut maxs = [0f32; MAX_CHANNEL_COUNT];
    let min_p: M = min(&src);
    let max_p: M = max(&src);
    let s = beta - alpha;
    for c in 0..U::channels() {
        mins[c] = min_p.raw()[c].to_f32().unwrap();
        maxs[c] = max_p.raw()[c].to_f32().unwrap();
    }
    for ((_, _, p), (_, _, q)) in dst.iter_mut().zip(src.iter()) {
        for c in 0..U::channels() {
            let d = maxs[c] - mins[c];
            let t = (q.raw()[c].to_f32().unwrap() - mins[c]) / d * s + alpha;
            p.raw_mut()[c] = NumCast::from(t).unwrap();
        }
    }
    dst
}

//pub fn max<T: Pixel>(src: &Image<T>) -> (u32, u32, T) {
//}

#[derive(Debug, Clone)]
pub enum RotateType{
    Cw0,
    Cw90,
    Cw180,
    Cw270
}

pub fn rotate_cw90<T: Pixel>(src: &Image<T>) -> Image<T> {
    let mut dst = Image::new(src.height(), src.width());
    for h in 0..src.height() {
        let psrc = src.row(h);
        let k = (src.height() - h - 1) as usize;
        for w in 0..src.width() {
            dst.row_mut(w)[k] = psrc[w as usize];
        }
    }
    dst
}

pub fn rotate_cw180<T: Pixel>(src: &Image<T>) -> Image<T> {
    let mut dst = Image::new(src.width(), src.height());
    for h in 0..src.height() {
        let psrc = src.row(h);
        let pdst = dst.row_mut(src.height() - h - 1);
        for w in 0..src.width() as usize {
            pdst[w] = psrc[src.width() as usize - w - 1];
        }
    }

    dst
}

pub fn rotate_cw270<T: Pixel>(src: &Image<T>) -> Image<T> {
    let mut dst = Image::new(src.height(), src.width());
    for h in 0..src.height() {
        let psrc = src.row(h);
        for w in 0..src.width() {
            dst.row_mut(src.width() - w - 1)[h as usize] = psrc[w as usize];
        }
    }
    dst
}

pub fn rotate<T: Pixel>(src: &Image<T>, rtype: RotateType) -> Image<T> {
    match rtype {
        RotateType::Cw0 => src.clone(),
        RotateType::Cw90 => rotate_cw90(src),
        RotateType::Cw180 => rotate_cw180(src),
        RotateType::Cw270 => rotate_cw270(src)
    }
}

#[cfg(test)]
mod test {
    use super::*;
    use std::path::Path;
    use image::ImageBgra;
    use imageio::ImageIO;
    use imageio::FreeImageIO;
    use geo::*;
    use math::affine::Affine2D;

    #[test]
    fn test_resize() {
        let path = Path::new("./tests/cat.jpg");
        let img: ImageBgra = FreeImageIO::from_path(&path).unwrap();

        let dst = resize_nearest(&img, 600, 400);
        assert_eq!(dst.width(), 600);
        assert_eq!(dst.height(), 400);
        let target = Path::new("/tmp/test-resize-out1.jpg");
        FreeImageIO::save(&target, &dst).unwrap();

        let dst = resize_bilinear(&img, 600, 400);
        assert_eq!(dst.width(), 600);
        assert_eq!(dst.height(), 400);
        let target = Path::new("/tmp/test-resize-out2.jpg");
        FreeImageIO::save(&target, &dst).unwrap();
    }

    #[test]
    fn test_warp() {
        let path = Path::new("./tests/cat.jpg");
        let img: ImageBgra = FreeImageIO::from_path(&path).unwrap();
        let src = vec![Pointf::new(0f32, 0f32), Pointf::new(1f32, 0f32)];
        let dst = vec![Pointf::new(0f32, 0f32), Pointf::new(1f32, 1f32)];

        let aff = Affine2D::nonreflect_similarity_from_points(&src, &dst).unwrap();
        let out = warp_perspective(&img, 200, 200, &aff, InterplateType::Nearest);
        let target = Path::new("/tmp/test-affine-out1.jpg");
        FreeImageIO::save(&target, &out).unwrap();

        let out = warp_perspective(&img, 300, 300, &aff, InterplateType::Bilinear);
        let target = Path::new("/tmp/test-affine-out2.jpg");
        FreeImageIO::save(&target, &out).unwrap();
    }

    #[test]
    fn test_flip() {
        let path = Path::new("./tests/cat.jpg");
        let img: ImageBgra = FreeImageIO::from_path(&path).unwrap();

        let out = flip_vertical(&img);
        let target = Path::new("/tmp/test-flip-out1.jpg");
        FreeImageIO::save(&target, &out).unwrap();

        let out = flip_horizontal(&img);
        let target = Path::new("/tmp/test-flip-out2.jpg");
        FreeImageIO::save(&target, &out).unwrap();
    }

    #[test]
    fn test_rotate() {
        let path = Path::new("./tests/cat.jpg");
        let img: ImageBgra = FreeImageIO::from_path(&path).unwrap();

        let out = rotate_cw90(&img);
        let target = Path::new("/tmp/test-rotate-out1.jpg");
        FreeImageIO::save(&target, &out).unwrap();

        let out = rotate_cw180(&img);
        let target = Path::new("/tmp/test-rotate-out2.jpg");
        FreeImageIO::save(&target, &out).unwrap();

        let out = rotate_cw270(&img);
        let target = Path::new("/tmp/test-rotate-out3.jpg");
        FreeImageIO::save(&target, &out).unwrap();
    }
}

<|endoftext|>


<commit_before>pub mod input;
pub mod cursor;
pub mod output;
pub mod area;
pub mod seat;
pub mod surface;
pub mod shell;

pub use self::area::*;
pub use self::cursor::*;
pub use self::output::*;
pub use self::seat::*;
pub use self::shell::*;
pub use self::surface::*;
pub use self::input::*;
<commit_msg>thx fmt<commit_after>pub mod input;
pub mod cursor;
pub mod output;
pub mod area;
pub mod seat;
pub mod surface;
pub mod shell;

pub use self::area::*;
pub use self::cursor::*;
pub use self::input::*;
pub use self::output::*;
pub use self::seat::*;
pub use self::shell::*;
pub use self::surface::*;
<|endoftext|>


<commit_before>use std::ops::{Index, IndexMut};
use std::collections::vec_deque::Iter;
use std::collections::VecDeque;

pub struct LimitedQueue<T> {
	q: VecDeque<T>,
	limit: usize
}

impl<T> LimitedQueue<T> {

	pub fn new(limit: usize) -> LimitedQueue<T>
	{
		let q = VecDeque::new();
		LimitedQueue { q: q, limit: limit }
	}

	pub fn with_allocation(limit: usize) -> LimitedQueue<T>
	{
		let q = VecDeque::with_capacity(limit);
		LimitedQueue { q: q, limit: limit }
	}

	pub fn push(&mut self, item: T)
	{
		self.q.push_back(item);
		while self.q.len() > self.limit {
			self.q.pop_front();
		}
	}

	pub fn iter(&self) -> Iter<T> { self.q.iter() }
	pub fn front(&self) -> Option<&T> { self.q.front() }
	pub fn back(&self) -> Option<&T> { self.q.back() }
}

impl<T> Index<usize> for LimitedQueue<T> {
	type Output = T;
	fn index(&self, index: usize) -> &T { &self.q[index] }
}

impl<T> IndexMut<usize> for LimitedQueue<T> {
	fn index_mut(&mut self, index: usize) -> &mut T { &mut self.q[index] }
}
<commit_msg>LimitedQueue: Add iter_mut method<commit_after>use std::ops::{Index, IndexMut};
use std::collections::vec_deque::{Iter, IterMut};
use std::collections::VecDeque;

pub struct LimitedQueue<T> {
	q: VecDeque<T>,
	limit: usize
}

impl<T> LimitedQueue<T> {

	pub fn new(limit: usize) -> LimitedQueue<T>
	{
		let q = VecDeque::new();
		LimitedQueue { q: q, limit: limit }
	}

	pub fn with_allocation(limit: usize) -> LimitedQueue<T>
	{
		let q = VecDeque::with_capacity(limit);
		LimitedQueue { q: q, limit: limit }
	}

	pub fn push(&mut self, item: T)
	{
		self.q.push_back(item);
		while self.q.len() > self.limit {
			self.q.pop_front();
		}
	}

	pub fn iter(&self) -> Iter<T> { self.q.iter() }
	pub fn iter_mut(&mut self) -> IterMut<T> { self.q.iter_mut() }
	pub fn front(&self) -> Option<&T> { self.q.front() }
	pub fn back(&self) -> Option<&T> { self.q.back() }
}

impl<T> Index<usize> for LimitedQueue<T> {
	type Output = T;
	fn index(&self, index: usize) -> &T { &self.q[index] }
}

impl<T> IndexMut<usize> for LimitedQueue<T> {
	fn index_mut(&mut self, index: usize) -> &mut T { &mut self.q[index] }
}
<|endoftext|>


<commit_before>// Copyright 2018 Developers of the Rand project.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! Implementation for Linux / Android
use crate::util::LazyBool;
use crate::util_libc::{last_os_error, sys_fill_exact};
use crate::{use_file, Error};

pub fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> {
    static HAS_GETRANDOM: LazyBool = LazyBool::new();
    if HAS_GETRANDOM.unsync_init(is_getrandom_available) {
        sys_fill_exact(dest, |buf| unsafe {
            libc::syscall(libc::SYS_getrandom, buf.as_mut_ptr(), buf.len(), 0) as libc::ssize_t
        })
    } else {
        use_file::getrandom_inner(dest)
    }
}

fn is_getrandom_available() -> bool {
    let res = unsafe { libc::syscall(libc::SYS_getrandom, 0, 0, libc::GRND_NONBLOCK) };
    if res < 0 {
        match last_os_error().raw_os_error() {
            Some(libc::ENOSYS) => false, // No kernel support
            Some(libc::EPERM) => false,  // Blocked by seccomp
            _ => true,
        }
    } else {
        true
    }
}
<commit_msg>Explicitly specify types to arguments of 'libc::syscall' (#74)<commit_after>// Copyright 2018 Developers of the Rand project.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! Implementation for Linux / Android
use crate::util::LazyBool;
use crate::util_libc::{last_os_error, sys_fill_exact};
use crate::{use_file, Error};

pub fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> {
    static HAS_GETRANDOM: LazyBool = LazyBool::new();
    if HAS_GETRANDOM.unsync_init(is_getrandom_available) {
        sys_fill_exact(dest, |buf| unsafe {
            getrandom(buf.as_mut_ptr() as *mut libc::c_void, buf.len(), 0)
        })
    } else {
        use_file::getrandom_inner(dest)
    }
}

fn is_getrandom_available() -> bool {
    let res = unsafe { getrandom(core::ptr::null_mut(), 0, libc::GRND_NONBLOCK) };
    if res < 0 {
        match last_os_error().raw_os_error() {
            Some(libc::ENOSYS) => false, // No kernel support
            Some(libc::EPERM) => false,  // Blocked by seccomp
            _ => true,
        }
    } else {
        true
    }
}

unsafe fn getrandom(
    buf: *mut libc::c_void,
    buflen: libc::size_t,
    flags: libc::c_uint,
) -> libc::ssize_t {
    libc::syscall(libc::SYS_getrandom, buf, buflen, flags) as libc::ssize_t
}
<|endoftext|>


<commit_before>//! Iterators that support the `pattern` module.
use std::marker::PhantomData;
use std::iter::ExactSizeIterator;

/* **************************************************************** */
/// An iterator that produces its input item exactly once.
#[derive(Debug)]
pub struct Once<T>(Option<T>);

impl<T> Once<T> {
    #[inline(always)]
    pub fn new(obj: T) -> Once<T> {
        Once(Some(obj))
    }
}

impl<T> ExactSizeIterator for Once<T> where T: Copy {
    #[inline(always)]
    fn len(&self) -> usize { 1 }
}

impl<T> Iterator for Once<T> where T: Copy {
    type Item = T;

    #[inline(always)]
    fn next(&mut self) -> Option<T> {
        if let Once(Some(val)) = *self {
            self.0 = None;
            Some(val) }
        else {
            None }
    }

    #[inline(always)]
    fn size_hint(&self) -> (usize, Option<usize>) {
        match self.0 {
            Some(..) => (1, Some(1)),
            None => (0, Some(0))
        }
    }
}
/* **************************************************************** */
/// An empty iterator.
#[derive(Debug)]
pub struct Empty<T> { _marker: PhantomData<T> }

impl<T> Empty<T> {
    #[inline(always)]
    pub fn new() -> Empty<T> {
        Empty{_marker: PhantomData}
    }
}

impl<T> ExactSizeIterator for Empty<T> {
    #[inline(always)]
    fn len(&self) -> usize { 0 }
}

impl<T=()> Iterator for Empty<T> {
    type Item = T;

    #[inline(always)]
    fn next(&mut self) -> Option<T> { None }

    #[inline(always)]
    fn size_hint(&self) -> (usize, Option<usize>) { (0, Some(0)) }
}

/* **************************************************************** */
// Interface for types that provide some form of iterator.
// pub trait Iterable<'t,Iter> where Iter: 't {
//     fn iter(&'t self) -> Iter;
// }
<commit_msg>util/iter.rs: fold a newline for a simple `else` branch<commit_after>//! Iterators that support the `pattern` module.
use std::marker::PhantomData;
use std::iter::ExactSizeIterator;

/* **************************************************************** */
/// An iterator that produces its input item exactly once.
#[derive(Debug)]
pub struct Once<T>(Option<T>);

impl<T> Once<T> {
    #[inline(always)]
    pub fn new(obj: T) -> Once<T> {
        Once(Some(obj))
    }
}

impl<T> ExactSizeIterator for Once<T> where T: Copy {
    #[inline(always)]
    fn len(&self) -> usize { 1 }
}

impl<T> Iterator for Once<T> where T: Copy {
    type Item = T;

    #[inline(always)]
    fn next(&mut self) -> Option<T> {
        if let Once(Some(val)) = *self {
            self.0 = None;
            Some(val) }
        else { None }
    }

    #[inline(always)]
    fn size_hint(&self) -> (usize, Option<usize>) {
        match self.0 {
            Some(..) => (1, Some(1)),
            None => (0, Some(0))
        }
    }
}
/* **************************************************************** */
/// An empty iterator.
#[derive(Debug)]
pub struct Empty<T> { _marker: PhantomData<T> }

impl<T> Empty<T> {
    #[inline(always)]
    pub fn new() -> Empty<T> {
        Empty{_marker: PhantomData}
    }
}

impl<T> ExactSizeIterator for Empty<T> {
    #[inline(always)]
    fn len(&self) -> usize { 0 }
}

impl<T=()> Iterator for Empty<T> {
    type Item = T;

    #[inline(always)]
    fn next(&mut self) -> Option<T> { None }

    #[inline(always)]
    fn size_hint(&self) -> (usize, Option<usize>) { (0, Some(0)) }
}

/* **************************************************************** */
// Interface for types that provide some form of iterator.
// pub trait Iterable<'t,Iter> where Iter: 't {
//     fn iter(&'t self) -> Iter;
// }
<|endoftext|>


<commit_before>//! Checks the licenses of third-party dependencies by inspecting vendors.

use std::collections::{BTreeSet, HashSet, HashMap};
use std::fs;
use std::path::Path;
use std::process::Command;

use serde::Deserialize;
use serde_json;

const LICENSES: &[&str] = &[
    "MIT/Apache-2.0",
    "MIT / Apache-2.0",
    "Apache-2.0/MIT",
    "Apache-2.0 / MIT",
    "MIT OR Apache-2.0",
    "MIT",
    "Unlicense/MIT",
    "Unlicense OR MIT",
];

/// These are exceptions to Rust's permissive licensing policy, and
/// should be considered bugs. Exceptions are only allowed in Rust
/// tooling. It is _crucial_ that no exception crates be dependencies
/// of the Rust runtime (std/test).
const EXCEPTIONS: &[&str] = &[
    "mdbook",             // MPL2, mdbook
    "openssl",            // BSD+advertising clause, cargo, mdbook
    "pest",               // MPL2, mdbook via handlebars
    "arrayref",           // BSD-2-Clause, mdbook via handlebars via pest
    "thread-id",          // Apache-2.0, mdbook
    "toml-query",         // MPL-2.0, mdbook
    "is-match",           // MPL-2.0, mdbook
    "cssparser",          // MPL-2.0, rustdoc
    "smallvec",           // MPL-2.0, rustdoc
    "fuchsia-zircon-sys", // BSD-3-Clause, rustdoc, rustc, cargo
    "fuchsia-zircon",     // BSD-3-Clause, rustdoc, rustc, cargo (jobserver & tempdir)
    "cssparser-macros",   // MPL-2.0, rustdoc
    "selectors",          // MPL-2.0, rustdoc
    "clippy_lints",       // MPL-2.0, rls
    "colored",            // MPL-2.0, rustfmt
    "ordslice",           // Apache-2.0, rls
    "cloudabi",           // BSD-2-Clause, (rls -> crossbeam-channel 0.2 -> rand 0.5)
    "ryu",                // Apache-2.0, rls/cargo/... (because of serde)
    "bytesize",           // Apache-2.0, cargo
    "im-rc",              // MPL-2.0+, cargo
    "adler32",            // BSD-3-Clause AND Zlib, cargo dep that isn't used
    "fortanix-sgx-abi",   // MPL-2.0+, libstd but only for `sgx` target
    "constant_time_eq",   // CC0-1.0, rustfmt
    "utf8parse",          // Apache-2.0 OR MIT, cargo via strip-ansi-escapes
    "vte",                // Apache-2.0 OR MIT, cargo via strip-ansi-escapes
    "sized-chunks",       // MPL-2.0+, cargo via im-rc
];

/// Which crates to check against the whitelist?
const WHITELIST_CRATES: &[CrateVersion<'_>] = &[
    CrateVersion("rustc", "0.0.0"),
    CrateVersion("rustc_codegen_llvm", "0.0.0"),
];

/// Whitelist of crates rustc is allowed to depend on. Avoid adding to the list if possible.
const WHITELIST: &[Crate<'_>] = &[
    Crate("adler32"),
    Crate("aho-corasick"),
    Crate("arrayvec"),
    Crate("atty"),
    Crate("backtrace"),
    Crate("backtrace-sys"),
    Crate("bitflags"),
    Crate("build_const"),
    Crate("byteorder"),
    Crate("cc"),
    Crate("cfg-if"),
    Crate("chalk-engine"),
    Crate("chalk-macros"),
    Crate("cloudabi"),
    Crate("cmake"),
    Crate("compiler_builtins"),
    Crate("crc"),
    Crate("crc32fast"),
    Crate("crossbeam-deque"),
    Crate("crossbeam-epoch"),
    Crate("crossbeam-utils"),
    Crate("datafrog"),
    Crate("either"),
    Crate("ena"),
    Crate("env_logger"),
    Crate("filetime"),
    Crate("flate2"),
    Crate("fuchsia-zircon"),
    Crate("fuchsia-zircon-sys"),
    Crate("getopts"),
    Crate("humantime"),
    Crate("itertools"),
    Crate("jobserver"),
    Crate("kernel32-sys"),
    Crate("lazy_static"),
    Crate("libc"),
    Crate("libz-sys"),
    Crate("lock_api"),
    Crate("log"),
    Crate("log_settings"),
    Crate("measureme"),
    Crate("memchr"),
    Crate("memmap"),
    Crate("memoffset"),
    Crate("miniz-sys"),
    Crate("miniz_oxide"),
    Crate("miniz_oxide_c_api"),
    Crate("nodrop"),
    Crate("num_cpus"),
    Crate("owning_ref"),
    Crate("parking_lot"),
    Crate("parking_lot_core"),
    Crate("pkg-config"),
    Crate("polonius-engine"),
    Crate("proc-macro2"),
    Crate("quick-error"),
    Crate("quote"),
    Crate("rand"),
    Crate("rand_chacha"),
    Crate("rand_core"),
    Crate("rand_hc"),
    Crate("rand_isaac"),
    Crate("rand_pcg"),
    Crate("rand_xorshift"),
    Crate("redox_syscall"),
    Crate("redox_termios"),
    Crate("regex"),
    Crate("regex-syntax"),
    Crate("remove_dir_all"),
    Crate("rustc-demangle"),
    Crate("rustc-hash"),
    Crate("rustc-rayon"),
    Crate("rustc-rayon-core"),
    Crate("rustc_version"),
    Crate("scoped-tls"),
    Crate("scopeguard"),
    Crate("semver"),
    Crate("semver-parser"),
    Crate("serde"),
    Crate("serde_derive"),
    Crate("smallvec"),
    Crate("stable_deref_trait"),
    Crate("syn"),
    Crate("synstructure"),
    Crate("tempfile"),
    Crate("termcolor"),
    Crate("terminon"),
    Crate("termion"),
    Crate("thread_local"),
    Crate("ucd-util"),
    Crate("unicode-width"),
    Crate("unicode-xid"),
    Crate("unreachable"),
    Crate("utf8-ranges"),
    Crate("vcpkg"),
    Crate("version_check"),
    Crate("void"),
    Crate("winapi"),
    Crate("winapi-build"),
    Crate("winapi-i686-pc-windows-gnu"),
    Crate("winapi-util"),
    Crate("winapi-x86_64-pc-windows-gnu"),
    Crate("wincolor"),
];

// Some types for Serde to deserialize the output of `cargo metadata` to.

#[derive(Deserialize)]
struct Output {
    resolve: Resolve,
}

#[derive(Deserialize)]
struct Resolve {
    nodes: Vec<ResolveNode>,
}

#[derive(Deserialize)]
struct ResolveNode {
    id: String,
    dependencies: Vec<String>,
}

/// A unique identifier for a crate.
#[derive(Copy, Clone, PartialOrd, Ord, PartialEq, Eq, Debug, Hash)]
struct Crate<'a>(&'a str); // (name)

#[derive(Copy, Clone, PartialOrd, Ord, PartialEq, Eq, Debug, Hash)]
struct CrateVersion<'a>(&'a str, &'a str); // (name, version)

impl Crate<'_> {
    pub fn id_str(&self) -> String {
        format!("{} ", self.0)
    }
}

impl<'a> CrateVersion<'a> {
    /// Returns the struct and whether or not the dependency is in-tree.
    pub fn from_str(s: &'a str) -> (Self, bool) {
        let mut parts = s.split(' ');
        let name = parts.next().unwrap();
        let version = parts.next().unwrap();
        let path = parts.next().unwrap();

        let is_path_dep = path.starts_with("(path+");

        (CrateVersion(name, version), is_path_dep)
    }

    pub fn id_str(&self) -> String {
        format!("{} {}", self.0, self.1)
    }
}

impl<'a> From<CrateVersion<'a>> for Crate<'a> {
    fn from(cv: CrateVersion<'a>) -> Crate<'a> {
        Crate(cv.0)
    }
}

/// Checks the dependency at the given path. Changes `bad` to `true` if a check failed.
///
/// Specifically, this checks that the license is correct.
pub fn check(path: &Path, bad: &mut bool) {
    // Check licences.
    let path = path.join("../vendor");
    assert!(path.exists(), "vendor directory missing");
    let mut saw_dir = false;
    for dir in t!(path.read_dir()) {
        saw_dir = true;
        let dir = t!(dir);

        // Skip our exceptions.
        let is_exception = EXCEPTIONS.iter().any(|exception| {
            dir.path()
                .to_str()
                .unwrap()
                .contains(&format!("vendor/{}", exception))
        });
        if is_exception {
            continue;
        }

        let toml = dir.path().join("Cargo.toml");
        *bad = *bad || !check_license(&toml);
    }
    assert!(saw_dir, "no vendored source");
}

/// Checks the dependency of `WHITELIST_CRATES` at the given path. Changes `bad` to `true` if a
/// check failed.
///
/// Specifically, this checks that the dependencies are on the `WHITELIST`.
pub fn check_whitelist(path: &Path, cargo: &Path, bad: &mut bool) {
    // Get dependencies from Cargo metadata.
    let resolve = get_deps(path, cargo);

    // Get the whitelist in a convenient form.
    let whitelist: HashSet<_> = WHITELIST.iter().cloned().collect();

    // Check dependencies.
    let mut visited = BTreeSet::new();
    let mut unapproved = BTreeSet::new();
    for &krate in WHITELIST_CRATES.iter() {
        let mut bad = check_crate_whitelist(&whitelist, &resolve, &mut visited, krate, false);
        unapproved.append(&mut bad);
    }

    if !unapproved.is_empty() {
        println!("Dependencies not on the whitelist:");
        for dep in unapproved {
            println!("* {}", dep.id_str());
        }
        *bad = true;
    }

    check_crate_duplicate(&resolve, bad);
}

fn check_license(path: &Path) -> bool {
    if !path.exists() {
        panic!("{} does not exist", path.display());
    }
    let contents = t!(fs::read_to_string(&path));

    let mut found_license = false;
    for line in contents.lines() {
        if !line.starts_with("license") {
            continue;
        }
        let license = extract_license(line);
        if !LICENSES.contains(&&*license) {
            println!("invalid license {} in {}", license, path.display());
            return false;
        }
        found_license = true;
        break;
    }
    if !found_license {
        println!("no license in {}", path.display());
        return false;
    }

    true
}

fn extract_license(line: &str) -> String {
    let first_quote = line.find('"');
    let last_quote = line.rfind('"');
    if let (Some(f), Some(l)) = (first_quote, last_quote) {
        let license = &line[f + 1..l];
        license.into()
    } else {
        "bad-license-parse".into()
    }
}

/// Gets the dependencies of the crate at the given path using `cargo metadata`.
fn get_deps(path: &Path, cargo: &Path) -> Resolve {
    // Run `cargo metadata` to get the set of dependencies.
    let output = Command::new(cargo)
        .arg("metadata")
        .arg("--format-version")
        .arg("1")
        .arg("--manifest-path")
        .arg(path.join("../Cargo.toml"))
        .output()
        .expect("Unable to run `cargo metadata`")
        .stdout;
    let output = String::from_utf8_lossy(&output);
    let output: Output = serde_json::from_str(&output).unwrap();

    output.resolve
}

/// Checks the dependencies of the given crate from the given cargo metadata to see if they are on
/// the whitelist. Returns a list of illegal dependencies.
fn check_crate_whitelist<'a>(
    whitelist: &'a HashSet<Crate<'_>>,
    resolve: &'a Resolve,
    visited: &mut BTreeSet<CrateVersion<'a>>,
    krate: CrateVersion<'a>,
    must_be_on_whitelist: bool,
) -> BTreeSet<Crate<'a>> {
    // This will contain bad deps.
    let mut unapproved = BTreeSet::new();

    // Check if we have already visited this crate.
    if visited.contains(&krate) {
        return unapproved;
    }

    visited.insert(krate);

    // If this path is in-tree, we don't require it to be on the whitelist.
    if must_be_on_whitelist {
        // If this dependency is not on `WHITELIST`, add to bad set.
        if !whitelist.contains(&krate.into()) {
            unapproved.insert(krate.into());
        }
    }

    // Do a DFS in the crate graph (it's a DAG, so we know we have no cycles!).
    let to_check = resolve
        .nodes
        .iter()
        .find(|n| n.id.starts_with(&krate.id_str()))
        .expect("crate does not exist");

    for dep in to_check.dependencies.iter() {
        let (krate, is_path_dep) = CrateVersion::from_str(dep);

        let mut bad = check_crate_whitelist(whitelist, resolve, visited, krate, !is_path_dep);
        unapproved.append(&mut bad);
    }

    unapproved
}

fn check_crate_duplicate(resolve: &Resolve, bad: &mut bool) {
    const FORBIDDEN_TO_HAVE_DUPLICATES: &[&str] = &[
        // These two crates take quite a long time to build, so don't allow two versions of them
        // to accidentally sneak into our dependency graph, in order to ensure we keep our CI times
        // under control.

        "cargo",
        "rustc-ap-syntax",
    ];
    let mut name_to_id: HashMap<_, Vec<_>> = HashMap::new();
    for node in resolve.nodes.iter() {
        name_to_id.entry(node.id.split_whitespace().next().unwrap())
            .or_default()
            .push(&node.id);
    }

    for name in FORBIDDEN_TO_HAVE_DUPLICATES {
        if name_to_id[name].len() <= 1 {
            continue
        }
        println!("crate `{}` is duplicated in `Cargo.lock`", name);
        for id in name_to_id[name].iter() {
            println!("  * {}", id);
        }
        *bad = true;
    }
}
<commit_msg>Rollup merge of #61096 - ehuss:tidy-license-short-circuit, r=Centril<commit_after>//! Checks the licenses of third-party dependencies by inspecting vendors.

use std::collections::{BTreeSet, HashSet, HashMap};
use std::fs;
use std::path::Path;
use std::process::Command;

use serde::Deserialize;
use serde_json;

const LICENSES: &[&str] = &[
    "MIT/Apache-2.0",
    "MIT / Apache-2.0",
    "Apache-2.0/MIT",
    "Apache-2.0 / MIT",
    "MIT OR Apache-2.0",
    "MIT",
    "Unlicense/MIT",
    "Unlicense OR MIT",
];

/// These are exceptions to Rust's permissive licensing policy, and
/// should be considered bugs. Exceptions are only allowed in Rust
/// tooling. It is _crucial_ that no exception crates be dependencies
/// of the Rust runtime (std/test).
const EXCEPTIONS: &[&str] = &[
    "mdbook",             // MPL2, mdbook
    "openssl",            // BSD+advertising clause, cargo, mdbook
    "pest",               // MPL2, mdbook via handlebars
    "arrayref",           // BSD-2-Clause, mdbook via handlebars via pest
    "thread-id",          // Apache-2.0, mdbook
    "toml-query",         // MPL-2.0, mdbook
    "is-match",           // MPL-2.0, mdbook
    "cssparser",          // MPL-2.0, rustdoc
    "smallvec",           // MPL-2.0, rustdoc
    "fuchsia-zircon-sys", // BSD-3-Clause, rustdoc, rustc, cargo
    "fuchsia-zircon",     // BSD-3-Clause, rustdoc, rustc, cargo (jobserver & tempdir)
    "cssparser-macros",   // MPL-2.0, rustdoc
    "selectors",          // MPL-2.0, rustdoc
    "clippy_lints",       // MPL-2.0, rls
    "colored",            // MPL-2.0, rustfmt
    "ordslice",           // Apache-2.0, rls
    "cloudabi",           // BSD-2-Clause, (rls -> crossbeam-channel 0.2 -> rand 0.5)
    "ryu",                // Apache-2.0, rls/cargo/... (because of serde)
    "bytesize",           // Apache-2.0, cargo
    "im-rc",              // MPL-2.0+, cargo
    "adler32",            // BSD-3-Clause AND Zlib, cargo dep that isn't used
    "fortanix-sgx-abi",   // MPL-2.0+, libstd but only for `sgx` target
    "constant_time_eq",   // CC0-1.0, rustfmt
    "utf8parse",          // Apache-2.0 OR MIT, cargo via strip-ansi-escapes
    "vte",                // Apache-2.0 OR MIT, cargo via strip-ansi-escapes
    "sized-chunks",       // MPL-2.0+, cargo via im-rc
];

/// Which crates to check against the whitelist?
const WHITELIST_CRATES: &[CrateVersion<'_>] = &[
    CrateVersion("rustc", "0.0.0"),
    CrateVersion("rustc_codegen_llvm", "0.0.0"),
];

/// Whitelist of crates rustc is allowed to depend on. Avoid adding to the list if possible.
const WHITELIST: &[Crate<'_>] = &[
    Crate("adler32"),
    Crate("aho-corasick"),
    Crate("arrayvec"),
    Crate("atty"),
    Crate("backtrace"),
    Crate("backtrace-sys"),
    Crate("bitflags"),
    Crate("build_const"),
    Crate("byteorder"),
    Crate("cc"),
    Crate("cfg-if"),
    Crate("chalk-engine"),
    Crate("chalk-macros"),
    Crate("cloudabi"),
    Crate("cmake"),
    Crate("compiler_builtins"),
    Crate("crc"),
    Crate("crc32fast"),
    Crate("crossbeam-deque"),
    Crate("crossbeam-epoch"),
    Crate("crossbeam-utils"),
    Crate("datafrog"),
    Crate("either"),
    Crate("ena"),
    Crate("env_logger"),
    Crate("filetime"),
    Crate("flate2"),
    Crate("fuchsia-zircon"),
    Crate("fuchsia-zircon-sys"),
    Crate("getopts"),
    Crate("humantime"),
    Crate("itertools"),
    Crate("jobserver"),
    Crate("kernel32-sys"),
    Crate("lazy_static"),
    Crate("libc"),
    Crate("libz-sys"),
    Crate("lock_api"),
    Crate("log"),
    Crate("log_settings"),
    Crate("measureme"),
    Crate("memchr"),
    Crate("memmap"),
    Crate("memoffset"),
    Crate("miniz-sys"),
    Crate("miniz_oxide"),
    Crate("miniz_oxide_c_api"),
    Crate("nodrop"),
    Crate("num_cpus"),
    Crate("owning_ref"),
    Crate("parking_lot"),
    Crate("parking_lot_core"),
    Crate("pkg-config"),
    Crate("polonius-engine"),
    Crate("proc-macro2"),
    Crate("quick-error"),
    Crate("quote"),
    Crate("rand"),
    Crate("rand_chacha"),
    Crate("rand_core"),
    Crate("rand_hc"),
    Crate("rand_isaac"),
    Crate("rand_pcg"),
    Crate("rand_xorshift"),
    Crate("redox_syscall"),
    Crate("redox_termios"),
    Crate("regex"),
    Crate("regex-syntax"),
    Crate("remove_dir_all"),
    Crate("rustc-demangle"),
    Crate("rustc-hash"),
    Crate("rustc-rayon"),
    Crate("rustc-rayon-core"),
    Crate("rustc_version"),
    Crate("scoped-tls"),
    Crate("scopeguard"),
    Crate("semver"),
    Crate("semver-parser"),
    Crate("serde"),
    Crate("serde_derive"),
    Crate("smallvec"),
    Crate("stable_deref_trait"),
    Crate("syn"),
    Crate("synstructure"),
    Crate("tempfile"),
    Crate("termcolor"),
    Crate("terminon"),
    Crate("termion"),
    Crate("thread_local"),
    Crate("ucd-util"),
    Crate("unicode-width"),
    Crate("unicode-xid"),
    Crate("unreachable"),
    Crate("utf8-ranges"),
    Crate("vcpkg"),
    Crate("version_check"),
    Crate("void"),
    Crate("winapi"),
    Crate("winapi-build"),
    Crate("winapi-i686-pc-windows-gnu"),
    Crate("winapi-util"),
    Crate("winapi-x86_64-pc-windows-gnu"),
    Crate("wincolor"),
];

// Some types for Serde to deserialize the output of `cargo metadata` to.

#[derive(Deserialize)]
struct Output {
    resolve: Resolve,
}

#[derive(Deserialize)]
struct Resolve {
    nodes: Vec<ResolveNode>,
}

#[derive(Deserialize)]
struct ResolveNode {
    id: String,
    dependencies: Vec<String>,
}

/// A unique identifier for a crate.
#[derive(Copy, Clone, PartialOrd, Ord, PartialEq, Eq, Debug, Hash)]
struct Crate<'a>(&'a str); // (name)

#[derive(Copy, Clone, PartialOrd, Ord, PartialEq, Eq, Debug, Hash)]
struct CrateVersion<'a>(&'a str, &'a str); // (name, version)

impl Crate<'_> {
    pub fn id_str(&self) -> String {
        format!("{} ", self.0)
    }
}

impl<'a> CrateVersion<'a> {
    /// Returns the struct and whether or not the dependency is in-tree.
    pub fn from_str(s: &'a str) -> (Self, bool) {
        let mut parts = s.split(' ');
        let name = parts.next().unwrap();
        let version = parts.next().unwrap();
        let path = parts.next().unwrap();

        let is_path_dep = path.starts_with("(path+");

        (CrateVersion(name, version), is_path_dep)
    }

    pub fn id_str(&self) -> String {
        format!("{} {}", self.0, self.1)
    }
}

impl<'a> From<CrateVersion<'a>> for Crate<'a> {
    fn from(cv: CrateVersion<'a>) -> Crate<'a> {
        Crate(cv.0)
    }
}

/// Checks the dependency at the given path. Changes `bad` to `true` if a check failed.
///
/// Specifically, this checks that the license is correct.
pub fn check(path: &Path, bad: &mut bool) {
    // Check licences.
    let path = path.join("../vendor");
    assert!(path.exists(), "vendor directory missing");
    let mut saw_dir = false;
    for dir in t!(path.read_dir()) {
        saw_dir = true;
        let dir = t!(dir);

        // Skip our exceptions.
        let is_exception = EXCEPTIONS.iter().any(|exception| {
            dir.path()
                .to_str()
                .unwrap()
                .contains(&format!("vendor/{}", exception))
        });
        if is_exception {
            continue;
        }

        let toml = dir.path().join("Cargo.toml");
        *bad = !check_license(&toml) || *bad;
    }
    assert!(saw_dir, "no vendored source");
}

/// Checks the dependency of `WHITELIST_CRATES` at the given path. Changes `bad` to `true` if a
/// check failed.
///
/// Specifically, this checks that the dependencies are on the `WHITELIST`.
pub fn check_whitelist(path: &Path, cargo: &Path, bad: &mut bool) {
    // Get dependencies from Cargo metadata.
    let resolve = get_deps(path, cargo);

    // Get the whitelist in a convenient form.
    let whitelist: HashSet<_> = WHITELIST.iter().cloned().collect();

    // Check dependencies.
    let mut visited = BTreeSet::new();
    let mut unapproved = BTreeSet::new();
    for &krate in WHITELIST_CRATES.iter() {
        let mut bad = check_crate_whitelist(&whitelist, &resolve, &mut visited, krate, false);
        unapproved.append(&mut bad);
    }

    if !unapproved.is_empty() {
        println!("Dependencies not on the whitelist:");
        for dep in unapproved {
            println!("* {}", dep.id_str());
        }
        *bad = true;
    }

    check_crate_duplicate(&resolve, bad);
}

fn check_license(path: &Path) -> bool {
    if !path.exists() {
        panic!("{} does not exist", path.display());
    }
    let contents = t!(fs::read_to_string(&path));

    let mut found_license = false;
    for line in contents.lines() {
        if !line.starts_with("license") {
            continue;
        }
        let license = extract_license(line);
        if !LICENSES.contains(&&*license) {
            println!("invalid license {} in {}", license, path.display());
            return false;
        }
        found_license = true;
        break;
    }
    if !found_license {
        println!("no license in {}", path.display());
        return false;
    }

    true
}

fn extract_license(line: &str) -> String {
    let first_quote = line.find('"');
    let last_quote = line.rfind('"');
    if let (Some(f), Some(l)) = (first_quote, last_quote) {
        let license = &line[f + 1..l];
        license.into()
    } else {
        "bad-license-parse".into()
    }
}

/// Gets the dependencies of the crate at the given path using `cargo metadata`.
fn get_deps(path: &Path, cargo: &Path) -> Resolve {
    // Run `cargo metadata` to get the set of dependencies.
    let output = Command::new(cargo)
        .arg("metadata")
        .arg("--format-version")
        .arg("1")
        .arg("--manifest-path")
        .arg(path.join("../Cargo.toml"))
        .output()
        .expect("Unable to run `cargo metadata`")
        .stdout;
    let output = String::from_utf8_lossy(&output);
    let output: Output = serde_json::from_str(&output).unwrap();

    output.resolve
}

/// Checks the dependencies of the given crate from the given cargo metadata to see if they are on
/// the whitelist. Returns a list of illegal dependencies.
fn check_crate_whitelist<'a>(
    whitelist: &'a HashSet<Crate<'_>>,
    resolve: &'a Resolve,
    visited: &mut BTreeSet<CrateVersion<'a>>,
    krate: CrateVersion<'a>,
    must_be_on_whitelist: bool,
) -> BTreeSet<Crate<'a>> {
    // This will contain bad deps.
    let mut unapproved = BTreeSet::new();

    // Check if we have already visited this crate.
    if visited.contains(&krate) {
        return unapproved;
    }

    visited.insert(krate);

    // If this path is in-tree, we don't require it to be on the whitelist.
    if must_be_on_whitelist {
        // If this dependency is not on `WHITELIST`, add to bad set.
        if !whitelist.contains(&krate.into()) {
            unapproved.insert(krate.into());
        }
    }

    // Do a DFS in the crate graph (it's a DAG, so we know we have no cycles!).
    let to_check = resolve
        .nodes
        .iter()
        .find(|n| n.id.starts_with(&krate.id_str()))
        .expect("crate does not exist");

    for dep in to_check.dependencies.iter() {
        let (krate, is_path_dep) = CrateVersion::from_str(dep);

        let mut bad = check_crate_whitelist(whitelist, resolve, visited, krate, !is_path_dep);
        unapproved.append(&mut bad);
    }

    unapproved
}

fn check_crate_duplicate(resolve: &Resolve, bad: &mut bool) {
    const FORBIDDEN_TO_HAVE_DUPLICATES: &[&str] = &[
        // These two crates take quite a long time to build, so don't allow two versions of them
        // to accidentally sneak into our dependency graph, in order to ensure we keep our CI times
        // under control.

        "cargo",
        "rustc-ap-syntax",
    ];
    let mut name_to_id: HashMap<_, Vec<_>> = HashMap::new();
    for node in resolve.nodes.iter() {
        name_to_id.entry(node.id.split_whitespace().next().unwrap())
            .or_default()
            .push(&node.id);
    }

    for name in FORBIDDEN_TO_HAVE_DUPLICATES {
        if name_to_id[name].len() <= 1 {
            continue
        }
        println!("crate `{}` is duplicated in `Cargo.lock`", name);
        for id in name_to_id[name].iter() {
            println!("  * {}", id);
        }
        *bad = true;
    }
}
<|endoftext|>