<commit_before>// This file is part of sokoban-rs
// Copyright 2015 Sébastien Watteau
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use std::ops::Deref;
use std::path::Path;
use sdl2::rect::Rect;
use sdl2::pixels::{Color, PixelFormatEnum};
use sdl2::render::{Renderer, Texture};
use sdl2_image::LoadTexture;
use sdl2_ttf::Font;

use super::game::{Level, Position, Direction};

/// The Drawer struct is responsible for drawing the game onto the screen.
pub struct Drawer<'a> {
    /// The underlying SDL renderer
    renderer: Renderer<'a>,
    /// The active tileset
    tileset: Switch,
    /// The font used to display text
    font: Font,
    /// The size of the screen in pixels
    screen_size: (u32, u32),
    /// The height of the status bar
    bar_height: u32,
    /// The color of the status bar
    bar_color: Color,
    /// The color of the text in the status bar
    bar_text_color: Color,
}

/// Represents a location for text in the status bar
enum StatusBarLocation {
    FlushLeft,
    FlushRight,
}

impl<'a> Drawer<'a> {
    /// Creates a new Drawer instance.
    pub fn new(renderer: Renderer<'a>) -> Drawer {
        let font = Font::from_file(Path::new("assets/font/RujisHandwritingFontv.2.0.ttf"), 20).unwrap();
        let screen_size = renderer.window().unwrap().drawable_size();
        let tileset = Switch::new(&renderer);
        Drawer {
            renderer: renderer,
            tileset: tileset,
            font: font,
            screen_size: screen_size,
            bar_height: 32,
            bar_color: Color::RGBA(20, 20, 20, 255),
            bar_text_color: Color::RGBA(255, 192, 0, 255),
        }
    }

    /// Draws a level onto the screen.
    pub fn draw(&mut self, level: &Level) {
        // Draw a full-size image onto an off-screen buffer
        let fullsize = self.get_rendering_size(&level);
        let _ = self.renderer.render_target()
            .expect("Render targets are not supported")
            .create_and_set(PixelFormatEnum::RGBA8888, fullsize);

        self.draw_fullsize(level);

        // Copy onto the screen with appropriate scaling
        let final_rect = self.get_centered_image_rect(self.get_scaled_rendering_size(&level));
        let texture = self.renderer.render_target()
            .unwrap()
            .reset()
            .unwrap_or_else(|err| panic!("Could not reset to the default render target: {}", err))
            .unwrap_or_else(|| panic!("Could not get the offscreen texture"));

        self.renderer.clear();
        self.renderer.copy(&texture, Some(Rect::new_unwrap(0, 0, fullsize.0, fullsize.1)), final_rect);

        self.draw_status_bar(&level);

        self.renderer.present();
    }

    /// Draws a full-size image of the given level onto the current render target.
    fn draw_fullsize(&mut self, level: &Level) {
        let (cols, rows) = level.extents();
        self.renderer.set_draw_color(Color::RGB(0, 0, 0));
        self.renderer.clear();

        for j in (0..rows) {
            for i in (0..cols) {
                let pos = Position::new(j, i);
                let (x, y) = self.tileset.get_coordinates(&pos);

                // First draw the floor tiles
                if level.is_square(&pos) {
                    self.draw_tile(Tile::Square, x, y);
                } else {
                    self.draw_tile(Tile::Floor, x, y);
                }

                // Add the shadows
                let flags = get_shadow_flags(&level, &pos);
                for f in &[N_EDGE, S_EDGE, E_EDGE, W_EDGE, NE_CORNER, NW_CORNER, SE_CORNER, SW_CORNER] {
                    if flags.contains(*f) {
                        self.draw_tile(Tile::Shadow(*f), x, y);
                    }
                }

                // Draw the other items
                let z = y - self.tileset.tile_offset();
                if level.is_wall(&pos) {
                    self.draw_tile(Tile::Wall, x, z);
                }
                if level.is_box(&pos) {
                    self.draw_tile(Tile::Rock, x, z);
                }
                if level.is_player(&pos) {
                    self.draw_tile(Tile::Player, x, z);
                }
            }
        }
    }

    /// Draws the status bar
    fn draw_status_bar(&mut self, level: &Level) {
        let prev_color = self.renderer.draw_color();
        self.renderer.set_draw_color(self.bar_color);
        let rect = Rect::new_unwrap(0, (self.screen_size.1 - self.bar_height) as i32, self.screen_size.0, self.bar_height);
        self.renderer.fill_rect(rect);
        self.renderer.set_draw_color(prev_color);

        // Draw the number of moves
        let s = format!("# moves: {}", level.get_steps());
        self.draw_status_text(&s, StatusBarLocation::FlushLeft);

        // Draw the level's title
        self.draw_status_text(level.title(), StatusBarLocation::FlushRight);
    }

    /// Draws text in the status bar
    fn draw_status_text(&mut self, text: &str, location: StatusBarLocation) {
        let surface = self.font.render_str_blended(text, self.bar_text_color).unwrap();
        let texture = self.renderer.create_texture_from_surface(&surface).unwrap();
        let margin = 4;
        let (w, h) = {
            let q = texture.query();
            (q.width, q.height)
        };
        let (x, y) = match location {
            StatusBarLocation::FlushLeft => {
                (margin as i32, (self.screen_size.1 - margin - h) as i32)
            },
            StatusBarLocation::FlushRight => {
                ((self.screen_size.0 - margin - w) as i32, (self.screen_size.1 - margin - h) as i32)
            },
        };
        self.renderer.copy(&texture, None, Some(Rect::new_unwrap(x, y, w, h)));
    }

    /// Draws a tile at the given coordinates.
    fn draw_tile(&mut self, tile: Tile, x: i32, y: i32) {
        let (col, row) = self.tileset.location(tile).unwrap_or_else(|| {
            panic!("No image for this tile: {:?}", tile);
        });
        let tile_rect = self.get_tile_rect(col, row);
        self.renderer.copy(self.tileset.texture(), tile_rect, Some(Rect::new_unwrap(x, y, self.tileset.tile_width(), self.tileset.tile_height())));
    }

    /// Returns the Rect of the tile located at the given row and column in the texture.
    fn get_tile_rect(&self, col: u32, row: u32) -> Option<Rect> {
        let (w, h) = (self.tileset.tile_width(), self.tileset.tile_height());
        let x = (col * w) as i32;
        let y = (row * h) as i32;
        Some(Rect::new_unwrap(x, y, w, h))
    }

    /// Returns the full size needed to draw the given level.
    fn get_rendering_size(&self, level: &Level) -> (u32, u32) {
        let (w, h) = level.extents();
        let width = w as u32 * self.tileset.tile_width();
        let height = if h > 0 {
            self.tileset.tile_height() + (h - 1) as u32 * self.tileset.tile_effective_height()
        } else {
            0
        };

        (width, height)
    }

    /// Returns the size of the drawing scaled to fit onto the screen.
    fn get_scaled_rendering_size(&self, level: &Level) -> (u32, u32) {
        let render_size = self.get_rendering_size(&level);
        let width_ratio = (self.screen_size.0 as f64) / (render_size.0 as f64);
        let h = self.screen_size.1 - self.bar_height;
        let height_ratio = (h as f64) / (render_size.1 as f64);
        let ratio = f64::min(1.0, f64::min(width_ratio, height_ratio));

        let scale = |sz: u32| {
            (ratio * (sz as f64)).floor() as u32
        };

        (scale(render_size.0), scale(render_size.1))
    }

    /// Returns the Rect of an image of given dimensions so that it's centered on the screen.
    fn get_centered_image_rect(&self, img_size: (u32, u32)) -> Option<Rect> {
        let x = (self.screen_size.0 - img_size.0) as i32 / 2;
        let y = (self.screen_size.1 - self.bar_height - img_size.1) as i32 / 2;
        Some(Rect::new_unwrap(x, y, img_size.0, img_size.1))
    }
}

/// Represents a kind of tile.
#[derive(Copy, Clone, Debug)]
enum Tile {
    /// Standard floor tile
    Floor,
    /// Wall tile
    Wall,
    /// Rock tile
    Rock,
    /// Target square tile
    Square,
    /// Player tile
    Player,
    /// Shadow tile
    Shadow(ShadowFlags),
}

/// Holds information about a tileset.
trait TileSet {
    /// Returns the associated texture
    fn texture(&self) -> &Texture;

    /// Returns the width of a tile.
    fn tile_width(&self) -> u32;

    /// Returns the height of a tile.
    fn tile_height(&self) -> u32;

    /// Returns the effective height of a tile (used for stacking)
    fn tile_effective_height(&self) -> u32;

    /// Returns the offset need to draw items on the floor.
    fn tile_offset(&self) -> i32;

    /// Returns the location of the tile in the tileset texture.
    fn location(&self, tile: Tile) -> Option<(u32, u32)> {
        match tile {
           Tile::Floor => Some((0, 0)),
           Tile::Wall => Some((0, 2)),
           Tile::Rock => Some((2, 0)),
           Tile::Square => Some((1, 0)),
           Tile::Player => Some((3, 0)),
           Tile::Shadow(N_EDGE) => Some((4, 0)),
           Tile::Shadow(S_EDGE) => Some((5, 0)),
           Tile::Shadow(E_EDGE) => Some((0, 1)),
           Tile::Shadow(W_EDGE) => Some((1, 1)),
           Tile::Shadow(NE_CORNER) => Some((2, 1)),
           Tile::Shadow(NW_CORNER) => Some((3, 1)),
           Tile::Shadow(SE_CORNER) => Some((4, 1)),
           Tile::Shadow(SW_CORNER) => Some((5, 1)),
           Tile::Shadow(ShadowFlags { .. }) => None,
        }
    }

    /// Returns the top-left corner coordinates of the tile corresponding
    /// to the given position.
    fn get_coordinates(&self, pos: &Position) -> (i32, i32) {
        let x = self.tile_width() as i32 * pos.column();
        let y = self.tile_effective_height() as i32 * pos.row();
        (x, y)
    }
}

/// Holds information about the big tileset.
struct BigTileSet {
    texture: Texture
}

impl BigTileSet {
    pub fn new(renderer: &Renderer) -> Self {
        BigTileSet {
            texture: renderer.load_texture(Path::new("assets/image/tileset.png")).unwrap()
        }
    }
}

impl TileSet for BigTileSet {
    fn texture(&self) -> &Texture { &self.texture }
    fn tile_width(&self) -> u32 { 101 }
    fn tile_height(&self) -> u32 { 171 }
    fn tile_effective_height(&self) -> u32 { 83 }
    fn tile_offset(&self) -> i32 { 40 }
}

/// Holds information about the small tileset.
struct SmallTileSet {
    texture: Texture
}

impl SmallTileSet {
    pub fn new(renderer: &Renderer) -> Self {
        SmallTileSet {
            texture: renderer.load_texture(Path::new("assets/image/tileset-small.png")).unwrap()
        }
    }
}

impl TileSet for SmallTileSet {
    fn texture(&self) -> &Texture { &self.texture }
    fn tile_width(&self) -> u32 { 50 }
    fn tile_height(&self) -> u32 { 85 }
    fn tile_effective_height(&self) -> u32 { 41 }
    fn tile_offset(&self) -> i32 { 20 }
}

struct Switch {
    small: SmallTileSet,
    big: BigTileSet,
}

impl Switch {
    pub fn new(renderer: &Renderer) -> Self {
        Switch {
            big: BigTileSet::new(renderer),
            small: SmallTileSet::new(renderer),
        }
    }
}

impl Deref for Switch {
    type Target = TileSet;
    fn deref(&self) -> &Self::Target {
        &self.small
    }
}

bitflags!(
    /// Represents the different kind of shadows that can be cast
    /// onto a floor tile.
    flags ShadowFlags: i32 {
        /// North edge
        const N_EDGE = 0x1,
        /// South edge
        const S_EDGE = 0x2,
        /// East edge
        const E_EDGE = 0x4,
        /// West edge
        const W_EDGE = 0x8,
        /// North East corner
        const NE_CORNER = 0x10,
        /// North West corner
        const NW_CORNER = 0x20,
        /// South East corner
        const SE_CORNER = 0x40,
        /// South West corner
        const SW_CORNER = 0x80,
    }
);

/// Returns the shadow flags for a particular position in the given level.
fn get_shadow_flags(level: &Level, pos: &Position) -> ShadowFlags {
    let north = pos.neighbor(Direction::Up);
    let south = pos.neighbor(Direction::Down);
    let west = pos.neighbor(Direction::Left);
    let east = pos.neighbor(Direction::Right);

    let mut flags = ShadowFlags::empty();
    if level.is_wall(&north) {
        flags = flags | N_EDGE;
    }
    if level.is_wall(&south) {
        flags = flags | S_EDGE;
    }
    if level.is_wall(&west) {
        flags = flags | W_EDGE;
    }
    if level.is_wall(&east) {
        flags = flags | E_EDGE;
    }
    if level.is_wall(&north.neighbor(Direction::Right)) && !flags.intersects(N_EDGE | E_EDGE) {
        flags = flags | NE_CORNER;
    }
    if level.is_wall(&north.neighbor(Direction::Left)) && !flags.intersects(N_EDGE | W_EDGE) {
        flags = flags | NW_CORNER;
    }
    if level.is_wall(&south.neighbor(Direction::Right)) && !flags.intersects(S_EDGE | E_EDGE) {
        flags = flags | SE_CORNER;
    }
    if level.is_wall(&south.neighbor(Direction::Left)) && !flags.intersects(S_EDGE | W_EDGE) {
        flags = flags | SW_CORNER;
    }
    flags
}

<commit_msg>Moved the get_rendering_size function to the TileSet trait.<commit_after>// This file is part of sokoban-rs
// Copyright 2015 Sébastien Watteau
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use std::ops::Deref;
use std::path::Path;
use sdl2::rect::Rect;
use sdl2::pixels::{Color, PixelFormatEnum};
use sdl2::render::{Renderer, Texture};
use sdl2_image::LoadTexture;
use sdl2_ttf::Font;

use super::game::{Level, Position, Direction};

/// The Drawer struct is responsible for drawing the game onto the screen.
pub struct Drawer<'a> {
    /// The underlying SDL renderer
    renderer: Renderer<'a>,
    /// The active tileset
    tileset: Switch,
    /// The font used to display text
    font: Font,
    /// The size of the screen in pixels
    screen_size: (u32, u32),
    /// The height of the status bar
    bar_height: u32,
    /// The color of the status bar
    bar_color: Color,
    /// The color of the text in the status bar
    bar_text_color: Color,
}

/// Represents a location for text in the status bar
enum StatusBarLocation {
    FlushLeft,
    FlushRight,
}

impl<'a> Drawer<'a> {
    /// Creates a new Drawer instance.
    pub fn new(renderer: Renderer<'a>) -> Drawer {
        let font = Font::from_file(Path::new("assets/font/RujisHandwritingFontv.2.0.ttf"), 20).unwrap();
        let screen_size = renderer.window().unwrap().drawable_size();
        let tileset = Switch::new(&renderer);
        Drawer {
            renderer: renderer,
            tileset: tileset,
            font: font,
            screen_size: screen_size,
            bar_height: 32,
            bar_color: Color::RGBA(20, 20, 20, 255),
            bar_text_color: Color::RGBA(255, 192, 0, 255),
        }
    }

    /// Draws a level onto the screen.
    pub fn draw(&mut self, level: &Level) {
        // Draw a full-size image onto an off-screen buffer
        let fullsize = self.tileset.get_rendering_size(level.extents());
        let _ = self.renderer.render_target()
            .expect("Render targets are not supported")
            .create_and_set(PixelFormatEnum::RGBA8888, fullsize);

        self.draw_fullsize(level);

        // Copy onto the screen with appropriate scaling
        let final_rect = self.get_centered_image_rect(self.get_scaled_rendering_size(&level));
        let texture = self.renderer.render_target()
            .unwrap()
            .reset()
            .unwrap_or_else(|err| panic!("Could not reset to the default render target: {}", err))
            .unwrap_or_else(|| panic!("Could not get the offscreen texture"));

        self.renderer.clear();
        self.renderer.copy(&texture, Some(Rect::new_unwrap(0, 0, fullsize.0, fullsize.1)), final_rect);

        self.draw_status_bar(&level);

        self.renderer.present();
    }

    /// Draws a full-size image of the given level onto the current render target.
    fn draw_fullsize(&mut self, level: &Level) {
        let (cols, rows) = level.extents();
        self.renderer.set_draw_color(Color::RGB(0, 0, 0));
        self.renderer.clear();

        for j in (0..rows) {
            for i in (0..cols) {
                let pos = Position::new(j, i);
                let (x, y) = self.tileset.get_coordinates(&pos);

                // First draw the floor tiles
                if level.is_square(&pos) {
                    self.draw_tile(Tile::Square, x, y);
                } else {
                    self.draw_tile(Tile::Floor, x, y);
                }

                // Add the shadows
                let flags = get_shadow_flags(&level, &pos);
                for f in &[N_EDGE, S_EDGE, E_EDGE, W_EDGE, NE_CORNER, NW_CORNER, SE_CORNER, SW_CORNER] {
                    if flags.contains(*f) {
                        self.draw_tile(Tile::Shadow(*f), x, y);
                    }
                }

                // Draw the other items
                let z = y - self.tileset.tile_offset();
                if level.is_wall(&pos) {
                    self.draw_tile(Tile::Wall, x, z);
                }
                if level.is_box(&pos) {
                    self.draw_tile(Tile::Rock, x, z);
                }
                if level.is_player(&pos) {
                    self.draw_tile(Tile::Player, x, z);
                }
            }
        }
    }

    /// Draws the status bar
    fn draw_status_bar(&mut self, level: &Level) {
        let prev_color = self.renderer.draw_color();
        self.renderer.set_draw_color(self.bar_color);
        let rect = Rect::new_unwrap(0, (self.screen_size.1 - self.bar_height) as i32, self.screen_size.0, self.bar_height);
        self.renderer.fill_rect(rect);
        self.renderer.set_draw_color(prev_color);

        // Draw the number of moves
        let s = format!("# moves: {}", level.get_steps());
        self.draw_status_text(&s, StatusBarLocation::FlushLeft);

        // Draw the level's title
        self.draw_status_text(level.title(), StatusBarLocation::FlushRight);
    }

    /// Draws text in the status bar
    fn draw_status_text(&mut self, text: &str, location: StatusBarLocation) {
        let surface = self.font.render_str_blended(text, self.bar_text_color).unwrap();
        let texture = self.renderer.create_texture_from_surface(&surface).unwrap();
        let margin = 4;
        let (w, h) = {
            let q = texture.query();
            (q.width, q.height)
        };
        let (x, y) = match location {
            StatusBarLocation::FlushLeft => {
                (margin as i32, (self.screen_size.1 - margin - h) as i32)
            },
            StatusBarLocation::FlushRight => {
                ((self.screen_size.0 - margin - w) as i32, (self.screen_size.1 - margin - h) as i32)
            },
        };
        self.renderer.copy(&texture, None, Some(Rect::new_unwrap(x, y, w, h)));
    }

    /// Draws a tile at the given coordinates.
    fn draw_tile(&mut self, tile: Tile, x: i32, y: i32) {
        let (col, row) = self.tileset.location(tile).unwrap_or_else(|| {
            panic!("No image for this tile: {:?}", tile);
        });
        let tile_rect = self.get_tile_rect(col, row);
        self.renderer.copy(self.tileset.texture(), tile_rect, Some(Rect::new_unwrap(x, y, self.tileset.tile_width(), self.tileset.tile_height())));
    }

    /// Returns the Rect of the tile located at the given row and column in the texture.
    fn get_tile_rect(&self, col: u32, row: u32) -> Option<Rect> {
        let (w, h) = (self.tileset.tile_width(), self.tileset.tile_height());
        let x = (col * w) as i32;
        let y = (row * h) as i32;
        Some(Rect::new_unwrap(x, y, w, h))
    }

    /// Returns the size of the drawing scaled to fit onto the screen.
    fn get_scaled_rendering_size(&self, level: &Level) -> (u32, u32) {
        let render_size = self.tileset.get_rendering_size(level.extents());
        let width_ratio = (self.screen_size.0 as f64) / (render_size.0 as f64);
        let h = self.screen_size.1 - self.bar_height;
        let height_ratio = (h as f64) / (render_size.1 as f64);
        let ratio = f64::min(1.0, f64::min(width_ratio, height_ratio));

        let scale = |sz: u32| {
            (ratio * (sz as f64)).floor() as u32
        };

        (scale(render_size.0), scale(render_size.1))
    }

    /// Returns the Rect of an image of given dimensions so that it's centered on the screen.
    fn get_centered_image_rect(&self, img_size: (u32, u32)) -> Option<Rect> {
        let x = (self.screen_size.0 - img_size.0) as i32 / 2;
        let y = (self.screen_size.1 - self.bar_height - img_size.1) as i32 / 2;
        Some(Rect::new_unwrap(x, y, img_size.0, img_size.1))
    }
}

/// Represents a kind of tile.
#[derive(Copy, Clone, Debug)]
enum Tile {
    /// Standard floor tile
    Floor,
    /// Wall tile
    Wall,
    /// Rock tile
    Rock,
    /// Target square tile
    Square,
    /// Player tile
    Player,
    /// Shadow tile
    Shadow(ShadowFlags),
}

/// Holds information about a tileset.
trait TileSet {
    /// Returns the associated texture
    fn texture(&self) -> &Texture;

    /// Returns the width of a tile.
    fn tile_width(&self) -> u32;

    /// Returns the height of a tile.
    fn tile_height(&self) -> u32;

    /// Returns the effective height of a tile (used for stacking)
    fn tile_effective_height(&self) -> u32;

    /// Returns the offset need to draw items on the floor.
    fn tile_offset(&self) -> i32;

    /// Returns the location of the tile in the tileset texture.
    fn location(&self, tile: Tile) -> Option<(u32, u32)> {
        match tile {
           Tile::Floor => Some((0, 0)),
           Tile::Wall => Some((0, 2)),
           Tile::Rock => Some((2, 0)),
           Tile::Square => Some((1, 0)),
           Tile::Player => Some((3, 0)),
           Tile::Shadow(N_EDGE) => Some((4, 0)),
           Tile::Shadow(S_EDGE) => Some((5, 0)),
           Tile::Shadow(E_EDGE) => Some((0, 1)),
           Tile::Shadow(W_EDGE) => Some((1, 1)),
           Tile::Shadow(NE_CORNER) => Some((2, 1)),
           Tile::Shadow(NW_CORNER) => Some((3, 1)),
           Tile::Shadow(SE_CORNER) => Some((4, 1)),
           Tile::Shadow(SW_CORNER) => Some((5, 1)),
           Tile::Shadow(ShadowFlags { .. }) => None,
        }
    }

    /// Returns the top-left corner coordinates of the tile corresponding
    /// to the given position.
    fn get_coordinates(&self, pos: &Position) -> (i32, i32) {
        let x = self.tile_width() as i32 * pos.column();
        let y = self.tile_effective_height() as i32 * pos.row();
        (x, y)
    }

    /// Returns the full size needed to draw a level of the given dimensions.
    fn get_rendering_size(&self, extents: (i32, i32)) -> (u32, u32) {
        let width = extents.0 as u32 * self.tile_width();
        let height = if extents.1 > 0 {
            self.tile_height() + (extents.1 - 1) as u32 * self.tile_effective_height()
        } else {
            0
        };
        (width, height)
    }
}

/// Holds information about the big tileset.
struct BigTileSet {
    texture: Texture
}

impl BigTileSet {
    pub fn new(renderer: &Renderer) -> Self {
        BigTileSet {
            texture: renderer.load_texture(Path::new("assets/image/tileset.png")).unwrap()
        }
    }
}

impl TileSet for BigTileSet {
    fn texture(&self) -> &Texture { &self.texture }
    fn tile_width(&self) -> u32 { 101 }
    fn tile_height(&self) -> u32 { 171 }
    fn tile_effective_height(&self) -> u32 { 83 }
    fn tile_offset(&self) -> i32 { 40 }
}

/// Holds information about the small tileset.
struct SmallTileSet {
    texture: Texture
}

impl SmallTileSet {
    pub fn new(renderer: &Renderer) -> Self {
        SmallTileSet {
            texture: renderer.load_texture(Path::new("assets/image/tileset-small.png")).unwrap()
        }
    }
}

impl TileSet for SmallTileSet {
    fn texture(&self) -> &Texture { &self.texture }
    fn tile_width(&self) -> u32 { 50 }
    fn tile_height(&self) -> u32 { 85 }
    fn tile_effective_height(&self) -> u32 { 41 }
    fn tile_offset(&self) -> i32 { 20 }
}

struct Switch {
    small: SmallTileSet,
    big: BigTileSet,
}

impl Switch {
    pub fn new(renderer: &Renderer) -> Self {
        Switch {
            big: BigTileSet::new(renderer),
            small: SmallTileSet::new(renderer),
        }
    }
}

impl Deref for Switch {
    type Target = TileSet;
    fn deref(&self) -> &Self::Target {
        &self.small
    }
}

bitflags!(
    /// Represents the different kind of shadows that can be cast
    /// onto a floor tile.
    flags ShadowFlags: i32 {
        /// North edge
        const N_EDGE = 0x1,
        /// South edge
        const S_EDGE = 0x2,
        /// East edge
        const E_EDGE = 0x4,
        /// West edge
        const W_EDGE = 0x8,
        /// North East corner
        const NE_CORNER = 0x10,
        /// North West corner
        const NW_CORNER = 0x20,
        /// South East corner
        const SE_CORNER = 0x40,
        /// South West corner
        const SW_CORNER = 0x80,
    }
);

/// Returns the shadow flags for a particular position in the given level.
fn get_shadow_flags(level: &Level, pos: &Position) -> ShadowFlags {
    let north = pos.neighbor(Direction::Up);
    let south = pos.neighbor(Direction::Down);
    let west = pos.neighbor(Direction::Left);
    let east = pos.neighbor(Direction::Right);

    let mut flags = ShadowFlags::empty();
    if level.is_wall(&north) {
        flags = flags | N_EDGE;
    }
    if level.is_wall(&south) {
        flags = flags | S_EDGE;
    }
    if level.is_wall(&west) {
        flags = flags | W_EDGE;
    }
    if level.is_wall(&east) {
        flags = flags | E_EDGE;
    }
    if level.is_wall(&north.neighbor(Direction::Right)) && !flags.intersects(N_EDGE | E_EDGE) {
        flags = flags | NE_CORNER;
    }
    if level.is_wall(&north.neighbor(Direction::Left)) && !flags.intersects(N_EDGE | W_EDGE) {
        flags = flags | NW_CORNER;
    }
    if level.is_wall(&south.neighbor(Direction::Right)) && !flags.intersects(S_EDGE | E_EDGE) {
        flags = flags | SE_CORNER;
    }
    if level.is_wall(&south.neighbor(Direction::Left)) && !flags.intersects(S_EDGE | W_EDGE) {
        flags = flags | SW_CORNER;
    }
    flags
}

<|endoftext|>


<commit_before>use refs;

use git2::{ Oid, Repository, Note, Commit };
use super::{ Error, Result, Request, Requests, CIStatuses, Analyses, Comments, Event, Events };

pub struct Review<'r> {
  git: &'r Repository,
  id: Oid,
  request: Request,
  requests: Vec<Request>,
}

impl<'r> Review<'r> {
  pub fn for_commit(git: &'r Repository, id: Oid) -> Result<Review<'r>> {
    git.find_note(refs::REVIEWS, id)
      .map_err(From::from)
      .and_then(|note| Review::from_note(git, id, note))
  }

  pub fn from_note(git: &'r Repository, id: Oid, note: Note<'r>) -> Result<Review<'r>> {
    Request::all_from_note(id, note)
      .and_then(|mut requests|
        if requests.is_empty() {
          Err(Error::NotFound)
        } else {
          requests.sort_by(|a, b| a.timestamp().cmp(&b.timestamp()));
          Ok((requests.pop().unwrap(), requests))
        })
      .map(|(request, requests)| Review::from_requests(git, id, request, requests))
  }

  pub fn id(&self) -> Oid {
    self.id
  }

  pub fn commit(&self) -> Result<Commit> {
    self.git.find_commit(self.id).map_err(From::from)
  }

  pub fn request(&self) -> &Request {
    &self.request
  }

  pub fn all_requests(&self) -> Requests {
    Requests::new(&self.request, &self.requests)
  }

  pub fn all_ci_statuses(&self) -> CIStatuses {
    CIStatuses::all_for_commit(&self.git, self.id())
  }

  pub fn latest_ci_statuses(&self) -> CIStatuses {
    CIStatuses::latest_for_commit(&self.git, self.id())
  }

  pub fn comments(&self) -> Comments {
    Comments::for_commit(&self.git, self.id())
  }

  pub fn analyses(&self) -> Analyses {
    Analyses::for_commit(&self.git, self.id())
  }

  pub fn events(&self) -> Events {
    let mut vec: Vec<Box<Event>> = vec![Box::new(self.request().clone()) as Box<Event>]
      .into_iter()
      .chain(self.all_ci_statuses().map(|status| Box::new(status) as Box<Event>))
      .chain(self.comments().map(|comment| Box::new(comment) as Box<Event>))
      .chain(self.analyses().map(|analysis| Box::new(analysis) as Box<Event>))
      .collect();
    vec.sort_by(|a, b| a.timestamp().cmp(&b.timestamp()));
    Events::new(vec)
  }

  fn from_requests(git: &'r Repository, id: Oid, request: Request, requests: Vec<Request>) -> Review<'r> {
    Review {
      git: git,
      id: id,
      request: request,
      requests: requests,
    }
  }
}
<commit_msg>Force request to be the first event even if its timestamp is later<commit_after>use refs;

use git2::{ Oid, Repository, Note, Commit };
use super::{ Error, Result, Request, Requests, CIStatuses, Analyses, Comments, Event, Events };

pub struct Review<'r> {
  git: &'r Repository,
  id: Oid,
  request: Request,
  requests: Vec<Request>,
}

impl<'r> Review<'r> {
  pub fn for_commit(git: &'r Repository, id: Oid) -> Result<Review<'r>> {
    git.find_note(refs::REVIEWS, id)
      .map_err(From::from)
      .and_then(|note| Review::from_note(git, id, note))
  }

  pub fn from_note(git: &'r Repository, id: Oid, note: Note<'r>) -> Result<Review<'r>> {
    Request::all_from_note(id, note)
      .and_then(|mut requests|
        if requests.is_empty() {
          Err(Error::NotFound)
        } else {
          requests.sort_by(|a, b| a.timestamp().cmp(&b.timestamp()));
          Ok((requests.pop().unwrap(), requests))
        })
      .map(|(request, requests)| Review::from_requests(git, id, request, requests))
  }

  pub fn id(&self) -> Oid {
    self.id
  }

  pub fn commit(&self) -> Result<Commit> {
    self.git.find_commit(self.id).map_err(From::from)
  }

  pub fn request(&self) -> &Request {
    &self.request
  }

  pub fn all_requests(&self) -> Requests {
    Requests::new(&self.request, &self.requests)
  }

  pub fn all_ci_statuses(&self) -> CIStatuses {
    CIStatuses::all_for_commit(&self.git, self.id())
  }

  pub fn latest_ci_statuses(&self) -> CIStatuses {
    CIStatuses::latest_for_commit(&self.git, self.id())
  }

  pub fn comments(&self) -> Comments {
    Comments::for_commit(&self.git, self.id())
  }

  pub fn analyses(&self) -> Analyses {
    Analyses::for_commit(&self.git, self.id())
  }

  pub fn events(&self) -> Events {
    let mut vec = Vec::new();
    vec.extend(self.all_ci_statuses().map(|status| Box::new(status) as Box<Event>));
    vec.extend(self.comments().map(|comment| Box::new(comment) as Box<Event>));
    vec.extend(self.analyses().map(|analysis| Box::new(analysis) as Box<Event>));

    vec.sort_by(|a, b| a.timestamp().cmp(&b.timestamp()));
    vec.insert(0, Box::new(self.request().clone()) as Box<Event>);
    Events::new(vec)
  }

  fn from_requests(git: &'r Repository, id: Oid, request: Request, requests: Vec<Request>) -> Review<'r> {
    Review {
      git: git,
      id: id,
      request: request,
      requests: requests,
    }
  }
}
<|endoftext|>


<commit_before>#![feature(phase)]
#[phase(syntax, link)] extern crate log;
extern crate collections;
extern crate sync;
extern crate rand;
extern crate serialize;
use std::cmp::{min, max};
use std::io::{IoResult, IoError, InvalidInput, SeekStyle, MemWriter, BufWriter, BufReader};
use std::io::{SeekSet, SeekEnd, SeekCur};
use serialize::ebml::{reader,writer};
use serialize::{Encodable, Decodable};
mod buffer;

#[deriving(Encodable, Decodable)]
enum SqlType {
	Char(uint),
	Varchar(uint),
	Integer,
}

#[deriving(Encodable, Decodable)]
enum SqlAttribute {
	Null,
	NotNull,
}

#[deriving(Encodable, Decodable)]
struct Column {
	name: ~str,
	datatype: SqlType,
	attributes: Vec<SqlAttribute>,
}

#[deriving(Encodable, Decodable)]
struct Relation {
	name: ~str,
	columns: Vec<Column>,
}

impl Relation {
	pub fn new(name: ~str) -> Relation {
		Relation {name: name, columns: Vec::new()}
	}

	pub fn add_column(&mut self, column: Column) {
		self.columns.push(column);
	}
}

#[deriving(Encodable, Decodable)]
struct Schema {
	relations: Vec<Relation>,
}

struct SchemaWriter{
	buffer_manager: buffer::BufferManager,
	location: u64,
	maximum: u64,
}

impl Writer for SchemaWriter {
	fn write(&mut self, buf: &[u8]) -> IoResult<()> {
		let pageno = self.location / buffer::PAGE_SIZE as u64 + 1;
		let start_from = (self.location % buffer::PAGE_SIZE as u64) as uint;

		let pagelock = self.buffer_manager.fix_page(pageno).unwrap_or_else(|| fail!("Fix page failed"));
		let mut copied = 0;
		{
			let mut page = pagelock.write();
			let content = page.get_mut_data();

			for i in range(0, min(buf.len(), buffer::PAGE_SIZE)) {
				content[start_from+i] = buf[i];
				copied += 1;
				self.location += 1;
			}
		}
		self.buffer_manager.unfix_page(pagelock, true);
		if self.location > self.maximum {
			self.maximum = self.location;
			let pagelock = self.buffer_manager.fix_page(0).unwrap_or_else(
				|| fail!("Fixing zero page failed"));
			{
				let mut page = pagelock.write();
				let content = page.get_mut_data();
				let mut writer = BufWriter::new(content);
				writer.write_le_u64(self.maximum);
			}
			self.buffer_manager.unfix_page(pagelock, true);
		}
		//TODO remaining bytes from buf
		println!("copied {}/{}, location: {}", copied, buf.len(), self.location);
		//assert!(copied, buf.len());
		if copied == buf.len() {
			Ok(())
		} else {
			Err(IoError {
				kind: InvalidInput,
				desc: "Did not copy all data",
				detail: None,
			})
		}
	}
}

impl Seek for SchemaWriter {
	fn tell(&self) -> IoResult<u64> {
		Ok(self.location)
	}

	fn seek(&mut self, pos: i64, style: SeekStyle) -> IoResult<()> {
		println!("Seeking {}", pos);
		match style {
			SeekSet => {
				self.location = pos as u64;
				Ok(())
			}
			SeekEnd => {
				Err(IoError {
					kind: InvalidInput,
					desc: "seek to end not supported",
					detail: None,
				})
			}
			SeekCur => {
				self.location = (self.location as i64 + pos) as u64;
				Ok(())
			}
		}
	}
}

impl SchemaWriter {
	pub fn new(bufman: buffer::BufferManager) -> SchemaWriter {
		SchemaWriter {buffer_manager: bufman, location: 0,
			maximum: 0}
	}

	pub fn get_data(&mut self) -> Vec<u8> {
		let pagelock = self.buffer_manager.fix_page(0).unwrap_or_else(
			|| fail!("Failed fixing 0 page for schema length"));
		let mut size = 0_u64;
		{
			let page = pagelock.read();
			let mut reader = BufReader::new(page.get_data());
			size = reader.read_le_u64().unwrap();
		}
		self.buffer_manager.unfix_page(pagelock, false);
		println!("Size: {}", size);

		let mut data: Vec<u8> = Vec::with_capacity(size as uint);
		let mut read = 0;

		println!("location: {}", self.location);
		for i in range(1, self.location / buffer::PAGE_SIZE as u64 + 2) {
			println!("Reading page {}", i);
			let pagelock = self.buffer_manager.fix_page(i).unwrap_or_else(
				|| fail!("Failed fixing page {}", i));
			{
				let page = pagelock.read();
				let content = page.get_data();
				let mut j = 0;
				while read < size {
					data.push(content[j]);
					j += 1;
					read += 1;
				}
			}
			self.buffer_manager.unfix_page(pagelock, false);
		}
		data
	}
}

impl Schema {
	pub fn new() -> Schema {
		Schema {relations: Vec::new()}
	}

	pub fn new_from_disk(bufmanager: buffer::BufferManager) -> Schema {
		// TODO
		Schema {relations: Vec::new()}
	}

	pub fn add_relation(&mut self, relation: Relation) {
		self.relations.push(relation);
	}

	pub fn save_to_disk(&self, bufmanager: buffer::BufferManager) {
		let mut wr1 = MemWriter::new();
		let mut wr2 = SchemaWriter::new(bufmanager);
		let v: u64 = 42;
		{
			let mut ebml_w1 = writer::Encoder(&mut wr1);
			let _ = self.encode(&mut ebml_w1);
			let mut ebml_w2 = writer::Encoder(&mut wr2);
			let _ = self.encode(&mut ebml_w2);
		}

		let dta = wr2.get_data();
		println!("wr1 len: {}", wr1.get_ref().len());
		println!("wr2 len: {}", dta.len());
		let ebml_doc1 = reader::Doc(wr1.get_ref());
		let ebml_doc2 = reader::Doc(dta.slice_to(290));
		let mut deser1 = reader::Decoder(ebml_doc1);
		let mut deser2 = reader::Decoder(ebml_doc2);
		let v1: Schema = match Decodable::decode(&mut deser1) {
			Ok(v) => v,
			Err(e) => fail!("Error decoding: {}", e),
		};
		println!("v1 == {:?}", v1);
		let v2: Schema = match Decodable::decode(&mut deser2) {
			Ok(v) => v,
			Err(e) => fail!("Error decoding: {}", e),
		};
		println!("v2 == {:?}", v2);

		for rel in self.relations.iter() {
			//rel.save_to_disk()
		}
	}
}

struct Record {
	// TODO: do we even need this?
	len: uint,
	data: Vec<u8>,
}

impl Record {
	/* move semantics, no copy */
	pub fn new(len: uint, data: Vec<u8>) -> Record {
		Record {len: len, data: data}
	}

	pub fn get_data<'a>(&'a self) -> &'a [u8] {
		self.data.as_slice()
	}
}

struct SPSegment;

/* our newtype struct: create a TID type as an alias to another type */
struct TID(u8);

impl SPSegment {
	pub fn insert(r: Record) -> TID {
		TID(0)
	}

	pub fn remove(tid: TID) -> bool {
		false
	}

	pub fn lookup(tid: TID) -> Record {
		// TODO
		Record {len: 1, data: vec!(1)}
	}

	pub fn update(tid: TID, r: Record) -> bool {
		false
	}
}

#[test]
fn main() {
}

#[test]
fn create_schema() {
	let name = Column {name: ~"name", datatype: Varchar(128), attributes: vec!(NotNull)};
	let age = Column {name: ~"age", datatype: Integer, attributes: vec!(NotNull)};
	let mut relation = Relation::new(~"Person");
	relation.add_column(name);
	relation.add_column(age);
	let mut schema = Schema::new();
	schema.add_relation(relation);
	let mut manager = buffer::BufferManager::new(1024, Path::new("."));
	schema.save_to_disk(manager);
	assert!(false);
}
<commit_msg>Implemented saving and loading schemas to disk in methods<commit_after>#![feature(phase)]
#[phase(syntax, link)] extern crate log;
extern crate collections;
extern crate sync;
extern crate rand;
extern crate serialize;
use std::cmp::{min, max};
use std::io::{IoResult, IoError, InvalidInput, SeekStyle, MemWriter, BufWriter, BufReader};
use std::io::{SeekSet, SeekEnd, SeekCur};
use serialize::ebml::{reader,writer};
use serialize::{Encodable, Decodable};
mod buffer;

#[deriving(Encodable, Decodable)]
enum SqlType {
	Char(uint),
	Varchar(uint),
	Integer,
}

#[deriving(Encodable, Decodable)]
enum SqlAttribute {
	Null,
	NotNull,
}

#[deriving(Encodable, Decodable)]
struct Column {
	name: ~str,
	datatype: SqlType,
	attributes: Vec<SqlAttribute>,
}

#[deriving(Encodable, Decodable)]
struct Relation {
	name: ~str,
	columns: Vec<Column>,
}

impl Relation {
	pub fn new(name: ~str) -> Relation {
		Relation {name: name, columns: Vec::new()}
	}

	pub fn add_column(&mut self, column: Column) {
		self.columns.push(column);
	}
}

#[deriving(Encodable, Decodable)]
struct Schema {
	relations: Vec<Relation>,
}

struct SchemaWriter<'a> {
	buffer_manager: &'a mut buffer::BufferManager,
	location: u64,
	maximum: u64,
}

impl<'a> Writer for SchemaWriter<'a> {
	fn write(&mut self, buf: &[u8]) -> IoResult<()> {
		let pageno = self.location / buffer::PAGE_SIZE as u64 + 1;
		let start_from = (self.location % buffer::PAGE_SIZE as u64) as uint;

		let pagelock = self.buffer_manager.fix_page(pageno).unwrap_or_else(|| fail!("Fix page failed"));
		let mut copied = 0;
		{
			let mut page = pagelock.write();
			let content = page.get_mut_data();

			for i in range(0, min(buf.len(), buffer::PAGE_SIZE)) {
				content[start_from+i] = buf[i];
				copied += 1;
				self.location += 1;
			}
		}
		self.buffer_manager.unfix_page(pagelock, true);
		if self.location > self.maximum {
			self.maximum = self.location;
			let pagelock = self.buffer_manager.fix_page(0).unwrap_or_else(
				|| fail!("Fixing zero page failed"));
			{
				let mut page = pagelock.write();
				let content = page.get_mut_data();
				let mut writer = BufWriter::new(content);
				writer.write_le_u64(self.maximum);
			}
			self.buffer_manager.unfix_page(pagelock, true);
		}
		//TODO remaining bytes from buf
		println!("copied {}/{}, location: {}", copied, buf.len(), self.location);
		//assert!(copied, buf.len());
		if copied == buf.len() {
			Ok(())
		} else {
			Err(IoError {
				kind: InvalidInput,
				desc: "Did not copy all data",
				detail: None,
			})
		}
	}
}

impl<'a> Seek for SchemaWriter<'a> {
	fn tell(&self) -> IoResult<u64> {
		Ok(self.location)
	}

	fn seek(&mut self, pos: i64, style: SeekStyle) -> IoResult<()> {
		println!("Seeking {}", pos);
		match style {
			SeekSet => {
				self.location = pos as u64;
				Ok(())
			}
			SeekEnd => {
				Err(IoError {
					kind: InvalidInput,
					desc: "seek to end not supported",
					detail: None,
				})
			}
			SeekCur => {
				self.location = (self.location as i64 + pos) as u64;
				Ok(())
			}
		}
	}
}

impl<'a> SchemaWriter<'a> {
	pub fn new<'b>(bufman: &'b mut buffer::BufferManager) -> SchemaWriter<'b> {
		SchemaWriter {buffer_manager: bufman, location: 0,
			maximum: 0}
	}

	pub fn get_data(&mut self) -> Vec<u8> {
		let pagelock = self.buffer_manager.fix_page(0).unwrap_or_else(
			|| fail!("Failed fixing 0 page for schema length"));
		let mut size = 0_u64;
		{
			let page = pagelock.read();
			let mut reader = BufReader::new(page.get_data());
			size = reader.read_le_u64().unwrap();
		}
		self.buffer_manager.unfix_page(pagelock, false);
		println!("Size: {}", size);

		let mut data: Vec<u8> = Vec::with_capacity(size as uint);
		let mut read = 0;

		println!("location: {}", self.location);
		for i in range(1, self.location / buffer::PAGE_SIZE as u64 + 2) {
			println!("Reading page {}", i);
			let pagelock = self.buffer_manager.fix_page(i).unwrap_or_else(
				|| fail!("Failed fixing page {}", i));
			{
				let page = pagelock.read();
				let content = page.get_data();
				let mut j = 0;
				while read < size {
					data.push(content[j]);
					j += 1;
					read += 1;
				}
			}
			self.buffer_manager.unfix_page(pagelock, false);
		}
		data
	}
}

impl Schema {
	pub fn new() -> Schema {
		Schema {relations: Vec::new()}
	}

	pub fn new_from_disk(bufmanager: &mut buffer::BufferManager) -> Schema {
		let mut wr = SchemaWriter::new(bufmanager);
		let data = wr.get_data();
		let ebml_doc = reader::Doc(data.as_slice());
		let mut deser = reader::Decoder(ebml_doc);
		let value: Schema = match Decodable::decode(&mut deser) {
			Ok(v) => v,
			Err(e) => fail!("Error decoding: {}", e),
		};
		value
	}

	pub fn add_relation(&mut self, relation: Relation) {
		self.relations.push(relation);
	}

	pub fn save_to_disk(&self, bufmanager: &mut buffer::BufferManager) {
		let mut wr1 = MemWriter::new();
		let mut wr2 = SchemaWriter::new(bufmanager);
		{
			let mut ebml_w1 = writer::Encoder(&mut wr1);
			let _ = self.encode(&mut ebml_w1);
			let mut ebml_w2 = writer::Encoder(&mut wr2);
			let _ = self.encode(&mut ebml_w2);
		}

		let dta = wr2.get_data();
		println!("wr1 len: {}", wr1.get_ref().len());
		println!("wr2 len: {}", dta.len());
		let ebml_doc1 = reader::Doc(wr1.get_ref());
		let ebml_doc2 = reader::Doc(dta.as_slice());
		let mut deser1 = reader::Decoder(ebml_doc1);
		let mut deser2 = reader::Decoder(ebml_doc2);
		let v1: Schema = match Decodable::decode(&mut deser1) {
			Ok(v) => v,
			Err(e) => fail!("Error decoding: {}", e),
		};
		println!("v1 == {:?}", v1);
		let v2: Schema = match Decodable::decode(&mut deser2) {
			Ok(v) => v,
			Err(e) => fail!("Error decoding: {}", e),
		};
		println!("v2 == {:?}", v2);
	}
}

struct Record {
	// TODO: do we even need this?
	len: uint,
	data: Vec<u8>,
}

impl Record {
	/* move semantics, no copy */
	pub fn new(len: uint, data: Vec<u8>) -> Record {
		Record {len: len, data: data}
	}

	pub fn get_data<'a>(&'a self) -> &'a [u8] {
		self.data.as_slice()
	}
}

struct SPSegment;

/* our newtype struct: create a TID type as an alias to another type */
struct TID(u8);

impl SPSegment {
	pub fn insert(r: Record) -> TID {
		TID(0)
	}

	pub fn remove(tid: TID) -> bool {
		false
	}

	pub fn lookup(tid: TID) -> Record {
		// TODO
		Record {len: 1, data: vec!(1)}
	}

	pub fn update(tid: TID, r: Record) -> bool {
		false
	}
}

#[test]
fn main() {
}

#[test]
fn create_schema() {
	let name = Column {name: ~"name", datatype: Varchar(128), attributes: vec!(NotNull)};
	let age = Column {name: ~"age", datatype: Integer, attributes: vec!(NotNull)};
	let mut relation = Relation::new(~"Person");
	relation.add_column(name);
	relation.add_column(age);
	let mut schema = Schema::new();
	schema.add_relation(relation);

	let mut manager = buffer::BufferManager::new(1024, Path::new("."));
	schema.save_to_disk(&mut manager);
	let new_schema = Schema::new_from_disk(&mut manager);
	println!("new_schema == {:?}", new_schema);
	assert!(false);
}
<|endoftext|>


<commit_before>use configuration::Configuration;
use std::slice::SliceExt;
use std::cmp::min;
use score::Score;
use sorted_result_set::SortedResultSet;
use std::ascii::AsciiExt;

#[derive(Debug)]
pub struct Search<'s> {
    pub config: &'s Configuration,
    pub current: usize,
    pub query: String,
    pub result: Vec<String>,
    choice_stack: ChoiceStack<'s>,
    done: bool,
}

#[derive(Debug)]
struct ChoiceStack<'s> {
    content: Vec<Vec<&'s String>>,
}

impl <'s>ChoiceStack<'s> {
    pub fn new(input: &'s Vec<String>) -> ChoiceStack<'s> {
        let mut first_stack_frame = Vec::new();
        for choice in input.iter() {
            first_stack_frame.push(choice);
        }

        ChoiceStack { content: vec![first_stack_frame] }
    }

    pub fn push(&mut self, frame: Vec<&'s String>) {
        self.content.push(frame);
    }

    pub fn pop(&mut self) {
        if self.content.len() > 1 {
            self.content.pop();
        }
    }

    pub fn peek(&self) -> &Vec<&'s String> {
        self.content.last().unwrap()
    }

    pub fn last_size(&self) -> usize {
        self.peek().len()
    }
}

impl<'s> Search<'s> {
    pub fn blank(config: &'s Configuration) -> Search<'s> {
        let query = config.initial_search.clone();

        let choice_stack = ChoiceStack::new(&config.choices);
        let result = Search::copy_items(&config.choices, config.visible_limit);

        Search::new(config, query, choice_stack, result, 0, false)
    }

    fn copy_items(input: &Vec<String>, size: usize) -> Vec<String> {
        input.iter().take(size).map(|x| x.clone() ).collect()
    }

    fn new(config: &'s Configuration, query: String, choice_stack: ChoiceStack<'s>, result: Vec<String>, index: usize, done: bool) -> Search<'s> {
        Search { config: config,
                 current: index,
                 query: query,
                 result: result,
                 choice_stack: choice_stack,
                 done: done}
    }

    pub fn is_done(&self) -> bool {
        self.done
    }

    pub fn done(self) -> Search<'s> {
        Search::new(self.config, self.query, self.choice_stack, self.result, self.current, true)
    }

    pub fn selection(&self) -> Option<String> {
        if self.result.len() > 0 {
            Some(self.result[self.current].to_string())
        } else {
            None
        }
    }

    fn new_for_index(self, index: usize) -> Search<'s> {
        Search::new(self.config, self.query, self.choice_stack, self.result, index, self.done)
    }

    pub fn iter_matches<F: FnMut(&'s String, f32)>(query: &str, choices: &Vec<&'s String>, mut f: F) {
        let lower_query = query.to_ascii_lowercase();

        for choice in choices.iter() {
            let lower_choice = choice.to_ascii_lowercase();

            match Score::score(&lower_choice, &lower_query) {
                0.0     => continue,
                quality => f(choice, quality),
            };
        }
    }

    pub fn down(self) -> Search<'s> {
        let next_index = self.next_index();
        self.new_for_index(next_index)
    }

    pub fn up(self) -> Search<'s> {
        let next_index = self.prev_index();
        self.new_for_index(next_index)
    }

    pub fn append_to_search(mut self, input: &str) -> Search<'s> {
        let mut new_query = self.query.clone();
        new_query.push_str(input.as_slice());

        let mut results = SortedResultSet::new(self.config.visible_limit);
        let mut filtered_choices: Vec<&String> = Vec::new();
        Search::iter_matches(new_query.as_slice(), &self.choice_stack.peek(),
                        |match_str, quality| {
                                               results.push(match_str, quality);
                                               filtered_choices.push(match_str)
                                             });

        self.choice_stack.push(filtered_choices);
        Search::new(self.config, new_query, self.choice_stack, results.as_sorted_vec(), 0, self.done)
    }

    pub fn backspace(mut self) -> Search<'s> {
        let mut new_query = self.query.clone();
        new_query.pop();

        self.choice_stack.pop();

        let mut results = SortedResultSet::new(self.config.visible_limit);
        Search::iter_matches(new_query.as_slice(), &self.choice_stack.peek(),
                        |match_str, quality| {
                                               results.push(match_str, quality);
                                             });
        Search::new(self.config, new_query, self.choice_stack, results.as_sorted_vec(), 0, self.done)
    }

    fn next_index(&self) -> usize {
        let next_index = self.current + 1;

        if next_index >= self.actual_limit() {
            0
        } else {
            next_index
        }
    }

    fn prev_index(&self) -> usize {
        if self.current == 0 {
            self.actual_limit() - 1
        } else {
            self.current - 1
        }
    }

    fn actual_limit(&self) -> usize {
        min(self.config.visible_limit, self.num_matches())
    }

    pub fn num_matches(&self) -> usize {
        self.choice_stack.last_size()
    }
}

#[cfg(test)]
mod tests {
    use configuration::Configuration;
    use super::*;

    fn one_two_three() -> Vec<String> {
        vec!["one".to_string(),
             "two".to_string(),
             "three".to_string()]
    }

    fn input_times(n: usize) ->Vec<String> {
        let mut result: Vec<String> = Vec::new();
        for thing in one_two_three().iter().cycle().take(n) {
            result.push(thing.clone());
        }
        result
    }

    fn basic_configuration() -> Configuration {
        let input = one_two_three();
        Configuration::from_inputs(input, None, None)
    }

    #[test]
    fn it_selects_the_first_choice_by_default() {
        let config = basic_configuration();
        let search = Search::blank(&config);

        assert_eq!(search.selection(), Some("one".to_string()));
    }

    #[test]
    fn it_selets_the_second_when_down_is_called() {
        let config = basic_configuration();
        let search = Search::blank(&config);

        assert_eq!(search.down().selection(), Some("two".to_string()));
    }

    #[test]
    fn it_loop_around_when_reaching_end_of_list() {
        let config = basic_configuration();
        let search = Search::blank(&config);

        assert_eq!(search.down().down().down().down().selection(), Some("two".to_string()));
    }

    #[test]
    fn it_loop_around_when_reaching_top_of_list() {
        let config = basic_configuration();
        let search = Search::blank(&config);

        assert_eq!(search.up().up().selection(), Some("two".to_string()));
    }

    #[test]
    fn it_loop_around_when_reaching_visible_limit() {
        let config = basic_configuration();
        let search = Search::blank(&config);

        assert_eq!(search.down().down().down().down().selection(), Some("two".to_string()));
    }

    #[test]
    fn it_moves_down_the_filtered_search_results() {
        let config = basic_configuration();
        let search = Search::blank(&config);

        assert_eq!(search.append_to_search("t").down().selection(), Some("three".to_string()));
    }

    #[test]
    fn it_moves_down_the_filtered_search_results_twice() {
        let config = basic_configuration();
        let search = Search::blank(&config);

        assert_eq!(search.append_to_search("t").append_to_search("w").selection(), Some("two".to_string()));
    }

    #[test]
    fn it_handles_not_matching_anything() {
        let config = basic_configuration();
        let search = Search::blank(&config);

        assert_eq!(search.append_to_search("x").selection(), None);
    }

    #[test]
    fn up_match_nothing_after_filtering_all_out() {
        let config = basic_configuration();
        let search = Search::blank(&config).append_to_search("x");

        assert_eq!(search.up().selection(), None);
    }

    #[test]
    fn down_match_nothing_after_filtering_all_out() {
        let config = basic_configuration();
        let search = Search::blank(&config).append_to_search("x");

        assert_eq!(search.down().selection(), None);
    }

    #[test]
    fn backspaces_over_characters() {
        let config = basic_configuration();
        let search = Search::blank(&config).append_to_search("e");

        assert_eq!(search.query, "e");
        assert_eq!(search.backspace().query, "");
    }

    #[test]
    fn resets_the_index_when_removing_char_from_search() {
        let config = basic_configuration();
        let search = Search::blank(&config).append_to_search("e");

        assert_eq!(search.down().backspace().current, 0);
    }

    #[test]
    fn resets_the_index_when_adding_char_to_seach() {
        let config = basic_configuration();
        let search = Search::blank(&config).down();

        assert_eq!(search.append_to_search("o").current, 0);
    }

    #[test]
    fn previous_results_appear_after_backspace() {
        let config = basic_configuration();
        let search = Search::blank(&config).append_to_search("t");

        assert_eq!(search.backspace().num_matches(), 3);
    }

    #[test]
    fn initial_search_is_not_done() {
        let config = basic_configuration();
        let search = Search::blank(&config);

        assert!(!search.is_done());
    }

    #[test]
    fn backspace_over_initial_doesnt_crash() {
        let config = basic_configuration();
        let search = Search::blank(&config);

        search.backspace();
    }

    #[test]
    fn done_search_is_done() {
        let config = basic_configuration();
        let search = Search::blank(&config).done();

        assert!(search.is_done());
    }

    #[test]
    fn done_search_has_selection() {
        let config = basic_configuration();
        let search = Search::blank(&config).done();

        assert_eq!(search.selection(), Some("one".to_string()));
    }

    #[test]
    fn loop_around_when_reaching_bottom_of_choices() {
        let config = basic_configuration();
        let search = Search::blank(&config).append_to_search("n").down();

        assert_eq!(search.selection(), Some("one".to_string()));
    }

    #[test]
    fn loop_around_when_reaching_top_of_choices() {
        let config = basic_configuration();
        let search = Search::blank(&config).append_to_search("n").up();

        assert_eq!(search.selection(), Some("one".to_string()));
    }

    #[test]
    fn search_is_case_insensitive() {
        let config = basic_configuration();
        let search = Search::blank(&config).append_to_search("T");

        assert_eq!(search.num_matches(), 2);
    }

    #[test]
    fn uses_configs_visible_limit_as_result_size() {
        let input = input_times(30);

        let mut bar = Vec::new();
        for f in input.iter() {
            bar.push((*f).clone());
        }

        let config = Configuration::from_inputs(bar, None, Some(20));
        let search = Search::blank(&config).append_to_search("T");

        assert_eq!(search.num_matches(), 20);
    }
}
<commit_msg>Inline creation of first stack_frame<commit_after>use configuration::Configuration;
use score::Score;
use sorted_result_set::SortedResultSet;
use std::slice::SliceExt;
use std::cmp::min;
use std::ascii::AsciiExt;

#[derive(Debug)]
pub struct Search<'s> {
    pub config: &'s Configuration,
    pub current: usize,
    pub query: String,
    pub result: Vec<String>,
    choice_stack: ChoiceStack<'s>,
    done: bool,
}

#[derive(Debug)]
struct ChoiceStack<'s> {
    content: Vec<Vec<&'s String>>,
}

impl <'s>ChoiceStack<'s> {
    pub fn new(input: &'s Vec<String>) -> ChoiceStack<'s> {
        let first_stack_frame = input.iter().map(|x| x).collect();

        ChoiceStack { content: vec![first_stack_frame] }
    }

    pub fn push(&mut self, frame: Vec<&'s String>) {
        self.content.push(frame);
    }

    pub fn pop(&mut self) {
        if self.content.len() > 1 {
            self.content.pop();
        }
    }

    pub fn peek(&self) -> &Vec<&'s String> {
        self.content.last().unwrap()
    }

    pub fn last_size(&self) -> usize {
        self.peek().len()
    }
}

impl<'s> Search<'s> {
    pub fn blank(config: &'s Configuration) -> Search<'s> {
        let query = config.initial_search.clone();

        let choice_stack = ChoiceStack::new(&config.choices);
        let result = Search::copy_items(&config.choices, config.visible_limit);

        Search::new(config, query, choice_stack, result, 0, false)
    }

    fn copy_items(input: &Vec<String>, size: usize) -> Vec<String> {
        input.iter().take(size).map(|x| x.clone() ).collect()
    }

    fn new(config: &'s Configuration, query: String, choice_stack: ChoiceStack<'s>, result: Vec<String>, index: usize, done: bool) -> Search<'s> {
        Search { config: config,
                 current: index,
                 query: query,
                 result: result,
                 choice_stack: choice_stack,
                 done: done}
    }

    pub fn is_done(&self) -> bool {
        self.done
    }

    pub fn done(self) -> Search<'s> {
        Search::new(self.config, self.query, self.choice_stack, self.result, self.current, true)
    }

    pub fn selection(&self) -> Option<String> {
        if self.result.len() > 0 {
            Some(self.result[self.current].to_string())
        } else {
            None
        }
    }

    fn new_for_index(self, index: usize) -> Search<'s> {
        Search::new(self.config, self.query, self.choice_stack, self.result, index, self.done)
    }

    pub fn iter_matches<F: FnMut(&'s String, f32)>(query: &str, choices: &Vec<&'s String>, mut f: F) {
        let lower_query = query.to_ascii_lowercase();

        for choice in choices.iter() {
            let lower_choice = choice.to_ascii_lowercase();

            match Score::score(&lower_choice, &lower_query) {
                0.0     => continue,
                quality => f(choice, quality),
            };
        }
    }

    pub fn down(self) -> Search<'s> {
        let next_index = self.next_index();
        self.new_for_index(next_index)
    }

    pub fn up(self) -> Search<'s> {
        let next_index = self.prev_index();
        self.new_for_index(next_index)
    }

    pub fn append_to_search(mut self, input: &str) -> Search<'s> {
        let mut new_query = self.query.clone();
        new_query.push_str(input.as_slice());

        let mut results = SortedResultSet::new(self.config.visible_limit);
        let mut filtered_choices: Vec<&String> = Vec::new();
        Search::iter_matches(new_query.as_slice(), &self.choice_stack.peek(),
                        |match_str, quality| {
                                               results.push(match_str, quality);
                                               filtered_choices.push(match_str)
                                             });

        self.choice_stack.push(filtered_choices);
        Search::new(self.config, new_query, self.choice_stack, results.as_sorted_vec(), 0, self.done)
    }

    pub fn backspace(mut self) -> Search<'s> {
        let mut new_query = self.query.clone();
        new_query.pop();

        self.choice_stack.pop();

        let mut results = SortedResultSet::new(self.config.visible_limit);
        Search::iter_matches(new_query.as_slice(), &self.choice_stack.peek(), |match_str, quality| results.push(match_str, quality) );

        Search::new(self.config, new_query, self.choice_stack, results.as_sorted_vec(), 0, self.done)
    }

    fn next_index(&self) -> usize {
        let next_index = self.current + 1;

        if next_index >= self.actual_limit() {
            0
        } else {
            next_index
        }
    }

    fn prev_index(&self) -> usize {
        if self.current == 0 {
            self.actual_limit() - 1
        } else {
            self.current - 1
        }
    }

    fn actual_limit(&self) -> usize {
        min(self.config.visible_limit, self.num_matches())
    }

    pub fn num_matches(&self) -> usize {
        self.choice_stack.last_size()
    }
}

#[cfg(test)]
mod tests {
    use configuration::Configuration;
    use super::*;

    fn one_two_three() -> Vec<String> {
        vec!["one".to_string(),
             "two".to_string(),
             "three".to_string()]
    }

    fn input_times(n: usize) ->Vec<String> {
        let mut result: Vec<String> = Vec::new();
        for thing in one_two_three().iter().cycle().take(n) {
            result.push(thing.clone());
        }
        result
    }

    fn basic_configuration() -> Configuration {
        let input = one_two_three();
        Configuration::from_inputs(input, None, None)
    }

    #[test]
    fn it_selects_the_first_choice_by_default() {
        let config = basic_configuration();
        let search = Search::blank(&config);

        assert_eq!(search.selection(), Some("one".to_string()));
    }

    #[test]
    fn it_selets_the_second_when_down_is_called() {
        let config = basic_configuration();
        let search = Search::blank(&config);

        assert_eq!(search.down().selection(), Some("two".to_string()));
    }

    #[test]
    fn it_loop_around_when_reaching_end_of_list() {
        let config = basic_configuration();
        let search = Search::blank(&config);

        assert_eq!(search.down().down().down().down().selection(), Some("two".to_string()));
    }

    #[test]
    fn it_loop_around_when_reaching_top_of_list() {
        let config = basic_configuration();
        let search = Search::blank(&config);

        assert_eq!(search.up().up().selection(), Some("two".to_string()));
    }

    #[test]
    fn it_loop_around_when_reaching_visible_limit() {
        let config = basic_configuration();
        let search = Search::blank(&config);

        assert_eq!(search.down().down().down().down().selection(), Some("two".to_string()));
    }

    #[test]
    fn it_moves_down_the_filtered_search_results() {
        let config = basic_configuration();
        let search = Search::blank(&config);

        assert_eq!(search.append_to_search("t").down().selection(), Some("three".to_string()));
    }

    #[test]
    fn it_moves_down_the_filtered_search_results_twice() {
        let config = basic_configuration();
        let search = Search::blank(&config);

        assert_eq!(search.append_to_search("t").append_to_search("w").selection(), Some("two".to_string()));
    }

    #[test]
    fn it_handles_not_matching_anything() {
        let config = basic_configuration();
        let search = Search::blank(&config);

        assert_eq!(search.append_to_search("x").selection(), None);
    }

    #[test]
    fn up_match_nothing_after_filtering_all_out() {
        let config = basic_configuration();
        let search = Search::blank(&config).append_to_search("x");

        assert_eq!(search.up().selection(), None);
    }

    #[test]
    fn down_match_nothing_after_filtering_all_out() {
        let config = basic_configuration();
        let search = Search::blank(&config).append_to_search("x");

        assert_eq!(search.down().selection(), None);
    }

    #[test]
    fn backspaces_over_characters() {
        let config = basic_configuration();
        let search = Search::blank(&config).append_to_search("e");

        assert_eq!(search.query, "e");
        assert_eq!(search.backspace().query, "");
    }

    #[test]
    fn resets_the_index_when_removing_char_from_search() {
        let config = basic_configuration();
        let search = Search::blank(&config).append_to_search("e");

        assert_eq!(search.down().backspace().current, 0);
    }

    #[test]
    fn resets_the_index_when_adding_char_to_seach() {
        let config = basic_configuration();
        let search = Search::blank(&config).down();

        assert_eq!(search.append_to_search("o").current, 0);
    }

    #[test]
    fn previous_results_appear_after_backspace() {
        let config = basic_configuration();
        let search = Search::blank(&config).append_to_search("t");

        assert_eq!(search.backspace().num_matches(), 3);
    }

    #[test]
    fn initial_search_is_not_done() {
        let config = basic_configuration();
        let search = Search::blank(&config);

        assert!(!search.is_done());
    }

    #[test]
    fn backspace_over_initial_doesnt_crash() {
        let config = basic_configuration();
        let search = Search::blank(&config);

        search.backspace();
    }

    #[test]
    fn done_search_is_done() {
        let config = basic_configuration();
        let search = Search::blank(&config).done();

        assert!(search.is_done());
    }

    #[test]
    fn done_search_has_selection() {
        let config = basic_configuration();
        let search = Search::blank(&config).done();

        assert_eq!(search.selection(), Some("one".to_string()));
    }

    #[test]
    fn loop_around_when_reaching_bottom_of_choices() {
        let config = basic_configuration();
        let search = Search::blank(&config).append_to_search("n").down();

        assert_eq!(search.selection(), Some("one".to_string()));
    }

    #[test]
    fn loop_around_when_reaching_top_of_choices() {
        let config = basic_configuration();
        let search = Search::blank(&config).append_to_search("n").up();

        assert_eq!(search.selection(), Some("one".to_string()));
    }

    #[test]
    fn search_is_case_insensitive() {
        let config = basic_configuration();
        let search = Search::blank(&config).append_to_search("T");

        assert_eq!(search.num_matches(), 2);
    }

    #[test]
    fn uses_configs_visible_limit_as_result_size() {
        let input = input_times(30);

        let mut bar = Vec::new();
        for f in input.iter() {
            bar.push((*f).clone());
        }

        let config = Configuration::from_inputs(bar, None, Some(20));
        let search = Search::blank(&config).append_to_search("T");

        assert_eq!(search.num_matches(), 20);
    }
}
<|endoftext|>


<commit_before>use std::net::{SocketAddrV4, UdpSocket};
use std::str;

use common::{messages, parsing, SearchOptions};
use errors::SearchError;
use gateway::Gateway;

/// Search gateway, using the given `SearchOptions`.
///
/// The default `SearchOptions` should suffice in most cases.
/// It can be created with `Default::default()` or `SearchOptions::default()`.
///
/// # Example
/// ```no_run
/// use igd::{search_gateway, SearchOptions, Result};
///
/// fn main() -> Result {
///     let gateway = search_gateway(Default::default())?;
///     let ip = gateway.get_external_ip()?;
///     println!("External IP address: {}", ip);
///     Ok(())
/// }
/// ```
pub fn search_gateway(options: SearchOptions) -> Result<Gateway, SearchError> {
    let socket = UdpSocket::bind(options.bind_addr)?;
    socket.set_read_timeout(options.timeout)?;

    socket.send_to(messages::SEARCH_REQUEST.as_bytes(), options.broadcast_address)?;

    loop {
        let mut buf = [0u8; 1500];
        let (read, _) = socket.recv_from(&mut buf)?;
        let text = str::from_utf8(&buf[..read])?;

        let location = parsing::parse_search_result(text)?;

        if let Ok(control_url) = get_control_url(&location) {
            // Defaults to using the first control url.
            if control_url.len() > 0{
                return Ok(Gateway {
                addr: location.0,
                control_url: control_url[0].clone(),
                });
            } else {
                return Err(SearchError::InvalidResponse);
            }
        }
    }
}

pub fn get_control_urls(options: SearchOptions) -> Result<Vec<String>, SearchError> {
    let socket = UdpSocket::bind(options.bind_addr)?;
    socket.set_read_timeout(options.timeout)?;

    socket.send_to(messages::SEARCH_REQUEST.as_bytes(), options.broadcast_address)?;
    loop {
        let mut buf = [0u8; 1500];
        let (read, _) = socket.recv_from(&mut buf)?;
        let text = str::from_utf8(&buf[..read])?;

        let location = parsing::parse_search_result(text)?;
        if let Ok(control_url) = get_control_url(&location) {
            if control_url.len() > 0{
                return Ok(control_url);
            } else {
                return Err(SearchError::InvalidResponse);
            }
        }
    }
}

/*
    A bit of an ugly temporary workaround, basically the idea is to use get_control_urls() and then call
    search_gateway() with a valid control url. Allows the user to select the interface to use.
*/
pub fn get_gateway_with_control_url(options: SearchOptions, url: &str) -> Result<Gateway, SearchError>{
    let mut gateway = search_gateway(options)?;
    gateway.control_url = String::from(url);
    Ok(gateway)
}

fn get_control_url(location: &(SocketAddrV4, String)) -> Result<Vec<String>, SearchError> {
    let url = format!("http://{}:{}{}", location.0.ip(), location.0.port(), location.1);
    let response = attohttpc::get(&url).send()?;
    let res = parsing::parse_control_url(&response.bytes()?[..]);
    res
}

#[test]
fn test_get_control_urls(){
    // This test will fail if upnp is disabled on the default interface ( default gateway )
    assert_eq!(get_control_urls(SearchOptions::default()).unwrap().len() > 0, true);
}<commit_msg>Removed test that would break travis<commit_after>use std::net::{SocketAddrV4, UdpSocket};
use std::str;

use common::{messages, parsing, SearchOptions};
use errors::SearchError;
use gateway::Gateway;

/// Search gateway, using the given `SearchOptions`.
///
/// The default `SearchOptions` should suffice in most cases.
/// It can be created with `Default::default()` or `SearchOptions::default()`.
///
/// # Example
/// ```no_run
/// use igd::{search_gateway, SearchOptions, Result};
///
/// fn main() -> Result {
///     let gateway = search_gateway(Default::default())?;
///     let ip = gateway.get_external_ip()?;
///     println!("External IP address: {}", ip);
///     Ok(())
/// }
/// ```
pub fn search_gateway(options: SearchOptions) -> Result<Gateway, SearchError> {
    let socket = UdpSocket::bind(options.bind_addr)?;
    socket.set_read_timeout(options.timeout)?;

    socket.send_to(messages::SEARCH_REQUEST.as_bytes(), options.broadcast_address)?;

    loop {
        let mut buf = [0u8; 1500];
        let (read, _) = socket.recv_from(&mut buf)?;
        let text = str::from_utf8(&buf[..read])?;

        let location = parsing::parse_search_result(text)?;

        if let Ok(control_url) = get_control_url(&location) {
            // Defaults to using the first control url.
            if control_url.len() > 0{
                return Ok(Gateway {
                addr: location.0,
                control_url: control_url[0].clone(),
                });
            } else {
                return Err(SearchError::InvalidResponse);
            }
        }
    }
}

pub fn get_control_urls(options: SearchOptions) -> Result<Vec<String>, SearchError> {
    let socket = UdpSocket::bind(options.bind_addr)?;
    socket.set_read_timeout(options.timeout)?;

    socket.send_to(messages::SEARCH_REQUEST.as_bytes(), options.broadcast_address)?;
    loop {
        let mut buf = [0u8; 1500];
        let (read, _) = socket.recv_from(&mut buf)?;
        let text = str::from_utf8(&buf[..read])?;

        let location = parsing::parse_search_result(text)?;
        if let Ok(control_url) = get_control_url(&location) {
            if control_url.len() > 0{
                return Ok(control_url);
            } else {
                return Err(SearchError::InvalidResponse);
            }
        }
    }
}

/*
    A bit of an ugly temporary workaround, basically the idea is to use get_control_urls() and then call
    search_gateway() with a valid control url. Allows the user to select the interface to use.
*/
pub fn get_gateway_with_control_url(options: SearchOptions, url: &str) -> Result<Gateway, SearchError>{
    let mut gateway = search_gateway(options)?;
    gateway.control_url = String::from(url);
    Ok(gateway)
}

fn get_control_url(location: &(SocketAddrV4, String)) -> Result<Vec<String>, SearchError> {
    let url = format!("http://{}:{}{}", location.0.ip(), location.0.port(), location.1);
    let response = attohttpc::get(&url).send()?;
    let res = parsing::parse_control_url(&response.bytes()?[..]);
    res
}

// #[test]
// fn test_get_control_urls(){
//     // This test will fail if upnp is disabled on the default interface ( default gateway )
//     assert_eq!(get_control_urls(SearchOptions::default()).unwrap().len() > 0, true);
// }<|endoftext|>


<commit_before>use std::net::TcpListener;
use std::net::TcpStream;
use std::fmt;
use std::io::Cursor;
use std::io::Read;

use solicit::server::SimpleServer;
use solicit::http::server::StreamFactory;
use solicit::http::server::ServerConnection;
use solicit::http::server::ServerSession;
use solicit::http::HttpScheme;
use solicit::http::StreamId;
use solicit::http::Header;
use solicit::http::OwnedHeader;
use solicit::http::HttpResult;
use solicit::http::priority::SimplePrioritizer;
use solicit::http::connection::HttpConnection;
use solicit::http::connection::EndStream;
use solicit::http::connection::SendStatus;
use solicit::http::connection::SendFrame;
use solicit::http::session::SessionState;
use solicit::http::session::DefaultSessionState;
use solicit::http::session::DefaultStream;
use solicit::http::session::Stream;
use solicit::http::session::Server;
use solicit::http::session::StreamState;
use solicit::http::session::StreamDataChunk;
use solicit::http::session::StreamDataError;
use solicit::http::transport::TransportStream;
use solicit::http::transport::TransportReceiveFrame;

use grpc;
use method::ServerServiceDefinition;

struct BsDebug<'a>(&'a [u8]);

impl<'a> fmt::Debug for BsDebug<'a> {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        try!(write!(fmt, "b\""));
        let u8a: &[u8] = self.0;
        for &c in u8a {
            // ASCII printable
            if c >= 0x20 && c < 0x7f {
                try!(write!(fmt, "{}", c as char));
            } else {
                try!(write!(fmt, "\\x{:02x}", c));
            }
        }
        try!(write!(fmt, "\""));
    	Ok(())
    }
}

struct HeaderDebug<'a>(&'a Header<'a, 'a>);

impl<'a> fmt::Debug for HeaderDebug<'a> {
	fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
	    write!(fmt, "Header {{ name: {:?}, value: {:?} }}",
	    	BsDebug(self.0.name()), BsDebug(self.0.value()))
	}
}

struct GrpcStream {
    stream_id: Option<StreamId>,
    headers: Option<Vec<Header<'static, 'static>>>,
    body: Vec<u8>,
    state: StreamState,
    data: Option<Cursor<Vec<u8>>>,

    buf: Vec<u8>,
    resp: Vec<u8>,
    service_definition: ServerServiceDefinition,
    path: String,
}

impl GrpcStream {
    fn with_id(stream_id: StreamId) -> Self {
        println!("new stream {}", stream_id);
        GrpcStream {
            stream_id: Some(stream_id),
            headers: None,
            body: Vec::new(),
            state: StreamState::Open,
            data: None,
            buf: Vec::new(),
            resp: Vec::new(),
            service_definition: ServerServiceDefinition::new(Vec::new()),
            path: String::new(),
        }
    }

    fn process_buf(&mut self) {
        loop {
            let (r, pos) = match grpc::parse_frame(&self.buf) {
                Some((frame, pos)) => {
                    let r = self.service_definition.handle_method(&self.path, frame);
                    (r, pos)
                }
                None => return,
            };

            self.buf.drain(..pos);
            self.resp.extend(r);
        }
    }
}

impl Stream for GrpcStream {
    fn set_headers(&mut self, headers: Vec<Header>) {
        for h in &headers {
            if h.name() == b":path" {
                self.path = String::from_utf8(h.value().to_owned()).unwrap();
            }
        }
        println!("headers: {:?}", headers.iter().map(|h| HeaderDebug(h)).collect::<Vec<_>>());
        self.headers = Some(headers.into_iter().map(|h| {
            let owned: OwnedHeader = h.into();
            owned.into()
        }).collect());
    }

    fn new_data_chunk(&mut self, data: &[u8]) {
        println!("hooray! data: {:?}", data);
        self.buf.extend(data);
        self.process_buf();
        println!("{:?}", grpc::parse_frame(data));
        self.body.extend(data.to_vec().into_iter());
    }

    fn set_state(&mut self, state: StreamState) {
        println!("set_state: {:?}", state);
        self.state = state;
        println!("s: {:?}", BsDebug(&self.body));
    }

    fn get_data_chunk(&mut self, buf: &mut [u8]) -> Result<StreamDataChunk, StreamDataError> {
        println!("get_data_chunk");
        if self.is_closed_local() {
            return Err(StreamDataError::Closed);
        }
        let chunk = match self.data.as_mut() {
            // No data associated to the stream, but it's open => nothing available for writing
            None => StreamDataChunk::Unavailable,
            Some(d) =>  {
                // For the `Vec`-backed reader, this should never fail, so unwrapping is
                // fine.
                let read = d.read(buf).unwrap();
                if (d.position() as usize) == d.get_ref().len() {
                    StreamDataChunk::Last(read)
                } else {
                    StreamDataChunk::Chunk(read)
                }
            }
        };
        // Transition the stream state to locally closed if we've extracted the final data chunk.
        match chunk {
            StreamDataChunk::Last(_) => self.close_local(),
            _ => {},
        };

        Ok(chunk)
    }

    fn state(&self) -> StreamState {
        self.state
    }
}

/*
struct GrpcSessionState {
    default_state: DefaultSessionState,
}

impl GrpcSessionState {
    fn new() -> Self {
        GrpcSessionState {
            default_state: DefaultSessionState::new(),
        }
    }
}
*/

struct GrpcStreamFactory;

impl StreamFactory for GrpcStreamFactory {
	type Stream = GrpcStream;

	fn create(&mut self, id: StreamId) -> GrpcStream {
		GrpcStream::with_id(id)
	}
}

struct GrpcServerConnection {
    conn: HttpConnection,
    factory: GrpcStreamFactory,
    state: DefaultSessionState<Server, GrpcStream>,
    receiver: TcpStream,
    sender: TcpStream,
}

impl GrpcServerConnection {
    fn new(mut stream: TcpStream) -> GrpcServerConnection {
        let mut preface = [0; 24];

        (&mut stream as &mut Read).read_exact(&mut preface).unwrap();
        if &preface != b"PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n" {
            panic!();
        }

        let conn = HttpConnection::new(HttpScheme::Http);

		let mut xx: TcpStream = stream.try_split().unwrap();
        let mut r = GrpcServerConnection {
            conn: conn,
            state: DefaultSessionState::<Server, _>::new(),
            receiver: xx,
            sender: stream,
            factory: GrpcStreamFactory,
        };

        //r.server_conn.init().unwrap();
        r
    }

    fn handle_requests(&mut self) -> HttpResult<Vec<(StreamId, Vec<u8>)>> {
        Ok(self.state.iter().flat_map(|(&id, s)| {
            if s.resp.is_empty() {
                None
            } else {
                Some((id, s.resp.clone()))
            }
        }).collect())
    }

    fn prepare_responses(&mut self, responses: Vec<(StreamId, Vec<u8>)>) -> HttpResult<()> {
        for r in responses {
            try!(self.conn.sender(&mut self.sender).send_headers(
                Vec::new(),
                r.0,
                EndStream::No));
            let mut stream = self.state.get_stream_mut(r.0).unwrap();
            stream.data = Some(Cursor::new(r.1));
        }
        Ok(())
    }

    fn send_next_data(&mut self) -> HttpResult<SendStatus> {
        const MAX_CHUNK_SIZE: usize = 8 * 1024;
        let mut buf = [0; MAX_CHUNK_SIZE];

        // TODO: Additionally account for the flow control windows.
        let mut prioritizer = SimplePrioritizer::new(&mut self.state, &mut buf);

        self.conn.sender(&mut self.sender).send_next_data(&mut prioritizer)
    }

    fn flush_streams(&mut self) -> HttpResult<()> {
        while let SendStatus::Sent = try!(self.send_next_data()) {}

        Ok(())
    }

    fn reap_streams(&mut self) {
        // Moves the streams out of the state and then drops them
        let closed = self.state.get_closed();
        println!("closed: {:?}", closed.iter().map(|s| s.stream_id).collect::<Vec<_>>());
    }

    pub fn handle_next_frame(&mut self) -> HttpResult<()> {
        let mut rx = TransportReceiveFrame::new(&mut self.receiver);
        let mut session = ServerSession::new(&mut self.state, &mut self.factory, &mut self.sender);
        self.conn.handle_next_frame(&mut rx, &mut session)
    }

    fn handle_next(&mut self) -> HttpResult<()> {
        try!(self.handle_next_frame());

        let responses = try!(self.handle_requests());

		try!(self.prepare_responses(responses));

        try!(self.flush_streams());
        self.reap_streams();

        Ok(())
    }

    fn run(&mut self) {
        loop {
            let r = self.handle_next();
            match r {
                e @ Err(..) => {
                    println!("{:?}", e);
                    return;
                }
                _ => {},
            }
        }
    }
}

pub struct GrpcServer {
    listener: TcpListener,
}

impl GrpcServer {
    pub fn new() -> GrpcServer {
        GrpcServer {
            listener: TcpListener::bind("127.0.0.1:50051").unwrap(),
        }
    }

    pub fn run(&mut self) {
        for mut stream in self.listener.incoming().map(|s| s.unwrap()) {
            println!("client connected!");
            GrpcServerConnection::new(stream).run();
        }
    }
}

<commit_msg>backup<commit_after>use std::net::TcpListener;
use std::net::TcpStream;
use std::fmt;
use std::io::Cursor;
use std::io::Read;
use std::convert::From;

use solicit::server::SimpleServer;
use solicit::http::server::StreamFactory;
use solicit::http::server::ServerConnection;
use solicit::http::server::ServerSession;
use solicit::http::HttpScheme;
use solicit::http::StreamId;
use solicit::http::Header;
use solicit::http::HeaderPart;
use solicit::http::OwnedHeader;
use solicit::http::HttpResult;
use solicit::http::priority::SimplePrioritizer;
use solicit::http::connection::HttpConnection;
use solicit::http::connection::EndStream;
use solicit::http::connection::SendStatus;
use solicit::http::connection::SendFrame;
use solicit::http::connection::DataChunk;
use solicit::http::session::SessionState;
use solicit::http::session::DefaultSessionState;
use solicit::http::session::DefaultStream;
use solicit::http::session::Stream;
use solicit::http::session::Server;
use solicit::http::session::StreamState;
use solicit::http::session::StreamDataChunk;
use solicit::http::session::StreamDataError;
use solicit::http::transport::TransportStream;
use solicit::http::transport::TransportReceiveFrame;

use grpc;
use method::ServerServiceDefinition;

struct BsDebug<'a>(&'a [u8]);

impl<'a> fmt::Debug for BsDebug<'a> {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        try!(write!(fmt, "b\""));
        let u8a: &[u8] = self.0;
        for &c in u8a {
            // ASCII printable
            if c >= 0x20 && c < 0x7f {
                try!(write!(fmt, "{}", c as char));
            } else {
                try!(write!(fmt, "\\x{:02x}", c));
            }
        }
        try!(write!(fmt, "\""));
    	Ok(())
    }
}

struct HeaderDebug<'a>(&'a Header<'a, 'a>);

impl<'a> fmt::Debug for HeaderDebug<'a> {
	fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
	    write!(fmt, "Header {{ name: {:?}, value: {:?} }}",
	    	BsDebug(self.0.name()), BsDebug(self.0.value()))
	}
}

struct GrpcStream {
    stream_id: Option<StreamId>,
    headers: Option<Vec<Header<'static, 'static>>>,
    body: Vec<u8>,
    state: StreamState,
    data: Option<Cursor<Vec<u8>>>,

    buf: Vec<u8>,
    resp: Vec<u8>,
    service_definition: ServerServiceDefinition,
    path: String,
}

impl GrpcStream {
    fn with_id(stream_id: StreamId) -> Self {
        println!("new stream {}", stream_id);
        GrpcStream {
            stream_id: Some(stream_id),
            headers: None,
            body: Vec::new(),
            state: StreamState::Open,
            data: None,
            buf: Vec::new(),
            resp: Vec::new(),
            service_definition: ServerServiceDefinition::new(Vec::new()),
            path: String::new(),
        }
    }

    fn process_buf(&mut self) {
        loop {
            let (r, pos) = match grpc::parse_frame(&self.buf) {
                Some((frame, pos)) => {
                    let r = self.service_definition.handle_method(&self.path, frame);
                    (r, pos)
                }
                None => return,
            };

            self.buf.drain(..pos);
            self.resp.extend(r);
        }
    }
}

impl Stream for GrpcStream {
    fn set_headers(&mut self, headers: Vec<Header>) {
        for h in &headers {
            if h.name() == b":path" {
                self.path = String::from_utf8(h.value().to_owned()).unwrap();
            }
        }
        println!("headers: {:?}", headers.iter().map(|h| HeaderDebug(h)).collect::<Vec<_>>());
        self.headers = Some(headers.into_iter().map(|h| {
            let owned: OwnedHeader = h.into();
            owned.into()
        }).collect());
    }

    fn new_data_chunk(&mut self, data: &[u8]) {
        println!("hooray! data: {:?}", data);
        self.buf.extend(data);
        self.process_buf();
        println!("{:?}", grpc::parse_frame(data));
        self.body.extend(data.to_vec().into_iter());
    }

    fn set_state(&mut self, state: StreamState) {
        println!("set_state: {:?}", state);
        self.state = state;
        println!("s: {:?}", BsDebug(&self.body));
    }

    fn get_data_chunk(&mut self, buf: &mut [u8]) -> Result<StreamDataChunk, StreamDataError> {
        println!("get_data_chunk");
        if self.is_closed_local() {
            return Err(StreamDataError::Closed);
        }
        let chunk = match self.data.as_mut() {
            // No data associated to the stream, but it's open => nothing available for writing
            None => StreamDataChunk::Unavailable,
            Some(d) =>  {
                // For the `Vec`-backed reader, this should never fail, so unwrapping is
                // fine.
                let read = d.read(buf).unwrap();
                if (d.position() as usize) == d.get_ref().len() {
                    StreamDataChunk::Last(read)
                } else {
                    StreamDataChunk::Chunk(read)
                }
            }
        };
        // Transition the stream state to locally closed if we've extracted the final data chunk.
        match chunk {
            StreamDataChunk::Last(_) => self.close_local(),
            _ => {},
        };

        Ok(chunk)
    }

    fn state(&self) -> StreamState {
        self.state
    }
}

/*
struct GrpcSessionState {
    default_state: DefaultSessionState,
}

impl GrpcSessionState {
    fn new() -> Self {
        GrpcSessionState {
            default_state: DefaultSessionState::new(),
        }
    }
}
*/

struct GrpcStreamFactory;

impl StreamFactory for GrpcStreamFactory {
	type Stream = GrpcStream;

	fn create(&mut self, id: StreamId) -> GrpcStream {
		GrpcStream::with_id(id)
	}
}

struct GrpcServerConnection {
    conn: HttpConnection,
    factory: GrpcStreamFactory,
    state: DefaultSessionState<Server, GrpcStream>,
    receiver: TcpStream,
    sender: TcpStream,
}

impl GrpcServerConnection {
    fn new(mut stream: TcpStream) -> GrpcServerConnection {
        let mut preface = [0; 24];

        (&mut stream as &mut Read).read_exact(&mut preface).unwrap();
        if &preface != b"PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n" {
            panic!();
        }

        let conn = HttpConnection::new(HttpScheme::Http);

		let mut xx: TcpStream = stream.try_split().unwrap();
        let mut r = GrpcServerConnection {
            conn: conn,
            state: DefaultSessionState::<Server, _>::new(),
            receiver: xx,
            sender: stream,
            factory: GrpcStreamFactory,
        };

        //r.server_conn.init().unwrap();
        r
    }

    fn handle_requests(&mut self) -> HttpResult<Vec<(StreamId, Vec<u8>)>> {
        Ok(self.state.iter().flat_map(|(&id, s)| {
            if s.resp.is_empty() {
                None
            } else {
                Some((id, s.resp.clone()))
            }
        }).collect())
    }

    fn prepare_responses(&mut self, responses: Vec<(StreamId, Vec<u8>)>) -> HttpResult<()> {
        for r in responses {
            try!(self.conn.sender(&mut self.sender).send_headers(
                vec![
                	Header::new(b":status", b"200"),
                	Header::new(&b"content-type"[..], &b"application/grpc"[..]),
                ],
                r.0,
                EndStream::No));

            try!(self.conn.sender(&mut self.sender).send_data(DataChunk::new_borrowed(&r.1[..], r.0, EndStream::No)));

            try!(self.conn.sender(&mut self.sender).send_headers(
                vec![
                	Header::new(&b"grpc-status"[..], b"0"),
                ],
                r.0,
                EndStream::Yes));
        }
        Ok(())
    }

    fn send_next_data(&mut self) -> HttpResult<SendStatus> {
        const MAX_CHUNK_SIZE: usize = 8 * 1024;
        let mut buf = [0; MAX_CHUNK_SIZE];

        // TODO: Additionally account for the flow control windows.
        let mut prioritizer = SimplePrioritizer::new(&mut self.state, &mut buf);

        self.conn.sender(&mut self.sender).send_next_data(&mut prioritizer)
    }

    fn flush_streams(&mut self) -> HttpResult<()> {
        while let SendStatus::Sent = try!(self.send_next_data()) {}

        Ok(())
    }

    fn reap_streams(&mut self) {
        // Moves the streams out of the state and then drops them
        let closed = self.state.get_closed();
        println!("closed: {:?}", closed.iter().map(|s| s.stream_id).collect::<Vec<_>>());
    }

    pub fn handle_next_frame(&mut self) -> HttpResult<()> {
        let mut rx = TransportReceiveFrame::new(&mut self.receiver);
        let mut session = ServerSession::new(&mut self.state, &mut self.factory, &mut self.sender);
        self.conn.handle_next_frame(&mut rx, &mut session)
    }

    fn handle_next(&mut self) -> HttpResult<()> {
        try!(self.handle_next_frame());

        let responses = try!(self.handle_requests());

		try!(self.prepare_responses(responses));

        try!(self.flush_streams());
        self.reap_streams();

        Ok(())
    }

    fn run(&mut self) {
        loop {
            let r = self.handle_next();
            match r {
                e @ Err(..) => {
                    println!("{:?}", e);
                    return;
                }
                _ => {},
            }
        }
    }
}

pub struct GrpcServer {
    listener: TcpListener,
}

impl GrpcServer {
    pub fn new() -> GrpcServer {
        GrpcServer {
            listener: TcpListener::bind("127.0.0.1:50051").unwrap(),
        }
    }

    pub fn run(&mut self) {
        for mut stream in self.listener.incoming().map(|s| s.unwrap()) {
            println!("client connected!");
            GrpcServerConnection::new(stream).run();
        }
    }
}

<|endoftext|>


<commit_before>extern crate rustc;
use rustc::util::sha2::{Sha256, Digest};

#[cfg(not(test))]
fn main() {
    println!("{}", sha_256("Rosetta code"));
}

fn sha_256(input: &str) -> String {
    let mut sh = box Sha256::new();
    sh.input_str(input);
    sh.result_str()
}

#[test]
fn test_rosetta() {
    assert_eq!(sha_256("Rosetta code"),
    "764faf5c61ac315f1497f9dfa542713965b785e5cc2f707d6468d7d1124cdfcf"
    .to_string());
}<commit_msg>removed trailing whitespace<commit_after>extern crate rustc;
use rustc::util::sha2::{Sha256, Digest};

#[cfg(not(test))]
fn main() {
    println!("{}", sha_256("Rosetta code"));
}

fn sha_256(input: &str) -> String {
    let mut sh = box Sha256::new();
    sh.input_str(input);
    sh.result_str()
}

#[test]
fn test_rosetta() {
    assert_eq!(sha_256("Rosetta code"),
    "764faf5c61ac315f1497f9dfa542713965b785e5cc2f707d6468d7d1124cdfcf"
    .to_string());
}<|endoftext|>