<commit_before>use std::mem;

pub trait Flat: Sized {
    type Element;

    fn new() -> Self;
    fn with_capacity(capacity: usize) -> Self;

    fn swap_remove(&mut self, index: usize) -> Option<Self::Element>;
    fn replace(&mut self,
               index: usize,
               element: Self::Element)
               -> Result<Self::Element, Self::Element>;

    fn push(&mut self, element: Self::Element);
}

pub trait FlatGet {
    type ElementRef;
    fn flat_get(self, index: usize) -> Option<Self::ElementRef>;
}

pub trait FlatGetMut {
    type ElementRefMut;
    fn flat_get_mut(self, index: usize) -> Option<Self::ElementRefMut>;
}

pub trait FlatAccess {
    type Access;
    fn flat_access(self) -> Self::Access;
}

pub trait FlatAccessMut {
    type AccessMut;
    fn flat_access_mut(self) -> Self::AccessMut;
}

impl<T> Flat for Vec<T> {
    type Element = T;

    #[inline]
    fn new() -> Self {
        Vec::new()
    }

    #[inline]
    fn with_capacity(capacity: usize) -> Self {
        Vec::with_capacity(capacity)
    }

    #[inline]
    fn replace(&mut self,
               index: usize,
               element: Self::Element)
               -> Result<Self::Element, Self::Element> {
        match self.get_mut(index) {
            Some(old) => Ok(mem::replace(old, element)),
            None => Err(element),
        }
    }

    #[inline]
    fn swap_remove(&mut self, index: usize) -> Option<Self::Element> {
        let index = index;
        if index < self.len() {
            Some(self.swap_remove(index))
        } else {
            None
        }
    }

    #[inline]
    fn push(&mut self, element: Self::Element) {
        self.push(element);
    }
}

impl<'a, T> FlatGet for &'a Vec<T> {
    type ElementRef = &'a T;

    fn flat_get(self, index: usize) -> Option<Self::ElementRef> {
        self.get(index)
    }
}

impl<'a, T> FlatGetMut for &'a mut Vec<T> {
    type ElementRefMut = &'a mut T;

    fn flat_get_mut(self, index: usize) -> Option<Self::ElementRefMut> {
        self.get_mut(index)
    }
}

impl<'a, T> FlatAccess for &'a Vec<T> {
    type Access = &'a [T];

    fn flat_access(self) -> Self::Access {
        &self[..]
    }
}

impl<'a, T> FlatAccessMut for &'a mut Vec<T> {
    type AccessMut = &'a mut [T];

    fn flat_access_mut(self) -> Self::AccessMut {
        &mut self[..]
    }
}

#[macro_export]
macro_rules! derive_flat {
    (
        #[element($element:ident, & $element_ref:ident, &mut $element_ref_mut:ident)]
        #[access(& $access:ident, &mut $access_mut:ident)]
        pub struct $struct_name:ident {
            $(#[element($element_field:ident)]
              pub $field:ident : $field_type:ty,)+
        }
    ) => {
        pub struct $struct_name {
            pub $($field : $field_type,)+
        }

        pub struct $element {
            pub $($element_field : <$field_type as $crate::Flat>::Element,)+
        }

        pub struct $element_ref <'a> {
            pub $($element_field : <&'a $field_type as $crate::FlatGet>::ElementRef,)+
        }

        pub struct $element_ref_mut <'a> {
            pub $($element_field : <&'a mut $field_type as $crate::FlatGetMut>::ElementRefMut,)+
        }

        pub struct $access <'a> {
            pub $($field : <&'a $field_type as $crate::FlatAccess>::Access,)+
        }

        pub struct $access_mut <'a> {
            pub $($field : <&'a mut $field_type as $crate::FlatAccessMut>::AccessMut,)+
        }

        impl $crate::Flat for $struct_name {
            type Element = $element;

            #[inline]
            fn new() -> Self {
                $struct_name {
                    $($field : $crate::Flat::new()),+
                }
            }

            #[inline]
            fn with_capacity(capacity: usize) -> Self {
                $struct_name {
                    $($field : $crate::Flat::with_capacity(capacity)),+
                }
            }

            #[inline]
            fn replace(&mut self,
                       index: usize,
                       element: Self::Element)
                       -> Result<Self::Element, Self::Element> {
                match ($($crate::Flat::replace(&mut self.$field,
                                               index,
                                               element.$element_field),)+) {
                    ($(Ok($element_field),)+) => Ok($element {
                        $($element_field: $element_field,)+
                    }),
                    ($(Err($element_field),)+) => Err($element {
                        $($element_field: $element_field,)+
                    }),
                    _ => panic!("out of lockstep derived flat in `replace`"),
                }
            }

            #[inline]
            fn swap_remove(&mut self, index: usize) -> Option<Self::Element> {
                match ($($crate::Flat::swap_remove(&mut self.$field, index),)+) {
                    ($(Some($element_field),)+) => Some($element {
                        $($element_field: $element_field,)+
                    }),
                    ($($element_field@None,)+) => None,
                    _ => panic!("out of lockstep derived flat in `swap_remove`"),
                }
            }

            #[inline]
            fn push(&mut self, element: Self::Element) {
                $($crate::Flat::push(&mut self.$field, element.$element_field);)+
            }
        }

        impl<'a> FlatGet for &'a $struct_name {
            type ElementRef = $element_ref <'a>;

            fn flat_get(self, index: usize) -> Option<Self::ElementRef> {
                match ($($crate::FlatGet::flat_get(&self.$field, index),)+) {
                    ($(Some($element_field),)+) => Some($element_ref {
                        $($element_field: $element_field,)+
                    }),
                    ($($element_field@None,)+) => None,
                    _ => panic!("out of lockstep derived flat in `flat_get`"),
                }
            }
        }

        impl<'a> FlatGetMut for &'a mut $struct_name {
            type ElementRefMut = $element_ref_mut <'a>;

            fn flat_get_mut(self, index: usize) -> Option<Self::ElementRefMut> {
                match ($($crate::FlatGetMut::flat_get_mut(&mut self.$field, index),)+) {
                    ($(Some($element_field),)+) => Some($element_ref_mut {
                        $($element_field: $element_field,)+
                    }),
                    ($($element_field@None,)+) => None,
                    _ => panic!("out of lockstep derived flat in `flat_get_mut`"),
                }
            }
        }

        impl<'a> FlatAccess for &'a $struct_name {
            type Access = $access <'a>;

            fn flat_access(self) -> Self::Access {
                $access {
                    $($field: $crate::FlatAccess::flat_access(&self.$field),)+
                }
            }
        }

        impl<'a> FlatAccessMut for &'a mut $struct_name {
            type AccessMut = $access_mut <'a>;

            fn flat_access_mut(self) -> Self::AccessMut {
                $access_mut {
                    $($field: $crate::FlatAccessMut::flat_access_mut(&mut self.$field),)+
                }
            }
        }
    }
}
<commit_msg>got rid of dead code warnings in derive_flat<commit_after>use std::mem;

pub trait Flat: Sized {
    type Element;

    fn new() -> Self;
    fn with_capacity(capacity: usize) -> Self;

    fn swap_remove(&mut self, index: usize) -> Option<Self::Element>;
    fn replace(&mut self,
               index: usize,
               element: Self::Element)
               -> Result<Self::Element, Self::Element>;

    fn push(&mut self, element: Self::Element);
}

pub trait FlatGet {
    type ElementRef;
    fn flat_get(self, index: usize) -> Option<Self::ElementRef>;
}

pub trait FlatGetMut {
    type ElementRefMut;
    fn flat_get_mut(self, index: usize) -> Option<Self::ElementRefMut>;
}

pub trait FlatAccess {
    type Access;
    fn flat_access(self) -> Self::Access;
}

pub trait FlatAccessMut {
    type AccessMut;
    fn flat_access_mut(self) -> Self::AccessMut;
}

impl<T> Flat for Vec<T> {
    type Element = T;

    #[inline]
    fn new() -> Self {
        Vec::new()
    }

    #[inline]
    fn with_capacity(capacity: usize) -> Self {
        Vec::with_capacity(capacity)
    }

    #[inline]
    fn replace(&mut self,
               index: usize,
               element: Self::Element)
               -> Result<Self::Element, Self::Element> {
        match self.get_mut(index) {
            Some(old) => Ok(mem::replace(old, element)),
            None => Err(element),
        }
    }

    #[inline]
    fn swap_remove(&mut self, index: usize) -> Option<Self::Element> {
        let index = index;
        if index < self.len() {
            Some(self.swap_remove(index))
        } else {
            None
        }
    }

    #[inline]
    fn push(&mut self, element: Self::Element) {
        self.push(element);
    }
}

impl<'a, T> FlatGet for &'a Vec<T> {
    type ElementRef = &'a T;

    fn flat_get(self, index: usize) -> Option<Self::ElementRef> {
        self.get(index)
    }
}

impl<'a, T> FlatGetMut for &'a mut Vec<T> {
    type ElementRefMut = &'a mut T;

    fn flat_get_mut(self, index: usize) -> Option<Self::ElementRefMut> {
        self.get_mut(index)
    }
}

impl<'a, T> FlatAccess for &'a Vec<T> {
    type Access = &'a [T];

    fn flat_access(self) -> Self::Access {
        &self[..]
    }
}

impl<'a, T> FlatAccessMut for &'a mut Vec<T> {
    type AccessMut = &'a mut [T];

    fn flat_access_mut(self) -> Self::AccessMut {
        &mut self[..]
    }
}

#[macro_export]
macro_rules! derive_flat {
    (
        #[element($element:ident, & $element_ref:ident, &mut $element_ref_mut:ident)]
        #[access(& $access:ident, &mut $access_mut:ident)]
        pub struct $struct_name:ident {
            $(#[element($element_field:ident)]
              pub $field:ident : $field_type:ty,)+
        }
    ) => {
        pub struct $struct_name {
            $(pub $field : $field_type,)+
        }

        #[allow(dead_code)]
        #[allow(unused_variables)]
        pub struct $element {
            $(pub $element_field : <$field_type as $crate::Flat>::Element,)+
        }

        #[allow(dead_code)]
        #[allow(unused_variables)]
        pub struct $element_ref <'a> {
            $(pub $element_field : <&'a $field_type as $crate::FlatGet>::ElementRef,)+
        }

        #[allow(dead_code)]
        #[allow(unused_variables)]
        pub struct $element_ref_mut <'a> {
            $(pub $element_field : <&'a mut $field_type as $crate::FlatGetMut>::ElementRefMut,)+
        }

        #[allow(dead_code)]
        #[allow(unused_variables)]
        pub struct $access <'a> {
            $(pub $field : <&'a $field_type as $crate::FlatAccess>::Access,)+
        }

        #[allow(dead_code)]
        #[allow(unused_variables)]
        pub struct $access_mut <'a> {
            $(pub $field : <&'a mut $field_type as $crate::FlatAccessMut>::AccessMut,)+
        }

        impl $crate::Flat for $struct_name {
            type Element = $element;

            #[inline]
            fn new() -> Self {
                $struct_name {
                    $($field : $crate::Flat::new()),+
                }
            }

            #[inline]
            fn with_capacity(capacity: usize) -> Self {
                $struct_name {
                    $($field : $crate::Flat::with_capacity(capacity)),+
                }
            }

            #[inline]
            fn replace(&mut self,
                       index: usize,
                       element: Self::Element)
                       -> Result<Self::Element, Self::Element> {
                match ($($crate::Flat::replace(&mut self.$field,
                                               index,
                                               element.$element_field),)+) {
                    ($(Ok($element_field),)+) => Ok($element {
                        $($element_field: $element_field,)+
                    }),
                    ($(Err($element_field),)+) => Err($element {
                        $($element_field: $element_field,)+
                    }),
                    _ => panic!("out of lockstep derived flat in `replace`"),
                }
            }

            #[inline]
            #[allow(dead_code)]
            #[allow(unused_variables)]
            fn swap_remove(&mut self, index: usize) -> Option<Self::Element> {
                match ($($crate::Flat::swap_remove(&mut self.$field, index),)+) {
                    ($(Some($element_field),)+) => Some($element {
                        $($element_field: $element_field,)+
                    }),
                    ($($element_field@None,)+) => None,
                    _ => panic!("out of lockstep derived flat in `swap_remove`"),
                }
            }

            #[inline]
            fn push(&mut self, element: Self::Element) {
                $($crate::Flat::push(&mut self.$field, element.$element_field);)+
            }
        }

        impl<'a> $crate::FlatGet for &'a $struct_name {
            type ElementRef = $element_ref <'a>;

            #[allow(dead_code)]
            #[allow(unused_variables)]
            fn flat_get(self, index: usize) -> Option<Self::ElementRef> {
                match ($($crate::FlatGet::flat_get(&self.$field, index),)+) {
                    ($(Some($element_field),)+) => Some($element_ref {
                        $($element_field: $element_field,)+
                    }),
                    ($($element_field@None,)+) => None,
                    _ => panic!("out of lockstep derived flat in `flat_get`"),
                }
            }
        }

        impl<'a> $crate::FlatGetMut for &'a mut $struct_name {
            type ElementRefMut = $element_ref_mut <'a>;

            #[allow(dead_code)]
            #[allow(unused_variables)]
            fn flat_get_mut(self, index: usize) -> Option<Self::ElementRefMut> {
                match ($($crate::FlatGetMut::flat_get_mut(&mut self.$field, index),)+) {
                    ($(Some($element_field),)+) => Some($element_ref_mut {
                        $($element_field: $element_field,)+
                    }),
                    ($($element_field@None,)+) => None,
                    _ => panic!("out of lockstep derived flat in `flat_get_mut`"),
                }
            }
        }

        impl<'a> $crate::FlatAccess for &'a $struct_name {
            type Access = $access <'a>;

            fn flat_access(self) -> Self::Access {
                $access {
                    $($field: $crate::FlatAccess::flat_access(&self.$field),)+
                }
            }
        }

        impl<'a> $crate::FlatAccessMut for &'a mut $struct_name {
            type AccessMut = $access_mut <'a>;

            fn flat_access_mut(self) -> Self::AccessMut {
                $access_mut {
                    $($field: $crate::FlatAccessMut::flat_access_mut(&mut self.$field),)+
                }
            }
        }
    }
}
<|endoftext|>


<commit_before>use piston_window::*;

use board::Board;
use randomizer::Randomizer;
use rendering::Position;
use tetromino::*;
use settings::*;
use stats::GameStats;

#[derive(Debug, PartialEq)]
pub enum GameState {
    Playing,
    Paused,
    GameOver
}

pub trait Game {
    fn on_input(&mut self, input: Input);
    fn on_update(&mut self, update_args: UpdateArgs);
    fn render(&mut self, context: Context, graphics: &mut G2d);
}

pub struct Rustris {
    board: Board,
    randomizer: Randomizer,
    current_piece: Piece,
    next_piece: Piece,
    stats: GameStats,
    time_since_moved: f64,
    state: GameState
}
impl Rustris {
    pub fn new() -> Rustris {
        let mut randomizer = Randomizer::new();
        let current_piece = randomizer.create_piece();
        let next_piece = randomizer.create_piece();
        Rustris {
            board: Board::new(),
            randomizer: randomizer,
            current_piece: current_piece,
            next_piece: next_piece,
            stats: GameStats::new(),
            time_since_moved: 0.0,
            state: GameState::Playing
        }
    }

    pub fn reset(&mut self) {
        self.board = Board::new();
        let mut randomizer = Randomizer::new();
        self.current_piece = randomizer.create_piece();
        self.next_piece = randomizer.create_piece();
        self.randomizer = randomizer;
        self.stats = GameStats::new();
        self.time_since_moved = 0.0;
        self.set_game_state(GameState::Playing);
    }

    pub fn get_game_stats(&self) -> &GameStats {
        &self.stats
    }

    pub fn get_next_piece(&self) -> &Piece {
        &self.next_piece
    }

    pub fn set_current_piece(&mut self, piece: Piece) {
        self.current_piece = piece;
    }

    pub fn is_playing(&self) -> bool {
        self.state == GameState::Playing
    }

    pub fn is_paused(&self) -> bool {
        self.state == GameState::Paused
    }

    pub fn is_game_over(&self) -> bool {
        self.state == GameState::GameOver
    }

    pub fn set_game_state(&mut self, state: GameState) {
        self.state = state;
    }

    fn drop_delay(&self) -> f64 {
        ((MAX_GAME_LEVEL + 1) - self.stats.get_level()) as f64 * 0.10
    }

    fn is_valid_board_position(&self, piece: &Piece) -> bool {
        // TODO: Add tests for this
        piece.blocks_iter().all(|block| {
            (block.x >= 0 && block.x < WIDTH_IN_BLOCKS &&
             block.y >= 0 && block.y < HEIGHT_IN_BLOCKS &&
             !self.board.is_space_occupied(block))
        })
    }

    fn lock_current_piece(&mut self) {
        self.board.set_piece(&self.current_piece);
        self.remove_completed_lines();
        self.get_new_piece();
    }

    fn remove_completed_lines(&mut self) {
        let number_removed = self.board.remove_completed_rows();
        self.stats.score_completed_lines(number_removed);
    }

    fn calculate_ghost_piece(&self) -> Piece {
        let mut ghost = self.current_piece;
        while self.is_valid_board_position(&ghost) {
            ghost.y += 1;
        }
        ghost.y -= 1;
        ghost
    }

    fn get_new_piece(&mut self) {
        let next = self.next_piece;
        if self.is_valid_board_position(&next){
            self.set_current_piece(next);
            self.next_piece = self.randomizer.create_piece();
        } else {
            self.state = GameState::GameOver;
        }
    }

    fn update(&mut self) {
        let moved = self.current_piece.moved(Direction::Down);
        if !self.is_valid_board_position(&moved) {
            self.lock_current_piece();
        } else {
            self.set_current_piece(moved);
        }
    }

    fn handle_playing_input(&mut self, input: Input) {
        let mut moved: Option<Piece> = None;
        match input {
            Input::Press(key) => {
                match key {
                    Button::Keyboard(Key::Up) => {
                        moved = Some(self.current_piece.rotated());
                    }
                    Button::Keyboard(Key::Down) => {
                        let move_down = self.current_piece.moved(Direction::Down);
                        if self.is_valid_board_position(&move_down) {
                            self.time_since_moved = 0.0;
                            self.stats.score_soft_drop();
                            self.set_current_piece(move_down);
                        }
                    }
                    Button::Keyboard(Key::Left) => {
                        moved = Some(self.current_piece.moved(Direction::Left));
                    }
                    Button::Keyboard(Key::Right) => {
                        moved = Some(self.current_piece.moved(Direction::Right));
                    }
                    Button::Keyboard(Key::Space) => {
                        let ghost = self.calculate_ghost_piece();
                        let rows_dropped = (ghost.y - self.current_piece.y) as u32;
                        self.stats.score_hard_drop(rows_dropped);
                        self.set_current_piece(ghost);
                        self.lock_current_piece();
                    }
                    Button::Keyboard(Key::P) => {
                        self.state = GameState::Paused
                    }
                    _ => {}
                }
            }
            _ => {}
        }
        if let Some(piece) = moved {
            if self.is_valid_board_position(&piece) {
                self.set_current_piece(piece);
            }
        }
    }

    fn handle_paused_input(&mut self, input: Input) {
        match input {
            Input::Press(key) => {
                match key {
                    Button::Keyboard(Key::P) => {
                        self.state = GameState::Playing
                    },
                    _ => {}
                }
            },
            _ => {}
        }
    }
}
impl Game for Rustris {
    fn on_input(&mut self, input: Input) {
        match self.state {
            GameState::Playing => self.handle_playing_input(input),
            GameState::Paused => self.handle_paused_input(input),
            _ => {}
        }
    }

    fn on_update(&mut self, update_args: UpdateArgs) {
        match self.state {
            GameState::Playing => {
                self.time_since_moved += update_args.dt;
                let delay = self.drop_delay();
                if self.time_since_moved >= delay {
                    self.time_since_moved -= delay;
                    self.update();
                }
            },
            _ => {}
        }
    }

    fn render(&mut self, context: Context, graphics: &mut G2d) {
        self.board.render(context, graphics);
        self.calculate_ghost_piece().render_in_grid(RenderType::Ghost, context, graphics);
        self.current_piece.render_in_grid(RenderType::Normal, context, graphics);
        self.next_piece.render(Position::new(553.0, 80.0), RenderType::Normal, context, graphics);
    }
}
<commit_msg>remove the unnecessary method<commit_after>use piston_window::*;

use board::Board;
use randomizer::Randomizer;
use rendering::Position;
use tetromino::*;
use settings::*;
use stats::GameStats;

#[derive(Debug, PartialEq)]
pub enum GameState {
    Playing,
    Paused,
    GameOver
}

pub trait Game {
    fn on_input(&mut self, input: Input);
    fn on_update(&mut self, update_args: UpdateArgs);
    fn render(&mut self, context: Context, graphics: &mut G2d);
}

pub struct Rustris {
    board: Board,
    randomizer: Randomizer,
    current_piece: Piece,
    next_piece: Piece,
    stats: GameStats,
    time_since_moved: f64,
    state: GameState
}
impl Rustris {
    pub fn new() -> Rustris {
        let mut randomizer = Randomizer::new();
        let current_piece = randomizer.create_piece();
        let next_piece = randomizer.create_piece();
        Rustris {
            board: Board::new(),
            randomizer: randomizer,
            current_piece: current_piece,
            next_piece: next_piece,
            stats: GameStats::new(),
            time_since_moved: 0.0,
            state: GameState::Playing
        }
    }

    pub fn reset(&mut self) {
        self.board = Board::new();
        let mut randomizer = Randomizer::new();
        self.current_piece = randomizer.create_piece();
        self.next_piece = randomizer.create_piece();
        self.randomizer = randomizer;
        self.stats = GameStats::new();
        self.time_since_moved = 0.0;
        self.set_game_state(GameState::Playing);
    }

    pub fn get_game_stats(&self) -> &GameStats {
        &self.stats
    }

    pub fn set_current_piece(&mut self, piece: Piece) {
        self.current_piece = piece;
    }

    pub fn is_playing(&self) -> bool {
        self.state == GameState::Playing
    }

    pub fn is_paused(&self) -> bool {
        self.state == GameState::Paused
    }

    pub fn is_game_over(&self) -> bool {
        self.state == GameState::GameOver
    }

    pub fn set_game_state(&mut self, state: GameState) {
        self.state = state;
    }

    fn drop_delay(&self) -> f64 {
        ((MAX_GAME_LEVEL + 1) - self.stats.get_level()) as f64 * 0.10
    }

    fn is_valid_board_position(&self, piece: &Piece) -> bool {
        // TODO: Add tests for this
        piece.blocks_iter().all(|block| {
            (block.x >= 0 && block.x < WIDTH_IN_BLOCKS &&
             block.y >= 0 && block.y < HEIGHT_IN_BLOCKS &&
             !self.board.is_space_occupied(block))
        })
    }

    fn lock_current_piece(&mut self) {
        self.board.set_piece(&self.current_piece);
        self.remove_completed_lines();
        self.get_new_piece();
    }

    fn remove_completed_lines(&mut self) {
        let number_removed = self.board.remove_completed_rows();
        self.stats.score_completed_lines(number_removed);
    }

    fn calculate_ghost_piece(&self) -> Piece {
        let mut ghost = self.current_piece;
        while self.is_valid_board_position(&ghost) {
            ghost.y += 1;
        }
        ghost.y -= 1;
        ghost
    }

    fn get_new_piece(&mut self) {
        let next = self.next_piece;
        if self.is_valid_board_position(&next){
            self.set_current_piece(next);
            self.next_piece = self.randomizer.create_piece();
        } else {
            self.state = GameState::GameOver;
        }
    }

    fn update(&mut self) {
        let moved = self.current_piece.moved(Direction::Down);
        if !self.is_valid_board_position(&moved) {
            self.lock_current_piece();
        } else {
            self.set_current_piece(moved);
        }
    }

    fn handle_playing_input(&mut self, input: Input) {
        let mut moved: Option<Piece> = None;
        match input {
            Input::Press(key) => {
                match key {
                    Button::Keyboard(Key::Up) => {
                        moved = Some(self.current_piece.rotated());
                    }
                    Button::Keyboard(Key::Down) => {
                        let move_down = self.current_piece.moved(Direction::Down);
                        if self.is_valid_board_position(&move_down) {
                            self.time_since_moved = 0.0;
                            self.stats.score_soft_drop();
                            self.set_current_piece(move_down);
                        }
                    }
                    Button::Keyboard(Key::Left) => {
                        moved = Some(self.current_piece.moved(Direction::Left));
                    }
                    Button::Keyboard(Key::Right) => {
                        moved = Some(self.current_piece.moved(Direction::Right));
                    }
                    Button::Keyboard(Key::Space) => {
                        let ghost = self.calculate_ghost_piece();
                        let rows_dropped = (ghost.y - self.current_piece.y) as u32;
                        self.stats.score_hard_drop(rows_dropped);
                        self.set_current_piece(ghost);
                        self.lock_current_piece();
                    }
                    Button::Keyboard(Key::P) => {
                        self.state = GameState::Paused
                    }
                    _ => {}
                }
            }
            _ => {}
        }
        if let Some(piece) = moved {
            if self.is_valid_board_position(&piece) {
                self.set_current_piece(piece);
            }
        }
    }

    fn handle_paused_input(&mut self, input: Input) {
        match input {
            Input::Press(key) => {
                match key {
                    Button::Keyboard(Key::P) => {
                        self.state = GameState::Playing
                    },
                    _ => {}
                }
            },
            _ => {}
        }
    }
}
impl Game for Rustris {
    fn on_input(&mut self, input: Input) {
        match self.state {
            GameState::Playing => self.handle_playing_input(input),
            GameState::Paused => self.handle_paused_input(input),
            _ => {}
        }
    }

    fn on_update(&mut self, update_args: UpdateArgs) {
        match self.state {
            GameState::Playing => {
                self.time_since_moved += update_args.dt;
                let delay = self.drop_delay();
                if self.time_since_moved >= delay {
                    self.time_since_moved -= delay;
                    self.update();
                }
            },
            _ => {}
        }
    }

    fn render(&mut self, context: Context, graphics: &mut G2d) {
        self.board.render(context, graphics);
        self.calculate_ghost_piece().render_in_grid(RenderType::Ghost, context, graphics);
        self.current_piece.render_in_grid(RenderType::Normal, context, graphics);
        self.next_piece.render(Position::new(553.0, 80.0), RenderType::Normal, context, graphics);
    }
}
<|endoftext|>


<commit_before>use petgraph::{Directed, EdgeDirection, Graph};
use petgraph::graph::NodeIndex;
use petgraph::algo::{connected_components, scc};
use triadic_census::{OptDenseDigraph, TriadicCensus};
use graph_neighbor_matching::{Edge, GraphSimilarityMatrix, IgnoreNodeColors};
use super::fitness_function::FitnessFunction;
use closed01::Closed01;
use std::fmt::Debug;

use graph_neighbor_matching::Graph as NGraph;

// #[derive(Debug)]
pub struct Goal<N: Debug, E: Debug> {
    _target_graph: OptDenseDigraph<N, E>,
    target_census: TriadicCensus,
    target_connected_components: usize,
    target_strongly_connected_components: usize,
    target_in_a: Vec<Vec<Edge>>,
    target_out_a: Vec<Vec<Edge>>,
}

fn graph_to_edgelist<N: Debug, E: Debug>(g: &Graph<N, E, Directed>)
                                         -> (Vec<Vec<Edge>>, Vec<Vec<Edge>>) {
    let mut in_a: Vec<Vec<Edge>> = Vec::with_capacity(g.node_count());
    let mut out_a: Vec<Vec<Edge>> = Vec::with_capacity(g.node_count());
    for ni in 0..g.node_count() {
        let mut in_neighbors: Vec<Edge> = Vec::new();
        let mut out_neighbors: Vec<Edge> = Vec::new();
        for n in g.neighbors_directed(NodeIndex::new(ni), EdgeDirection::Incoming) {
            in_neighbors.push(Edge::new(n.index(), Closed01::zero()));
        }
        for n in g.neighbors_directed(NodeIndex::new(ni), EdgeDirection::Outgoing) {
            out_neighbors.push(Edge::new(n.index(), Closed01::zero()));
        }
        in_a.push(in_neighbors);
        out_a.push(out_neighbors);
    }

    (in_a, out_a)
}

impl<N: Clone + Default + Debug, E: Clone + Default + Debug> Goal<N, E> {
    pub fn new(g: OptDenseDigraph<N, E>) -> Goal<N, E> {
        let census = TriadicCensus::from(&g);
        let (in_a, out_a) = graph_to_edgelist(g.ref_graph());
        let cc = connected_components(g.ref_graph());
        let scc = scc(g.ref_graph()).len();
        Goal {
            _target_graph: g,
            target_census: census,
            target_connected_components: cc,
            target_strongly_connected_components: scc,
            target_in_a: in_a,
            target_out_a: out_a,
        }
    }

    pub fn apply_fitness_function(&self,
                                  fitfun: FitnessFunction,
                                  g: &OptDenseDigraph<(), ()>)
                                  -> f32 {
        match fitfun {
            FitnessFunction::ConnectedComponents => self.connected_components_distance(g) as f32,
            FitnessFunction::StronglyConnectedComponents => {
                self.strongly_connected_components_distance(g) as f32
            }
            FitnessFunction::NeighborMatching => self.neighbor_matching_score(g) as f32,
            FitnessFunction::TriadicDistance => self.triadic_distance(g) as f32,
        }
    }

    pub fn triadic_distance<A: Debug, B: Debug>(&self, g: &OptDenseDigraph<A, B>) -> f64 {
        let census = TriadicCensus::from(g);
        TriadicCensus::distance(&self.target_census, &census)
    }

    pub fn connected_components_distance<A: Default+Debug, B: Default+Debug>(&self,
                                                                 g: &OptDenseDigraph<A, B>)
                                                                 -> usize {
        let cc = connected_components(g.ref_graph()) as isize;
        ((self.target_connected_components as isize) - cc).abs() as usize
    }
    pub fn strongly_connected_components_distance<A: Default + Debug, B: Default + Debug>
        (&self,
         g: &OptDenseDigraph<A, B>)
         -> usize {
        let scc = scc(g.ref_graph()).len() as isize;
        ((self.target_strongly_connected_components as isize) - scc).abs() as usize
    }

    pub fn neighbor_matching_score<A: Default + Debug, B: Default + Debug>(&self,
                                                                           g: &OptDenseDigraph<A,
                                                                                B>)
                                                                           -> f32 {
        let (in_b, out_b) = graph_to_edgelist(g.ref_graph());
        let mut sim = GraphSimilarityMatrix::new(NGraph::new(&self.target_in_a[..],
                                                             &self.target_out_a[..]),
                                                 NGraph::new(&in_b[..], &out_b[..]),
                                                 IgnoreNodeColors);
        // XXX
        sim.iterate(20, 0.1);

        let score = sim.score_sum_norm_min_degree(None);

        let score = 1.0 - score.get();
        score
    }
}
<commit_msg>Use the max_degree norm for neighbor matching<commit_after>use petgraph::{Directed, EdgeDirection, Graph};
use petgraph::graph::NodeIndex;
use petgraph::algo::{connected_components, scc};
use triadic_census::{OptDenseDigraph, TriadicCensus};
use graph_neighbor_matching::{Edge, GraphSimilarityMatrix, IgnoreNodeColors};
use super::fitness_function::FitnessFunction;
use closed01::Closed01;
use std::fmt::Debug;

use graph_neighbor_matching::Graph as NGraph;

// #[derive(Debug)]
pub struct Goal<N: Debug, E: Debug> {
    _target_graph: OptDenseDigraph<N, E>,
    target_census: TriadicCensus,
    target_connected_components: usize,
    target_strongly_connected_components: usize,
    target_in_a: Vec<Vec<Edge>>,
    target_out_a: Vec<Vec<Edge>>,
}

fn graph_to_edgelist<N: Debug, E: Debug>(g: &Graph<N, E, Directed>)
                                         -> (Vec<Vec<Edge>>, Vec<Vec<Edge>>) {
    let mut in_a: Vec<Vec<Edge>> = Vec::with_capacity(g.node_count());
    let mut out_a: Vec<Vec<Edge>> = Vec::with_capacity(g.node_count());
    for ni in 0..g.node_count() {
        let mut in_neighbors: Vec<Edge> = Vec::new();
        let mut out_neighbors: Vec<Edge> = Vec::new();
        for n in g.neighbors_directed(NodeIndex::new(ni), EdgeDirection::Incoming) {
            in_neighbors.push(Edge::new(n.index(), Closed01::zero()));
        }
        for n in g.neighbors_directed(NodeIndex::new(ni), EdgeDirection::Outgoing) {
            out_neighbors.push(Edge::new(n.index(), Closed01::zero()));
        }
        in_a.push(in_neighbors);
        out_a.push(out_neighbors);
    }

    (in_a, out_a)
}

impl<N: Clone + Default + Debug, E: Clone + Default + Debug> Goal<N, E> {
    pub fn new(g: OptDenseDigraph<N, E>) -> Goal<N, E> {
        let census = TriadicCensus::from(&g);
        let (in_a, out_a) = graph_to_edgelist(g.ref_graph());
        let cc = connected_components(g.ref_graph());
        let scc = scc(g.ref_graph()).len();
        Goal {
            _target_graph: g,
            target_census: census,
            target_connected_components: cc,
            target_strongly_connected_components: scc,
            target_in_a: in_a,
            target_out_a: out_a,
        }
    }

    pub fn apply_fitness_function(&self,
                                  fitfun: FitnessFunction,
                                  g: &OptDenseDigraph<(), ()>)
                                  -> f32 {
        match fitfun {
            FitnessFunction::ConnectedComponents => self.connected_components_distance(g) as f32,
            FitnessFunction::StronglyConnectedComponents => {
                self.strongly_connected_components_distance(g) as f32
            }
            FitnessFunction::NeighborMatching => self.neighbor_matching_score(g) as f32,
            FitnessFunction::TriadicDistance => self.triadic_distance(g) as f32,
        }
    }

    pub fn triadic_distance<A: Debug, B: Debug>(&self, g: &OptDenseDigraph<A, B>) -> f64 {
        let census = TriadicCensus::from(g);
        TriadicCensus::distance(&self.target_census, &census)
    }

    pub fn connected_components_distance<A: Default+Debug, B: Default+Debug>(&self,
                                                                 g: &OptDenseDigraph<A, B>)
                                                                 -> usize {
        let cc = connected_components(g.ref_graph()) as isize;
        ((self.target_connected_components as isize) - cc).abs() as usize
    }
    pub fn strongly_connected_components_distance<A: Default + Debug, B: Default + Debug>
        (&self,
         g: &OptDenseDigraph<A, B>)
         -> usize {
        let scc = scc(g.ref_graph()).len() as isize;
        ((self.target_strongly_connected_components as isize) - scc).abs() as usize
    }

    pub fn neighbor_matching_score<A: Default + Debug, B: Default + Debug>(&self,
                                                                           g: &OptDenseDigraph<A,
                                                                                B>)
                                                                           -> f32 {
        let (in_b, out_b) = graph_to_edgelist(g.ref_graph());
        let mut sim = GraphSimilarityMatrix::new(NGraph::new(&self.target_in_a[..],
                                                             &self.target_out_a[..]),
                                                 NGraph::new(&in_b[..], &out_b[..]),
                                                 IgnoreNodeColors);
        // XXX
        sim.iterate(20, 0.1);

        let score = sim.score_sum_norm_max_degree(None);

        let score = 1.0 - score.get();
        score
    }
}
<|endoftext|>


<commit_before>use util::{ZOPFLI_WINDOW_MASK, ZOPFLI_MIN_MATCH};

const HASH_SHIFT: i32 = 5;
const HASH_MASK: i32 = 32767;

#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum Which {
    Hash1,
    Hash2,
}

pub struct ZopfliHash {
    head: Vec<i32>,  /* Hash value to index of its most recent occurrence. */
    prev: Vec<u16>,  /* Index to index of prev. occurrence of same hash. */
    hashval: Vec<i32>,  /* Index to hash value at this index. */
    val: i32,  /* Current hash value. */

    /* Fields with similar purpose as the above hash, but for the second hash with
    a value that is calculated differently.  */
    head2: Vec<i32>,  /* Hash value to index of its most recent occurrence. */
    prev2: Vec<u16>,  /* Index to index of prev. occurrence of same hash. */
    hashval2: Vec<i32>,  /* Index to hash value at this index. */
    val2: i32,  /* Current hash value. */

    pub same: Vec<u16>,  /* Amount of repetitions of same byte after this .*/
}

impl ZopfliHash {
    pub fn new(window_size: usize) -> ZopfliHash {
        ZopfliHash {
           head: vec![-1; 65536],
           prev: (0..window_size as u16).collect(),
           hashval: vec![-1; window_size],
           val: 0,

           /* Fields with similar purpose as the above hash, but for the second hash with
           a value that is calculated differently.  */
           head2: vec![-1; 65536],
           prev2: (0..window_size as u16).collect(),
           hashval2: vec![-1; window_size],
           val2: 0,

           same: vec![0; window_size],
       }
    }

    pub fn reset(&mut self, window_size: usize) {
        self.val = 0;
        self.head = vec![-1; 65536];
        self.prev = (0..window_size as u16).collect();
        self.hashval = vec![-1; window_size];

        self.same = vec![0; window_size];
        self.val2 = 0;
        self.head2 = vec![-1; 65536];
        self.prev2 = (0..window_size as u16).collect();
        self.hashval2 = vec![-1; window_size];
    }

    pub fn warmup(&mut self, arr: &[u8], pos: usize, end: usize) {
        let c = arr[pos];
        self.update_val(c);

        if pos + 1 < end {
            let c = arr[pos + 1];
            self.update_val(c);
        }
    }

    /// Update the sliding hash value with the given byte. All calls to this function
    /// must be made on consecutive input characters. Since the hash value exists out
    /// of multiple input bytes, a few warmups with this function are needed initially.
    fn update_val(&mut self, c: u8) {
        self.val = ((self.val << HASH_SHIFT) ^ c as i32) & HASH_MASK;
    }

    pub fn update(&mut self, array: &[u8], pos: usize) {
        let hash_value = array.get(pos + ZOPFLI_MIN_MATCH - 1).cloned().unwrap_or(0);
        self.update_val(hash_value);

        let hpos = pos & ZOPFLI_WINDOW_MASK;
        self.hashval[hpos] = self.val;

        let index = self.val as usize;
        if self.head[index] != -1 && self.hashval[self.head[index] as usize] == self.val {
            self.prev[hpos] = self.head[index] as u16;
        } else {
            self.prev[hpos] = hpos as u16;
        }
        self.head[index] = hpos as i32;

        // Update "same".
        let mut amount = 0;
        let same_index = pos.wrapping_sub(1) & ZOPFLI_WINDOW_MASK;
        if self.same[same_index] > 1 {
            amount = self.same[same_index] as i32 - 1;
        }

        let mut another_index = pos + amount as usize + 1;
        while another_index < array.len() && array[pos] == array[another_index] && amount < -1 {
            amount += 1;
            another_index += 1;
        }
        self.same[hpos] = amount as u16;

        self.val2 = ((self.same[hpos].wrapping_sub(ZOPFLI_MIN_MATCH as u16) & 255) ^ self.val as u16) as i32;
        self.hashval2[hpos] = self.val2;

        let index2 = self.val2 as usize;
        if self.head2[index2] != -1 && self.hashval2[self.head2[index2] as usize] == self.val2 {
            self.prev2[hpos] = self.head2[index2] as u16;
        } else {
            self.prev2[hpos] = hpos as u16;
        }
        self.head2[index2] = hpos as i32;
    }

    pub fn head_at(&self, index: usize, which: Which) -> i32 {
        match which {
            Which::Hash1 => self.head[index],
            Which::Hash2 => self.head2[index],
        }
    }

    pub fn prev_at(&self, index: usize, which: Which) -> u16 {
        match which {
            Which::Hash1 => self.prev[index],
            Which::Hash2 => self.prev2[index],
        }
    }

    pub fn hash_val_at(&self, index: usize, which: Which) -> i32 {
        match which {
            Which::Hash1 => self.hashval[index],
            Which::Hash2 => self.hashval2[index],
        }
    }

    pub fn val(&self, which: Which) -> i32 {
        match which {
            Which::Hash1 => self.val,
            Which::Hash2 => self.val2,
        }
    }
}
<commit_msg>Extract repeated same array access<commit_after>use util::{ZOPFLI_WINDOW_MASK, ZOPFLI_MIN_MATCH};

const HASH_SHIFT: i32 = 5;
const HASH_MASK: i32 = 32767;

#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum Which {
    Hash1,
    Hash2,
}

pub struct ZopfliHash {
    head: Vec<i32>,  /* Hash value to index of its most recent occurrence. */
    prev: Vec<u16>,  /* Index to index of prev. occurrence of same hash. */
    hashval: Vec<i32>,  /* Index to hash value at this index. */
    val: i32,  /* Current hash value. */

    /* Fields with similar purpose as the above hash, but for the second hash with
    a value that is calculated differently.  */
    head2: Vec<i32>,  /* Hash value to index of its most recent occurrence. */
    prev2: Vec<u16>,  /* Index to index of prev. occurrence of same hash. */
    hashval2: Vec<i32>,  /* Index to hash value at this index. */
    val2: i32,  /* Current hash value. */

    pub same: Vec<u16>,  /* Amount of repetitions of same byte after this .*/
}

impl ZopfliHash {
    pub fn new(window_size: usize) -> ZopfliHash {
        ZopfliHash {
           head: vec![-1; 65536],
           prev: (0..window_size as u16).collect(),
           hashval: vec![-1; window_size],
           val: 0,

           /* Fields with similar purpose as the above hash, but for the second hash with
           a value that is calculated differently.  */
           head2: vec![-1; 65536],
           prev2: (0..window_size as u16).collect(),
           hashval2: vec![-1; window_size],
           val2: 0,

           same: vec![0; window_size],
       }
    }

    pub fn reset(&mut self, window_size: usize) {
        self.val = 0;
        self.head = vec![-1; 65536];
        self.prev = (0..window_size as u16).collect();
        self.hashval = vec![-1; window_size];

        self.same = vec![0; window_size];
        self.val2 = 0;
        self.head2 = vec![-1; 65536];
        self.prev2 = (0..window_size as u16).collect();
        self.hashval2 = vec![-1; window_size];
    }

    pub fn warmup(&mut self, arr: &[u8], pos: usize, end: usize) {
        let c = arr[pos];
        self.update_val(c);

        if pos + 1 < end {
            let c = arr[pos + 1];
            self.update_val(c);
        }
    }

    /// Update the sliding hash value with the given byte. All calls to this function
    /// must be made on consecutive input characters. Since the hash value exists out
    /// of multiple input bytes, a few warmups with this function are needed initially.
    fn update_val(&mut self, c: u8) {
        self.val = ((self.val << HASH_SHIFT) ^ c as i32) & HASH_MASK;
    }

    pub fn update(&mut self, array: &[u8], pos: usize) {
        let hash_value = array.get(pos + ZOPFLI_MIN_MATCH - 1).cloned().unwrap_or(0);
        self.update_val(hash_value);

        let hpos = pos & ZOPFLI_WINDOW_MASK;
        self.hashval[hpos] = self.val;

        let index = self.val as usize;
        if self.head[index] != -1 && self.hashval[self.head[index] as usize] == self.val {
            self.prev[hpos] = self.head[index] as u16;
        } else {
            self.prev[hpos] = hpos as u16;
        }
        self.head[index] = hpos as i32;

        // Update "same".
        let mut amount = 0;
        let same_index = pos.wrapping_sub(1) & ZOPFLI_WINDOW_MASK;
        let same = self.same[same_index];
        if same > 1 {
            amount = same as i32 - 1;
        }

        let mut another_index = pos + amount as usize + 1;
        while another_index < array.len() && array[pos] == array[another_index] && amount < -1 {
            amount += 1;
            another_index += 1;
        }
        self.same[hpos] = amount as u16;

        self.val2 = ((self.same[hpos].wrapping_sub(ZOPFLI_MIN_MATCH as u16) & 255) ^ self.val as u16) as i32;
        self.hashval2[hpos] = self.val2;

        let index2 = self.val2 as usize;
        if self.head2[index2] != -1 && self.hashval2[self.head2[index2] as usize] == self.val2 {
            self.prev2[hpos] = self.head2[index2] as u16;
        } else {
            self.prev2[hpos] = hpos as u16;
        }
        self.head2[index2] = hpos as i32;
    }

    pub fn head_at(&self, index: usize, which: Which) -> i32 {
        match which {
            Which::Hash1 => self.head[index],
            Which::Hash2 => self.head2[index],
        }
    }

    pub fn prev_at(&self, index: usize, which: Which) -> u16 {
        match which {
            Which::Hash1 => self.prev[index],
            Which::Hash2 => self.prev2[index],
        }
    }

    pub fn hash_val_at(&self, index: usize, which: Which) -> i32 {
        match which {
            Which::Hash1 => self.hashval[index],
            Which::Hash2 => self.hashval2[index],
        }
    }

    pub fn val(&self, which: Which) -> i32 {
        match which {
            Which::Hash1 => self.val,
            Which::Hash2 => self.val2,
        }
    }
}
<|endoftext|>


<commit_before>use time;
use std::io;
use std::io::BufReader;
use std::io::prelude::*;
use std::os::unix::prelude::*;
use std::process::{Command, Stdio};
use std::sync::mpsc::{channel, Receiver};
use std::thread;
use super::Event;
use super::rep::Payload;

/// Represents a description of a github hook
/// A hook should describe a cmd to run and for which
/// events. It should be assumed that the github event name
/// and event payload will be available in env vars
/// `GH_EVENT_NAME` and `GH_EVENT_PAYLOAD` respectively
#[derive(Clone, Default, Debug, RustcDecodable, RustcEncodable)]
pub struct Hook {
  /// Command to run when an event is recieved
  pub cmd: String,
  /// List of github events that should trigger this hook
  pub events: Vec<String>,
  /// Optional name of hook. Defaults to cmd
  pub name: Option<String>,
  /// Github organization filter
  pub organization: Option<String>,
  /// Github repository filter
  pub repository: Option<String>,
  /// Github event sender filter
  pub sender: Option<String>
}

impl Hook {

  /// return a debug hook that targets all events
  /// and does nothing but echo the event name and payload
  pub fn echo() -> Hook {
     Hook {
       cmd: "echo \"$GH_EVENT_NAME\": \"$GH_EVENT_PAYLOAD\"".to_owned(),
       events: vec!["*".to_owned()],
       ..Default::default()
     }
  }

  fn info(&self, msg: String) {
    write!(&mut io::stderr(),
      "{} {}: {}",
       time::now().to_utc().rfc3339(), self.name(), msg);
  }

  fn err(&self, msg: String) {
    print!("{} {}: {}", time::now().to_utc().rfc3339(), self.name(), msg)
  }

  /// return true if this hook targets the provided
  /// event name, false otherwise
  pub fn targets(&self, event: &String, payload: &Payload) -> bool {
    // todo: filter on payload.{ repo, org, sender }
    (self.events.contains(event)
     || self.events.contains(&"*".to_owned()))
  }

  /// return name if defined, otherwise cmd
  pub fn name(&self) -> String {
    self.name.clone().unwrap_or(self.cmd.clone())
  }

  /// start recv'ing events
  pub fn recv(&self, rx: Receiver<Event>) {
    loop {
      match rx.recv() {
        Ok(event) => {
          self.run(&event);
          ()
        },
        Err(e) => {
          self.err(format!("Recv err: {}\n", e.to_string()));
          break
        }
      }
    }
  }

  /// run hook cmd, returning true if cmd succeeded, otherwise false
  pub fn run(&self, event: &Event) -> bool {

     fn collect<T: Read + Send + 'static>(stm: Option<T>) ->
       Receiver<io::Result<Vec<String>>> {
       let (tx, rx) = channel();
       if let Some(s) = stm {
         thread::spawn(move || {
           let mut buf = BufReader::with_capacity(64, s);
           let mut lines: Vec<String> = Vec::new();
           loop {
             let mut line = String::new();
             match buf.read_line(&mut line) {
                Ok(0) | Err(_)  => break,
                Ok(_)  => lines.push(line)
             }
           }
           tx.send(Ok(lines)).unwrap()
         });
       } else {
         tx.send(Ok(vec![])).unwrap();
       }
       rx
     }

     match Command::new("/bin/sh")
       .arg("-c")
       .arg(&self.cmd)
       .env("GH_EVENT_NAME", &event.name)
       .env("GH_EVENT_PAYLOAD", &event.payload)
       .stdin(Stdio::null())
       .stdout(Stdio::piped())
       .stderr(Stdio::piped())
       .spawn() {
         Err(e) => {
           self.err(format!("error executing {}: {}\n", self.cmd, e));
           false
         },
         Ok(mut child)  => {

           let stdout = collect(child.stdout.take());
           let stderr = collect(child.stderr.take());
           let status = child.wait();

           match stdout.recv() {
             Ok(Ok(lines)) => for l in lines {
               self.info(l);
             },
             Ok(Err(e))    => self.err(format!("stdout io err {}\n", e)),
             Err(e)        => self.err(format!("stdout recv err {}\n", e))
           };

           match stderr.recv() {
             Ok(Ok(lines)) => for l in lines {
               self.info(l)
             },
             Ok(Err(e))    => self.err(format!("stderr io err {}\n", e)),
             Err(e)        => self.err(format!("stderr recv err {}\n", e))
           };

           match status {
             Ok(s) => {
               if s.success() {
                 self.info("that worked\n".to_owned());
                 true
               } else {
                 match s.code() {
                   Some(c) => self.info(format!("hook exited with status {}\n", c)),
                   _ => if let Some(s) = s.signal() {
                     self.err(format!("process killed by signal {}\n", s))
                   }
                 };
                 false
               }
             },
             Err(e) => {
               self.err(format!("error getting exit status {}\n", e.to_string()));
               false
             }
           }
         }
       }
  }
}

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

  #[test]
  fn test_hook_run_pass() {
    let hook = Hook {
      cmd: "true".to_owned(),
      ..Default::default()
    };
    assert!(hook.run(&Event {
      ..Default::default()
    }))
  }

  #[test]
  fn test_hook_run_fail() {
    let hook = Hook {
      cmd: "false".to_owned(),
      ..Default::default()
    };
    assert!(!hook.run(&Event {
      ..Default::default()
    }))
  }

  #[test]
  fn test_hook_run_panic() {
    let hook = Hook {
      cmd: "oiusdfasdf".to_owned(),
      ..Default::default()
    };
    assert!(!hook.run(&Event {
      ..Default::default()
    }))
  }
}
<commit_msg>simply type constraints<commit_after>use time;
use std::io;
use std::io::BufReader;
use std::io::prelude::*;
use std::os::unix::prelude::*;
use std::process::{Command, Stdio};
use std::sync::mpsc::{channel, Receiver};
use std::thread;
use super::Event;
use super::rep::Payload;

/// Represents a description of a github hook
/// A hook should describe a cmd to run and for which
/// events. It should be assumed that the github event name
/// and event payload will be available in env vars
/// `GH_EVENT_NAME` and `GH_EVENT_PAYLOAD` respectively
#[derive(Clone, Default, Debug, RustcDecodable, RustcEncodable)]
pub struct Hook {
  /// Command to run when an event is recieved
  pub cmd: String,
  /// List of github events that should trigger this hook
  pub events: Vec<String>,
  /// Optional name of hook. Defaults to cmd
  pub name: Option<String>,
  /// Github organization filter
  pub organization: Option<String>,
  /// Github repository filter
  pub repository: Option<String>,
  /// Github event sender filter
  pub sender: Option<String>
}

impl Hook {

  /// return a debug hook that targets all events
  /// and does nothing but echo the event name and payload
  pub fn echo() -> Hook {
     Hook {
       cmd: "echo \"$GH_EVENT_NAME\": \"$GH_EVENT_PAYLOAD\"".to_owned(),
       events: vec!["*".to_owned()],
       ..Default::default()
     }
  }

  fn info<S: Into<String>>(&self, msg: S) {
    write!(&mut io::stderr(),
      "{} {}: {}",
       time::now().to_utc().rfc3339(), self.name(), msg.into());
  }

  fn err<S: Into<S>>(&self, msg: S) {
    print!("{} {}: {}", time::now().to_utc().rfc3339(), self.name(), msg.into())
  }

  /// return true if this hook targets the provided
  /// event name, false otherwise
  pub fn targets(&self, event: &String, payload: &Payload) -> bool {
    // todo: filter on payload.{ repo, org, sender }
    (self.events.contains(event)
     || self.events.contains(&"*".to_owned()))
  }

  /// return name if defined, otherwise cmd
  pub fn name(&self) -> String {
    self.name.clone().unwrap_or(self.cmd.clone())
  }

  /// start recv'ing events
  pub fn recv(&self, rx: Receiver<Event>) {
    loop {
      match rx.recv() {
        Ok(event) => {
          self.run(&event);
          ()
        },
        Err(e) => {
          self.err(format!("Recv err: {}\n", e.to_string()));
          break
        }
      }
    }
  }

  /// run hook cmd, returning true if cmd succeeded, otherwise false
  pub fn run(&self, event: &Event) -> bool {

     fn collect<T: Read + Send + 'static>(stm: Option<T>) ->
       Receiver<io::Result<Vec<String>>> {
       let (tx, rx) = channel();
       if let Some(s) = stm {
         thread::spawn(move || {
           let mut buf = BufReader::with_capacity(64, s);
           let mut lines: Vec<String> = Vec::new();
           loop {
             let mut line = String::new();
             match buf.read_line(&mut line) {
                Ok(0) | Err(_)  => break,
                Ok(_)  => lines.push(line)
             }
           }
           tx.send(Ok(lines)).unwrap()
         });
       } else {
         tx.send(Ok(vec![])).unwrap();
       }
       rx
     }

     match Command::new("/bin/sh")
       .arg("-c")
       .arg(&self.cmd)
       .env("GH_EVENT_NAME", &event.name)
       .env("GH_EVENT_PAYLOAD", &event.payload)
       .stdin(Stdio::null())
       .stdout(Stdio::piped())
       .stderr(Stdio::piped())
       .spawn() {
         Err(e) => {
           self.err(format!("error executing {}: {}\n", self.cmd, e));
           false
         },
         Ok(mut child)  => {

           let stdout = collect(child.stdout.take());
           let stderr = collect(child.stderr.take());
           let status = child.wait();

           match stdout.recv() {
             Ok(Ok(lines)) => for l in lines {
               self.info(l);
             },
             Ok(Err(e))    => self.err(format!("stdout io err {}\n", e)),
             Err(e)        => self.err(format!("stdout recv err {}\n", e))
           };

           match stderr.recv() {
             Ok(Ok(lines)) => for l in lines {
               self.info(l)
             },
             Ok(Err(e))    => self.err(format!("stderr io err {}\n", e)),
             Err(e)        => self.err(format!("stderr recv err {}\n", e))
           };

           match status {
             Ok(s) => {
               if s.success() {
                 self.info("that worked\n".to_owned());
                 true
               } else {
                 match s.code() {
                   Some(c) => self.info(format!("hook exited with status {}\n", c)),
                   _ => if let Some(s) = s.signal() {
                     self.err(format!("process killed by signal {}\n", s))
                   }
                 };
                 false
               }
             },
             Err(e) => {
               self.err(format!("error getting exit status {}\n", e.to_string()));
               false
             }
           }
         }
       }
  }
}

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

  #[test]
  fn test_hook_run_pass() {
    let hook = Hook {
      cmd: "true".to_owned(),
      ..Default::default()
    };
    assert!(hook.run(&Event {
      ..Default::default()
    }))
  }

  #[test]
  fn test_hook_run_fail() {
    let hook = Hook {
      cmd: "false".to_owned(),
      ..Default::default()
    };
    assert!(!hook.run(&Event {
      ..Default::default()
    }))
  }

  #[test]
  fn test_hook_run_panic() {
    let hook = Hook {
      cmd: "oiusdfasdf".to_owned(),
      ..Default::default()
    };
    assert!(!hook.run(&Event {
      ..Default::default()
    }))
  }
}
<|endoftext|>


<commit_before>//! Helpers for writing Alfred script filter JSON output (Alfred 3)
//!
//! # Examples
//!
//! ### Writing items
//!
//! ```
//! # extern crate alfred;
//! # use std::io::{self, Write};
//! #
//! # fn write_items() -> io::Result<()> {
//! alfred::json::write_items(io::stdout(), &[
//!     alfred::Item::new("Item 1"),
//!     alfred::ItemBuilder::new("Item 2")
//!                         .subtitle("Subtitle")
//!                         .into_item(),
//!     alfred::ItemBuilder::new("Item 3")
//!                         .arg("Argument")
//!                         .subtitle("Subtitle")
//!                         .icon_filetype("public.folder")
//!                         .into_item()
//! ])
//! # }
//! #
//! # fn main() {
//! #     match write_items() {
//! #         Ok(()) => {},
//! #         Err(err) => {
//! #             let _ = writeln!(&mut io::stderr(), "Error writing items: {}", err);
//! #         }
//! #     }
//! # }
//! ```
//!
//! ### Writing items with variables
//!
//! ```
//! # extern crate alfred;
//! # use std::io::{self, Write};
//! #
//! # fn write_items() -> io::Result<()> {
//! alfred::json::Builder::with_items(&[
//!     alfred::Item::new("Item 1"),
//!     alfred::ItemBuilder::new("Item 2")
//!                         .subtitle("Subtitle")
//!                         .into_item(),
//!     alfred::ItemBuilder::new("Item 3")
//!                         .arg("Argument")
//!                         .subtitle("Subtitle")
//!                         .icon_filetype("public.folder")
//!                         .into_item()
//! ]).variable("fruit", "banana")
//!   .variable("vegetable", "carrot")
//!   .write(io::stdout())
//! # }
//! #
//! # fn main() {
//! #     match write_items() {
//! #         Ok(()) => {},
//! #         Err(err) => {
//! #             let _ = writeln!(&mut io::stderr(), "Error writing items: {}", err);
//! #         }
//! #     }
//! # }
//! ```

use ::{Item, ItemType, Modifier, Icon};
use serde_json as json;
use serde_json::value::Value;
use std::collections::HashMap;
use std::io;
use std::io::prelude::*;

/// Writes a complete JSON document representing the `Item`s to the `Write`.
///
/// The `Write` is flushed after the JSON document is written.
pub fn write_items<W: Write>(w: W, items: &[Item]) -> io::Result<()> {
    Builder::with_items(items).write(w)
}

/// A helper type for writing out items with top-level variables.
///
/// Note: If you don't need top-level variables the `write_items()` function is easier to use.
pub struct Builder<'a> {
    /// The items that will be written out.
    pub items: &'a [Item<'a>],
    /// The variables that will be written out.
    pub variables: HashMap<&'a str, &'a str>
}

impl<'a> Builder<'a> {
    /// Returns a new `Builder` with no items.
    pub fn new() -> Builder<'a> {
        Builder { items: &[], variables: HashMap::new() }
    }

    /// Returns a new `Builder` with the given items.
    pub fn with_items(items: &'a [Item]) -> Builder<'a> {
        Builder { items, variables: HashMap::new() }
    }

    /// Writes a complete JSON document representing the items and variables to the `Write`.
    ///
    /// The `Write` is flushed after the JSON document is written.
    pub fn write<W: Write>(self, mut w: W) -> io::Result<()> {
        write!(&mut w, "{}", self.into_json())?;
        w.flush()
    }

    fn into_json(self) -> Value {
        let mut root = json::Map::new();
        // We know for a fact that our implementation of ToJson cannot return an error.
        root.insert("items".to_string(), Value::Array(self.items.into_iter()
                                                                .map(|x| x.into_json())
                                                                .collect()));
        let mut iter = self.variables.into_iter();
        if let Some(first) = iter.next() {
            let mut vars = json::Map::new();
            vars.insert(first.0.into(), Value::String(first.1.into()));
            for elt in iter {
                vars.insert(elt.0.into(), Value::String(elt.1.into()));
            }
            root.insert("variables".to_owned(), Value::Object(vars));
        }
        Value::Object(root)
    }

    /// Replaces the builder's items with `items`.
    pub fn items(mut self, items: &'a [Item]) -> Builder<'a> {
        self.set_items(items);
        self
    }

    /// Replaces the builder's variables with `variables`.
    pub fn variables(mut self, variables: HashMap<&'a str, &'a str>) -> Builder<'a> {
        self.set_variables(variables);
        self
    }

    /// Inserts a new variable into the builder's variables.
    pub fn variable(mut self, key: &'a str, value: &'a str) -> Builder<'a> {
        self.set_variable(key, value);
        self
    }

    /// Replaces the builder's items with `items`.
    pub fn set_items(&mut self, items: &'a [Item]) {
        self.items = items
    }

    /// Replaces the builder's variables with `variables`.
    pub fn set_variables(&mut self, variables: HashMap<&'a str, &'a str>) {
        self.variables = variables
    }

    /// Inserts a new variable into the builder's variables.
    pub fn set_variable(&mut self, key: &'a str, value: &'a str) {
        self.variables.insert(key, value);
    }
}

impl<'a> Item<'a> {
    fn into_json(&self) -> Value {
        let mut d = json::Map::new();
        d.insert("title".to_string(), json!(self.title));
        if let Some(ref subtitle) = self.subtitle {
            d.insert("subtitle".to_string(), json!(subtitle));
        }
        if let Some(ref icon) = self.icon {
            d.insert("icon".to_string(), icon.into_json());
        }
        if let Some(ref uid) = self.uid {
            d.insert("uid".to_string(), json!(uid));
        }
        if let Some(ref arg) = self.arg {
            d.insert("arg".to_string(), json!(arg));
        }
        match self.type_ {
            ItemType::Default => {}
            ItemType::File => {
                d.insert("type".to_string(), json!("file"));
            }
            ItemType::FileSkipCheck => {
                d.insert("type".to_string(), json!("file:skipcheck"));
            }
        }
        if !self.valid {
            d.insert("valid".to_string(), Value::Bool(false));
        }
        if let Some(ref autocomplete) = self.autocomplete {
            d.insert("autocomplete".to_string(), json!(autocomplete));
        }
        if self.text_copy.is_some() || self.text_large_type.is_some() {
            let mut text = json::Map::new();
            if let Some(ref text_copy) = self.text_copy {
                text.insert("copy".to_string(), json!(text_copy));
            }
            if let Some(ref text_large_type) = self.text_large_type {
                text.insert("largetype".to_string(), json!(text_large_type));
            }
            d.insert("text".to_string(), Value::Object(text));
        }
        if let Some(ref url) = self.quicklook_url {
            d.insert("quicklookurl".to_string(), json!(url));
        }
        if !self.modifiers.is_empty() {
            let mut mods = json::Map::with_capacity(self.modifiers.len());
            for (modifier, data) in &self.modifiers {
                let key = match *modifier {
                    Modifier::Command => "cmd",
                    Modifier::Option => "alt",
                    Modifier::Control => "ctrl",
                    Modifier::Shift => "shift",
                    Modifier::Fn => "fn"
                }.to_string();
                let mut mod_ = json::Map::new();
                if let Some(ref subtitle) = data.subtitle {
                    mod_.insert("subtitle".to_string(), json!(subtitle));
                }
                if let Some(ref arg) = data.arg {
                    mod_.insert("arg".to_string(), json!(arg));
                }
                if let Some(valid) = data.valid {
                    mod_.insert("valid".to_string(), json!(valid));
                }
                if let Some(ref icon) = data.icon {
                    mod_.insert("icon".to_string(), icon.into_json());
                }
                mods.insert(key, Value::Object(mod_));
            }
            d.insert("mods".to_string(), Value::Object(mods));
        }
        if !self.variables.is_empty() {
            let mut vars = json::Map::with_capacity(self.variables.len());
            for (key, value) in &self.variables {
                vars.insert(key.clone().into_owned(), json!(value.clone().into_owned()));
            }
            d.insert("variables".to_owned(), Value::Object(vars));
        }
        Value::Object(d)
    }
}

impl<'a> Icon<'a> {
    fn into_json(&self) -> Value {
        match *self {
            Icon::Path(ref s) => json!({"path": s}),
            Icon::File(ref s) => json!({"type": "fileicon", "path": s}),
            Icon::FileType(ref s) => json!({"type": "filetype", "path": s})
        }
    }
}

#[test]
fn test_into_json() {
    let item = Item::new("Item 1");
    assert_eq!(item.into_json(), json!({"title": "Item 1"}));
    let item = ::ItemBuilder::new("Item 2")
                              .subtitle("Subtitle")
                              .into_item();
    assert_eq!(item.into_json(),
              json!({
                  "title": "Item 2",
                  "subtitle": "Subtitle"
              }));
    let item = ::ItemBuilder::new("Item 3")
                              .arg("Argument")
                              .subtitle("Subtitle")
                              .icon_filetype("public.folder")
                              .into_item();
    assert_eq!(item.into_json(),
               json!({
                   "title": "Item 3",
                   "subtitle": "Subtitle",
                   "arg": "Argument",
                   "icon": { "type": "filetype", "path": "public.folder" }
               }));
    let item = ::ItemBuilder::new("Item 4")
                              .arg("Argument")
                              .subtitle("Subtitle")
                              .arg_mod(Modifier::Option, "Alt Argument")
                              .valid_mod(Modifier::Option, false)
                              .icon_file_mod(Modifier::Option, "opt.png")
                              .arg_mod(Modifier::Control, "Ctrl Argument")
                              .subtitle_mod(Modifier::Control, "Ctrl Subtitle")
                              .icon_path_mod(Modifier::Control, "ctrl.png")
                              .arg_mod(Modifier::Shift, "Shift Argument")
                              .into_item();
    assert_eq!(item.into_json(),
               json!({
                   "title": "Item 4",
                   "subtitle": "Subtitle",
                   "arg": "Argument",
                   "mods": {
                       "alt": {
                            "arg": "Alt Argument",
                            "valid": false,
                            "icon": { "type": "fileicon", "path": "opt.png" }
                       },
                       "ctrl": {
                           "arg": "Ctrl Argument",
                           "subtitle": "Ctrl Subtitle",
                           "icon": { "path": "ctrl.png" }
                       },
                       "shift": {
                           "arg": "Shift Argument"
                       }
                   }
               }));
    let item = ::ItemBuilder::new("Item 5")
                             .arg("Argument")
                             .variable("fruit", "banana")
                             .variable("vegetable", "carrot")
                             .into_item();
    assert_eq!(item.into_json(),
               json!({
                   "title": "Item 5",
                   "arg": "Argument",
                   "variables": {
                       "fruit": "banana",
                       "vegetable": "carrot"
                   }
               }));
}

#[test]
fn test_builder() {
    let json = Builder::with_items(&[
        Item::new("Item 1"),
        ::ItemBuilder::new("Item 2")
                      .subtitle("Subtitle")
                      .into_item(),
        ::ItemBuilder::new("Item 3")
                      .arg("Argument")
                      .subtitle("Subtitle")
                      .icon_filetype("public.folder")
                      .into_item()
    ]).variable("fruit", "banana")
      .variable("vegetable", "carrot")
      .into_json();
    assert_eq!(json,
               json!({
                   "items": [
                       {
                           "title": "Item 1"
                       },
                       {
                           "title": "Item 2",
                           "subtitle": "Subtitle"
                       },
                       {
                           "title": "Item 3",
                           "arg": "Argument",
                           "subtitle": "Subtitle",
                           "icon": { "type": "filetype", "path": "public.folder" }
                       }
                   ],
                   "variables": {
                       "fruit": "banana",
                       "vegetable": "carrot"
                   }
               }));
}
<commit_msg>Apply Clippy's suggestions<commit_after>//! Helpers for writing Alfred script filter JSON output (Alfred 3)
//!
//! # Examples
//!
//! ### Writing items
//!
//! ```
//! # extern crate alfred;
//! # use std::io::{self, Write};
//! #
//! # fn write_items() -> io::Result<()> {
//! alfred::json::write_items(io::stdout(), &[
//!     alfred::Item::new("Item 1"),
//!     alfred::ItemBuilder::new("Item 2")
//!                         .subtitle("Subtitle")
//!                         .into_item(),
//!     alfred::ItemBuilder::new("Item 3")
//!                         .arg("Argument")
//!                         .subtitle("Subtitle")
//!                         .icon_filetype("public.folder")
//!                         .into_item()
//! ])
//! # }
//! #
//! # fn main() {
//! #     match write_items() {
//! #         Ok(()) => {},
//! #         Err(err) => {
//! #             let _ = writeln!(&mut io::stderr(), "Error writing items: {}", err);
//! #         }
//! #     }
//! # }
//! ```
//!
//! ### Writing items with variables
//!
//! ```
//! # extern crate alfred;
//! # use std::io::{self, Write};
//! #
//! # fn write_items() -> io::Result<()> {
//! alfred::json::Builder::with_items(&[
//!     alfred::Item::new("Item 1"),
//!     alfred::ItemBuilder::new("Item 2")
//!                         .subtitle("Subtitle")
//!                         .into_item(),
//!     alfred::ItemBuilder::new("Item 3")
//!                         .arg("Argument")
//!                         .subtitle("Subtitle")
//!                         .icon_filetype("public.folder")
//!                         .into_item()
//! ]).variable("fruit", "banana")
//!   .variable("vegetable", "carrot")
//!   .write(io::stdout())
//! # }
//! #
//! # fn main() {
//! #     match write_items() {
//! #         Ok(()) => {},
//! #         Err(err) => {
//! #             let _ = writeln!(&mut io::stderr(), "Error writing items: {}", err);
//! #         }
//! #     }
//! # }
//! ```

use ::{Item, ItemType, Modifier, Icon};
use serde_json as json;
use serde_json::value::Value;
use std::collections::HashMap;
use std::io;
use std::io::prelude::*;

/// Writes a complete JSON document representing the `Item`s to the `Write`.
///
/// The `Write` is flushed after the JSON document is written.
pub fn write_items<W: Write>(w: W, items: &[Item]) -> io::Result<()> {
    Builder::with_items(items).write(w)
}

/// A helper type for writing out items with top-level variables.
///
/// Note: If you don't need top-level variables the `write_items()` function is easier to use.
#[derive(Clone,Debug,Default)]
pub struct Builder<'a> {
    /// The items that will be written out.
    pub items: &'a [Item<'a>],
    /// The variables that will be written out.
    pub variables: HashMap<&'a str, &'a str>
}

impl<'a> Builder<'a> {
    /// Returns a new `Builder` with no items.
    pub fn new() -> Builder<'a> {
        Builder { items: &[], variables: HashMap::new() }
    }

    /// Returns a new `Builder` with the given items.
    pub fn with_items(items: &'a [Item]) -> Builder<'a> {
        Builder { items, variables: HashMap::new() }
    }

    /// Writes a complete JSON document representing the items and variables to the `Write`.
    ///
    /// The `Write` is flushed after the JSON document is written.
    pub fn write<W: Write>(self, mut w: W) -> io::Result<()> {
        write!(&mut w, "{}", self.into_json())?;
        w.flush()
    }

    fn into_json(self) -> Value {
        let mut root = json::Map::new();
        // We know for a fact that our implementation of ToJson cannot return an error.
        root.insert("items".to_string(), Value::Array(self.items.into_iter()
                                                                .map(|x| x.to_json())
                                                                .collect()));
        let mut iter = self.variables.into_iter();
        if let Some(first) = iter.next() {
            let mut vars = json::Map::new();
            vars.insert(first.0.into(), Value::String(first.1.into()));
            for elt in iter {
                vars.insert(elt.0.into(), Value::String(elt.1.into()));
            }
            root.insert("variables".to_owned(), Value::Object(vars));
        }
        Value::Object(root)
    }

    /// Replaces the builder's items with `items`.
    pub fn items(mut self, items: &'a [Item]) -> Builder<'a> {
        self.set_items(items);
        self
    }

    /// Replaces the builder's variables with `variables`.
    pub fn variables(mut self, variables: HashMap<&'a str, &'a str>) -> Builder<'a> {
        self.set_variables(variables);
        self
    }

    /// Inserts a new variable into the builder's variables.
    pub fn variable(mut self, key: &'a str, value: &'a str) -> Builder<'a> {
        self.set_variable(key, value);
        self
    }

    /// Replaces the builder's items with `items`.
    pub fn set_items(&mut self, items: &'a [Item]) {
        self.items = items
    }

    /// Replaces the builder's variables with `variables`.
    pub fn set_variables(&mut self, variables: HashMap<&'a str, &'a str>) {
        self.variables = variables
    }

    /// Inserts a new variable into the builder's variables.
    pub fn set_variable(&mut self, key: &'a str, value: &'a str) {
        self.variables.insert(key, value);
    }
}

impl<'a> Item<'a> {
    fn to_json(&self) -> Value {
        let mut d = json::Map::new();
        d.insert("title".to_string(), json!(self.title));
        if let Some(ref subtitle) = self.subtitle {
            d.insert("subtitle".to_string(), json!(subtitle));
        }
        if let Some(ref icon) = self.icon {
            d.insert("icon".to_string(), icon.to_json());
        }
        if let Some(ref uid) = self.uid {
            d.insert("uid".to_string(), json!(uid));
        }
        if let Some(ref arg) = self.arg {
            d.insert("arg".to_string(), json!(arg));
        }
        match self.type_ {
            ItemType::Default => {}
            ItemType::File => {
                d.insert("type".to_string(), json!("file"));
            }
            ItemType::FileSkipCheck => {
                d.insert("type".to_string(), json!("file:skipcheck"));
            }
        }
        if !self.valid {
            d.insert("valid".to_string(), Value::Bool(false));
        }
        if let Some(ref autocomplete) = self.autocomplete {
            d.insert("autocomplete".to_string(), json!(autocomplete));
        }
        if self.text_copy.is_some() || self.text_large_type.is_some() {
            let mut text = json::Map::new();
            if let Some(ref text_copy) = self.text_copy {
                text.insert("copy".to_string(), json!(text_copy));
            }
            if let Some(ref text_large_type) = self.text_large_type {
                text.insert("largetype".to_string(), json!(text_large_type));
            }
            d.insert("text".to_string(), Value::Object(text));
        }
        if let Some(ref url) = self.quicklook_url {
            d.insert("quicklookurl".to_string(), json!(url));
        }
        if !self.modifiers.is_empty() {
            let mut mods = json::Map::with_capacity(self.modifiers.len());
            for (modifier, data) in &self.modifiers {
                let key = match *modifier {
                    Modifier::Command => "cmd",
                    Modifier::Option => "alt",
                    Modifier::Control => "ctrl",
                    Modifier::Shift => "shift",
                    Modifier::Fn => "fn"
                }.to_string();
                let mut mod_ = json::Map::new();
                if let Some(ref subtitle) = data.subtitle {
                    mod_.insert("subtitle".to_string(), json!(subtitle));
                }
                if let Some(ref arg) = data.arg {
                    mod_.insert("arg".to_string(), json!(arg));
                }
                if let Some(valid) = data.valid {
                    mod_.insert("valid".to_string(), json!(valid));
                }
                if let Some(ref icon) = data.icon {
                    mod_.insert("icon".to_string(), icon.to_json());
                }
                mods.insert(key, Value::Object(mod_));
            }
            d.insert("mods".to_string(), Value::Object(mods));
        }
        if !self.variables.is_empty() {
            let mut vars = json::Map::with_capacity(self.variables.len());
            for (key, value) in &self.variables {
                vars.insert(key.clone().into_owned(), json!(value.clone().into_owned()));
            }
            d.insert("variables".to_owned(), Value::Object(vars));
        }
        Value::Object(d)
    }
}

impl<'a> Icon<'a> {
    fn to_json(&self) -> Value {
        match *self {
            Icon::Path(ref s) => json!({"path": s}),
            Icon::File(ref s) => json!({"type": "fileicon", "path": s}),
            Icon::FileType(ref s) => json!({"type": "filetype", "path": s})
        }
    }
}

#[test]
fn test_to_json() {
    let item = Item::new("Item 1");
    assert_eq!(item.to_json(), json!({"title": "Item 1"}));
    let item = ::ItemBuilder::new("Item 2")
                              .subtitle("Subtitle")
                              .into_item();
    assert_eq!(item.to_json(),
              json!({
                  "title": "Item 2",
                  "subtitle": "Subtitle"
              }));
    let item = ::ItemBuilder::new("Item 3")
                              .arg("Argument")
                              .subtitle("Subtitle")
                              .icon_filetype("public.folder")
                              .into_item();
    assert_eq!(item.to_json(),
               json!({
                   "title": "Item 3",
                   "subtitle": "Subtitle",
                   "arg": "Argument",
                   "icon": { "type": "filetype", "path": "public.folder" }
               }));
    let item = ::ItemBuilder::new("Item 4")
                              .arg("Argument")
                              .subtitle("Subtitle")
                              .arg_mod(Modifier::Option, "Alt Argument")
                              .valid_mod(Modifier::Option, false)
                              .icon_file_mod(Modifier::Option, "opt.png")
                              .arg_mod(Modifier::Control, "Ctrl Argument")
                              .subtitle_mod(Modifier::Control, "Ctrl Subtitle")
                              .icon_path_mod(Modifier::Control, "ctrl.png")
                              .arg_mod(Modifier::Shift, "Shift Argument")
                              .into_item();
    assert_eq!(item.to_json(),
               json!({
                   "title": "Item 4",
                   "subtitle": "Subtitle",
                   "arg": "Argument",
                   "mods": {
                       "alt": {
                            "arg": "Alt Argument",
                            "valid": false,
                            "icon": { "type": "fileicon", "path": "opt.png" }
                       },
                       "ctrl": {
                           "arg": "Ctrl Argument",
                           "subtitle": "Ctrl Subtitle",
                           "icon": { "path": "ctrl.png" }
                       },
                       "shift": {
                           "arg": "Shift Argument"
                       }
                   }
               }));
    let item = ::ItemBuilder::new("Item 5")
                             .arg("Argument")
                             .variable("fruit", "banana")
                             .variable("vegetable", "carrot")
                             .into_item();
    assert_eq!(item.to_json(),
               json!({
                   "title": "Item 5",
                   "arg": "Argument",
                   "variables": {
                       "fruit": "banana",
                       "vegetable": "carrot"
                   }
               }));
}

#[test]
fn test_builder() {
    let json = Builder::with_items(&[
        Item::new("Item 1"),
        ::ItemBuilder::new("Item 2")
                      .subtitle("Subtitle")
                      .into_item(),
        ::ItemBuilder::new("Item 3")
                      .arg("Argument")
                      .subtitle("Subtitle")
                      .icon_filetype("public.folder")
                      .into_item()
    ]).variable("fruit", "banana")
      .variable("vegetable", "carrot")
      .into_json();
    assert_eq!(json,
               json!({
                   "items": [
                       {
                           "title": "Item 1"
                       },
                       {
                           "title": "Item 2",
                           "subtitle": "Subtitle"
                       },
                       {
                           "title": "Item 3",
                           "arg": "Argument",
                           "subtitle": "Subtitle",
                           "icon": { "type": "filetype", "path": "public.folder" }
                       }
                   ],
                   "variables": {
                       "fruit": "banana",
                       "vegetable": "carrot"
                   }
               }));
}
<|endoftext|>