<commit_before>pub mod game_over_state;
pub mod intro_state;
pub mod menu_state;
pub mod play_state;
pub mod shared;

use crate::states::intro_state::IntroState;
use ggez::event::{Axis, Button, EventHandler, Keycode, Mod, MouseButton, MouseState};
use ggez::{audio, graphics, timer, Context, GameResult};
use std::collections::HashMap;
use std::time::Duration;

/// A global structure that stores all game assets. This is passed down into a
/// state by the `StateManager`.
pub struct Assets {
    images: HashMap<String, graphics::Image>,
    font: HashMap<String, graphics::Font>,
    music: HashMap<String, audio::Source>,
    sfx: HashMap<String, audio::Source>,
}

impl Assets {
    pub fn new() -> Self {
        Self {
            images: HashMap::new(),
            font: HashMap::new(),
            music: HashMap::new(),
            sfx: HashMap::new(),
        }
    }

    /// Add an image asset to the asset manager.
    pub fn add_image(&mut self, name: &str, image: graphics::Image) -> GameResult<()> {
        self.images.insert(name.to_string(), image);
        Ok(())
    }

    /// Returns an image with the key `name` from the asset manager.
    pub fn get_image(&self, name: &str) -> GameResult<&graphics::Image> {
        let img = self.images.get(name);
        Ok(img.unwrap())
    }

    /// Add a font asset to the asset manager.
    pub fn add_font(&mut self, name: &str, font: graphics::Font) -> GameResult<()> {
        self.font.insert(name.to_string(), font);
        Ok(())
    }

    /// Returns a font with the key `name` from the asset manager.
    pub fn get_font(&self, name: &str) -> GameResult<&graphics::Font> {
        let font = self.font.get(name);
        Ok(font.unwrap())
    }

    /// Add an audio asset meant for music to the asset manager.
    pub fn add_music(&mut self, name: &str, audio: audio::Source) -> GameResult<()> {
        self.music.insert(name.to_string(), audio);
        Ok(())
    }

    /// Returns an audio asset meant for music with the key `name` from the
    /// asset manager.
    pub fn get_music(&self, name: &str) -> GameResult<&audio::Source> {
        let audio = self.music.get(name);
        Ok(audio.unwrap())
    }

    /// Returns the number of audio assets meant for music playback currently in
    /// the asset manager.
    pub fn get_music_count(&self) -> u32 {
        self.music.len() as u32
    }

    /// Add an audio asset meant for sound effects to the asset manager.
    pub fn add_sfx(&mut self, name: &str, audio: audio::Source) -> GameResult<()> {
        self.sfx.insert(name.to_string(), audio);
        Ok(())
    }

    /// Returns an audio asset meant for sound effects with the key `name` from
    /// the asset manager.
    pub fn get_sfx(&self, name: &str) -> GameResult<&audio::Source> {
        let audio = self.sfx.get(name);
        Ok(audio.unwrap())
    }
}

/// Describes a potential state transition. `EventHandler::update` returns a
/// `GameResult<Transition>` that can be used to request a state change from the
/// state manager.
pub enum Transition {
    /// Don't change states.
    None,
    /// Push another state ontop of the current state.
    Push(Box<State>),
    /// Remove all states from the stack and then push a new one.
    Swap(Box<State>),
    /// Remove the state currently at the top of the stack.
    Pop,
    /// Clear the stack which in turn quits the game.
    Drain,
}

pub trait State {
    fn update(
        &mut self,
        ctx: &mut Context,
        assets: &Assets,
        dt: Duration,
    ) -> GameResult<Transition>;
    fn draw(&mut self, ctx: &mut Context, assets: &Assets) -> GameResult<()>;
    fn key_down_event(&mut self, _keycode: Keycode, _keymod: Mod, _repeat: bool, _assets: &Assets) {
    }
    fn key_up_event(&mut self, _keycode: Keycode, _keymod: Mod, _repeat: bool) {}
    fn mouse_button_down_event(&mut self, _button: MouseButton, _x: i32, _y: i32) {}
    fn mouse_button_up_event(&mut self, _button: MouseButton, _x: i32, _y: i32) {}
    fn mouse_motion_event(&mut self, _state: MouseState, _x: i32, _y: i32, _xrel: i32, _yrel: i32) {
    }
    fn mouse_wheel_event(&mut self, _x: i32, _y: i32) {}
    fn controller_button_down_event(&mut self, _btn: Button, _instance_id: i32, _assets: &Assets) {}
    fn controller_button_up_event(&mut self, _btn: Button, _instance_id: i32) {}
    fn controller_axis_event(&mut self, _axis: Axis, _value: i16, _instance_id: i32) {}
    fn focus_event(&mut self, _gained: bool) {}
    fn quit_event(&mut self) -> bool {
        false
    }
}

/// A `StateManager` will manage requests to push, pop or swap states on the
/// state stack. It owns the `Assets` struct and dictates whether the game
/// continues to run or not.
///
/// States must be put into a `Box` before being handed to the `StateManager`.
pub struct StateManager {
    assets: Assets,
    running: bool,
    states: Vec<Box<State>>,
}

impl StateManager {
    /// Create a new `StateManager` and initializes the first state.
    pub fn new(ctx: &mut Context) -> StateManager {
        let assets = StateManager::initialize_assets(ctx).unwrap();
        let state = Box::new(IntroState::new(ctx, &assets).unwrap());

        StateManager {
            running: true,
            states: vec![state],
            assets,
        }
    }

    fn initialize_assets(ctx: &mut Context) -> GameResult<Assets> {
        let mut assets = Assets::new();
        assets.add_image("block", graphics::Image::new(ctx, "/block.png")?)?;
        assets.add_image("menu_bg", graphics::Image::new(ctx, "/blackhole2.png")?)?;
        assets.add_image("game_bg", graphics::Image::new(ctx, "/space_bg.png")?)?;
        assets.add_font(
            "title",
            graphics::Font::new(ctx, "/DejaVuSansMono.ttf", 32)?,
        )?;
        assets.add_font(
            "title_shadow",
            graphics::Font::new(ctx, "/DejaVuSansMono.ttf", 33)?,
        )?;
        assets.add_font(
            "normal",
            graphics::Font::new(ctx, "/DejaVuSansMono.ttf", 18)?,
        )?;
        assets.add_font("ui", graphics::Font::new(ctx, "/DejaVuSansMono.ttf", 16)?)?;

        let mut play_0 = audio::Source::new(ctx, "/music/Track2.ogg")?;
        let mut play_1 = audio::Source::new(ctx, "/music/Track4.ogg")?;
        let mut menu = audio::Source::new(ctx, "/music/Track3.ogg")?;
        play_0.set_volume(0.4);
        play_1.set_volume(0.4);
        menu.set_volume(0.4);
        assets.add_music("play_0", play_0)?;
        assets.add_music("play_1", play_1)?;
        assets.add_music("menu", menu)?;

        let mut click = audio::Source::new(ctx, "/sfx/click.ogg")?;
        let mut gameover_win = audio::Source::new(ctx, "/sfx/gameover_win.ogg")?;
        let mut gameover_lose = audio::Source::new(ctx, "/sfx/gameover_lose.ogg")?;
        click.set_volume(0.5);
        gameover_win.set_volume(0.5);
        gameover_lose.set_volume(0.5);
        assets.add_sfx("click", click)?;
        assets.add_sfx("gameover_win", gameover_win)?;
        assets.add_sfx("gameover_lose", gameover_lose)?;

        Ok(assets)
    }

    /// Clears the state stack and sets `running` to false which quits the game.
    pub fn quit(&mut self) {
        self.states.clear();
        self.running = false
    }
}

impl StateManager {
    /// Calls the state transition handler depending on the `Transition` given
    /// as an argument.
    fn handle_transition(&mut self, transition: Transition) {
        match transition {
            Transition::None => (),
            Transition::Pop => self.pop(),
            Transition::Swap(state) => self.swap(state),
            Transition::Push(state) => self.push(state),
            Transition::Drain => self.drain(),
        }
    }

    /// Pops the state at the top of the stack. If the size of the state stack
    /// after this operation is `0`, the game quits.
    fn pop(&mut self) {
        self.states.pop();

        if self.states.is_empty() {
            self.quit();
        }
    }

    /// Pushes a state onto the state stack.
    fn push(&mut self, boxed_state: Box<State>) {
        self.states.push(boxed_state)
    }

    /// Removes all states from the state stack and then pushes the given
    /// `boxed_state` onto the stack.
    fn swap(&mut self, boxed_state: Box<State>) {
        self.states.clear();
        self.push(boxed_state);
    }

    /// Removes all states from the state stack and quits the game.
    fn drain(&mut self) {
        self.states.clear();
        self.quit();
    }
}

impl EventHandler for StateManager {
    fn update(&mut self, ctx: &mut Context, dt: Duration) -> GameResult<()> {
        let transition = match self.states.last_mut() {
            Some(state) => state.update(ctx, &self.assets, dt),
            None => Ok(Transition::None),
        };

        self.handle_transition(transition?);

        Ok(())
    }

    /// `StateManager::draw` handles drawing for all states on the stack. This
    /// enables the use of having pause screens or other states render on top of
    /// the current state. Due to this, `StateManager::draw` also controls both
    /// clearing and swapping of buffers.
    fn draw(&mut self, ctx: &mut Context) -> GameResult<()> {
        // draw everything in the stack
        use ggez::graphics;

        graphics::set_background_color(ctx, graphics::Color::new(0.0, 0.0, 0.0, 255.0));
        graphics::clear(ctx);

        for (_, state) in self.states.iter_mut().enumerate() {
            state.draw(ctx, &self.assets)?;
        }

        graphics::present(ctx);
        timer::sleep(Duration::from_secs(0));
        Ok(())
    }
    fn mouse_button_down_event(&mut self, _button: MouseButton, _x: i32, _y: i32) {
        if let Some(state) = self.states.last_mut() {
            state.mouse_button_down_event(_button, _x, _y);
        }
    }

    fn mouse_button_up_event(&mut self, _button: MouseButton, _x: i32, _y: i32) {
        if let Some(state) = self.states.last_mut() {
            state.mouse_button_up_event(_button, _x, _y);
        }
    }

    fn mouse_motion_event(&mut self, _state: MouseState, _x: i32, _y: i32, _xrel: i32, _yrel: i32) {
        if let Some(state) = self.states.last_mut() {
            state.mouse_motion_event(_state, _x, _y, _xrel, _yrel);
        }
    }

    fn mouse_wheel_event(&mut self, _x: i32, _y: i32) {
        if let Some(state) = self.states.last_mut() {
            state.mouse_wheel_event(_x, _y);
        }
    }

    fn key_down_event(&mut self, _keycode: Keycode, _keymod: Mod, _repeat: bool) {
        if let Some(state) = self.states.last_mut() {
            state.key_down_event(_keycode, _keymod, _repeat, &self.assets);
        }
    }

    fn key_up_event(&mut self, _keycode: Keycode, _keymod: Mod, _repeat: bool) {
        if let Some(state) = self.states.last_mut() {
            state.key_up_event(_keycode, _keymod, _repeat);
        }
    }

    fn controller_button_down_event(&mut self, _btn: Button, _instance_id: i32) {
        if let Some(state) = self.states.last_mut() {
            state.controller_button_down_event(_btn, _instance_id, &self.assets);
        }
    }
    fn controller_button_up_event(&mut self, _btn: Button, _instance_id: i32) {
        if let Some(state) = self.states.last_mut() {
            state.controller_button_up_event(_btn, _instance_id);
        }
    }
    fn controller_axis_event(&mut self, _axis: Axis, _value: i16, _instance_id: i32) {
        if let Some(state) = self.states.last_mut() {
            state.controller_axis_event(_axis, _value, _instance_id);
        }
    }

    fn focus_event(&mut self, _gained: bool) {
        if let Some(state) = self.states.last_mut() {
            state.focus_event(_gained);
        }
    }

    /// Called upon a quit event.  If it returns true,
    /// the game does not exit.
    fn quit_event(&mut self) -> bool {
        match self.states.last_mut() {
            Some(state) => state.quit_event(),
            None => false,
        }
    }
}
<commit_msg>Fix: Quit if StateManager.running is false<commit_after>pub mod game_over_state;
pub mod intro_state;
pub mod menu_state;
pub mod play_state;
pub mod shared;

use crate::states::intro_state::IntroState;
use ggez::event::{Axis, Button, EventHandler, Keycode, Mod, MouseButton, MouseState};
use ggez::{audio, graphics, timer, Context, GameResult};
use std::collections::HashMap;
use std::time::Duration;

/// A global structure that stores all game assets. This is passed down into a
/// state by the `StateManager`.
pub struct Assets {
    images: HashMap<String, graphics::Image>,
    font: HashMap<String, graphics::Font>,
    music: HashMap<String, audio::Source>,
    sfx: HashMap<String, audio::Source>,
}

impl Assets {
    pub fn new() -> Self {
        Self {
            images: HashMap::new(),
            font: HashMap::new(),
            music: HashMap::new(),
            sfx: HashMap::new(),
        }
    }

    /// Add an image asset to the asset manager.
    pub fn add_image(&mut self, name: &str, image: graphics::Image) -> GameResult<()> {
        self.images.insert(name.to_string(), image);
        Ok(())
    }

    /// Returns an image with the key `name` from the asset manager.
    pub fn get_image(&self, name: &str) -> GameResult<&graphics::Image> {
        let img = self.images.get(name);
        Ok(img.unwrap())
    }

    /// Add a font asset to the asset manager.
    pub fn add_font(&mut self, name: &str, font: graphics::Font) -> GameResult<()> {
        self.font.insert(name.to_string(), font);
        Ok(())
    }

    /// Returns a font with the key `name` from the asset manager.
    pub fn get_font(&self, name: &str) -> GameResult<&graphics::Font> {
        let font = self.font.get(name);
        Ok(font.unwrap())
    }

    /// Add an audio asset meant for music to the asset manager.
    pub fn add_music(&mut self, name: &str, audio: audio::Source) -> GameResult<()> {
        self.music.insert(name.to_string(), audio);
        Ok(())
    }

    /// Returns an audio asset meant for music with the key `name` from the
    /// asset manager.
    pub fn get_music(&self, name: &str) -> GameResult<&audio::Source> {
        let audio = self.music.get(name);
        Ok(audio.unwrap())
    }

    /// Returns the number of audio assets meant for music playback currently in
    /// the asset manager.
    pub fn get_music_count(&self) -> u32 {
        self.music.len() as u32
    }

    /// Add an audio asset meant for sound effects to the asset manager.
    pub fn add_sfx(&mut self, name: &str, audio: audio::Source) -> GameResult<()> {
        self.sfx.insert(name.to_string(), audio);
        Ok(())
    }

    /// Returns an audio asset meant for sound effects with the key `name` from
    /// the asset manager.
    pub fn get_sfx(&self, name: &str) -> GameResult<&audio::Source> {
        let audio = self.sfx.get(name);
        Ok(audio.unwrap())
    }
}

/// Describes a potential state transition. `EventHandler::update` returns a
/// `GameResult<Transition>` that can be used to request a state change from the
/// state manager.
pub enum Transition {
    /// Don't change states.
    None,
    /// Push another state ontop of the current state.
    Push(Box<State>),
    /// Remove all states from the stack and then push a new one.
    Swap(Box<State>),
    /// Remove the state currently at the top of the stack.
    Pop,
    /// Clear the stack which in turn quits the game.
    Drain,
}

pub trait State {
    fn update(
        &mut self,
        ctx: &mut Context,
        assets: &Assets,
        dt: Duration,
    ) -> GameResult<Transition>;
    fn draw(&mut self, ctx: &mut Context, assets: &Assets) -> GameResult<()>;
    fn key_down_event(&mut self, _keycode: Keycode, _keymod: Mod, _repeat: bool, _assets: &Assets) {
    }
    fn key_up_event(&mut self, _keycode: Keycode, _keymod: Mod, _repeat: bool) {}
    fn mouse_button_down_event(&mut self, _button: MouseButton, _x: i32, _y: i32) {}
    fn mouse_button_up_event(&mut self, _button: MouseButton, _x: i32, _y: i32) {}
    fn mouse_motion_event(&mut self, _state: MouseState, _x: i32, _y: i32, _xrel: i32, _yrel: i32) {
    }
    fn mouse_wheel_event(&mut self, _x: i32, _y: i32) {}
    fn controller_button_down_event(&mut self, _btn: Button, _instance_id: i32, _assets: &Assets) {}
    fn controller_button_up_event(&mut self, _btn: Button, _instance_id: i32) {}
    fn controller_axis_event(&mut self, _axis: Axis, _value: i16, _instance_id: i32) {}
    fn focus_event(&mut self, _gained: bool) {}
    fn quit_event(&mut self) -> bool {
        false
    }
}

/// A `StateManager` will manage requests to push, pop or swap states on the
/// state stack. It owns the `Assets` struct and dictates whether the game
/// continues to run or not.
///
/// States must be put into a `Box` before being handed to the `StateManager`.
pub struct StateManager {
    assets: Assets,
    running: bool,
    states: Vec<Box<State>>,
}

impl StateManager {
    /// Create a new `StateManager` and initializes the first state.
    pub fn new(ctx: &mut Context) -> StateManager {
        let assets = StateManager::initialize_assets(ctx).unwrap();
        let state = Box::new(IntroState::new(ctx, &assets).unwrap());

        StateManager {
            running: true,
            states: vec![state],
            assets,
        }
    }

    fn initialize_assets(ctx: &mut Context) -> GameResult<Assets> {
        let mut assets = Assets::new();
        assets.add_image("block", graphics::Image::new(ctx, "/block.png")?)?;
        assets.add_image("menu_bg", graphics::Image::new(ctx, "/blackhole2.png")?)?;
        assets.add_image("game_bg", graphics::Image::new(ctx, "/space_bg.png")?)?;
        assets.add_font(
            "title",
            graphics::Font::new(ctx, "/DejaVuSansMono.ttf", 32)?,
        )?;
        assets.add_font(
            "title_shadow",
            graphics::Font::new(ctx, "/DejaVuSansMono.ttf", 33)?,
        )?;
        assets.add_font(
            "normal",
            graphics::Font::new(ctx, "/DejaVuSansMono.ttf", 18)?,
        )?;
        assets.add_font("ui", graphics::Font::new(ctx, "/DejaVuSansMono.ttf", 16)?)?;

        let mut play_0 = audio::Source::new(ctx, "/music/Track2.ogg")?;
        let mut play_1 = audio::Source::new(ctx, "/music/Track4.ogg")?;
        let mut menu = audio::Source::new(ctx, "/music/Track3.ogg")?;
        play_0.set_volume(0.4);
        play_1.set_volume(0.4);
        menu.set_volume(0.4);
        assets.add_music("play_0", play_0)?;
        assets.add_music("play_1", play_1)?;
        assets.add_music("menu", menu)?;

        let mut click = audio::Source::new(ctx, "/sfx/click.ogg")?;
        let mut gameover_win = audio::Source::new(ctx, "/sfx/gameover_win.ogg")?;
        let mut gameover_lose = audio::Source::new(ctx, "/sfx/gameover_lose.ogg")?;
        click.set_volume(0.5);
        gameover_win.set_volume(0.5);
        gameover_lose.set_volume(0.5);
        assets.add_sfx("click", click)?;
        assets.add_sfx("gameover_win", gameover_win)?;
        assets.add_sfx("gameover_lose", gameover_lose)?;

        Ok(assets)
    }

    /// Clears the state stack and sets `running` to false which quits the game.
    pub fn quit(&mut self) {
        self.states.clear();
        self.running = false
    }
}

impl StateManager {
    /// Calls the state transition handler depending on the `Transition` given
    /// as an argument.
    fn handle_transition(&mut self, transition: Transition) {
        match transition {
            Transition::None => (),
            Transition::Pop => self.pop(),
            Transition::Swap(state) => self.swap(state),
            Transition::Push(state) => self.push(state),
            Transition::Drain => self.drain(),
        }
    }

    /// Pops the state at the top of the stack. If the size of the state stack
    /// after this operation is `0`, the game quits.
    fn pop(&mut self) {
        self.states.pop();

        if self.states.is_empty() {
            self.quit();
        }
    }

    /// Pushes a state onto the state stack.
    fn push(&mut self, boxed_state: Box<State>) {
        self.states.push(boxed_state)
    }

    /// Removes all states from the state stack and then pushes the given
    /// `boxed_state` onto the stack.
    fn swap(&mut self, boxed_state: Box<State>) {
        self.states.clear();
        self.push(boxed_state);
    }

    /// Removes all states from the state stack and quits the game.
    fn drain(&mut self) {
        self.states.clear();
        self.quit();
    }
}

impl EventHandler for StateManager {
    fn update(&mut self, ctx: &mut Context, dt: Duration) -> GameResult<()> {
        if !self.running {
            ctx.quit()?;
        }

        let transition = match self.states.last_mut() {
            Some(state) => state.update(ctx, &self.assets, dt),
            None => Ok(Transition::None),
        };

        self.handle_transition(transition?);

        Ok(())
    }

    /// `StateManager::draw` handles drawing for all states on the stack. This
    /// enables the use of having pause screens or other states render on top of
    /// the current state. Due to this, `StateManager::draw` also controls both
    /// clearing and swapping of buffers.
    fn draw(&mut self, ctx: &mut Context) -> GameResult<()> {
        // draw everything in the stack
        use ggez::graphics;

        graphics::set_background_color(ctx, graphics::Color::new(0.0, 0.0, 0.0, 255.0));
        graphics::clear(ctx);

        for (_, state) in self.states.iter_mut().enumerate() {
            state.draw(ctx, &self.assets)?;
        }

        graphics::present(ctx);
        timer::sleep(Duration::from_secs(0));
        Ok(())
    }
    fn mouse_button_down_event(&mut self, _button: MouseButton, _x: i32, _y: i32) {
        if let Some(state) = self.states.last_mut() {
            state.mouse_button_down_event(_button, _x, _y);
        }
    }

    fn mouse_button_up_event(&mut self, _button: MouseButton, _x: i32, _y: i32) {
        if let Some(state) = self.states.last_mut() {
            state.mouse_button_up_event(_button, _x, _y);
        }
    }

    fn mouse_motion_event(&mut self, _state: MouseState, _x: i32, _y: i32, _xrel: i32, _yrel: i32) {
        if let Some(state) = self.states.last_mut() {
            state.mouse_motion_event(_state, _x, _y, _xrel, _yrel);
        }
    }

    fn mouse_wheel_event(&mut self, _x: i32, _y: i32) {
        if let Some(state) = self.states.last_mut() {
            state.mouse_wheel_event(_x, _y);
        }
    }

    fn key_down_event(&mut self, _keycode: Keycode, _keymod: Mod, _repeat: bool) {
        if let Some(state) = self.states.last_mut() {
            state.key_down_event(_keycode, _keymod, _repeat, &self.assets);
        }
    }

    fn key_up_event(&mut self, _keycode: Keycode, _keymod: Mod, _repeat: bool) {
        if let Some(state) = self.states.last_mut() {
            state.key_up_event(_keycode, _keymod, _repeat);
        }
    }

    fn controller_button_down_event(&mut self, _btn: Button, _instance_id: i32) {
        if let Some(state) = self.states.last_mut() {
            state.controller_button_down_event(_btn, _instance_id, &self.assets);
        }
    }
    fn controller_button_up_event(&mut self, _btn: Button, _instance_id: i32) {
        if let Some(state) = self.states.last_mut() {
            state.controller_button_up_event(_btn, _instance_id);
        }
    }
    fn controller_axis_event(&mut self, _axis: Axis, _value: i16, _instance_id: i32) {
        if let Some(state) = self.states.last_mut() {
            state.controller_axis_event(_axis, _value, _instance_id);
        }
    }

    fn focus_event(&mut self, _gained: bool) {
        if let Some(state) = self.states.last_mut() {
            state.focus_event(_gained);
        }
    }

    /// Called upon a quit event.  If it returns true,
    /// the game does not exit.
    fn quit_event(&mut self) -> bool {
        match self.states.last_mut() {
            Some(state) => state.quit_event(),
            None => false,
        }
    }
}
<|endoftext|>


<commit_before>const CLEARV: u8 = 0b0000_0000;
const BOLD: u8 = 0b0000_0001;
const UNDERLINE: u8 = 0b0000_0010;
const REVERSED: u8 = 0b0000_0100;
const ITALIC: u8 = 0b0000_1000;
const BLINK: u8 = 0b0001_0000;
const HIDDEN: u8 = 0b0010_0000;
const DIMMED: u8 = 0b0100_0000;
const STRIKETHROUGH: u8 = 0b1000_0000;

static STYLES: [(u8, Styles); 8] = [
    (BOLD, Styles::Bold),
    (DIMMED, Styles::Dimmed),
    (UNDERLINE, Styles::Underline),
    (REVERSED, Styles::Reversed),
    (ITALIC, Styles::Italic),
    (BLINK, Styles::Blink),
    (HIDDEN, Styles::Hidden),
    (STRIKETHROUGH, Styles::Strikethrough),
];

pub static CLEAR: Style = Style(CLEARV);

#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub struct Style(u8);

#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum Styles {
    Clear,
    Bold,
    Dimmed,
    Underline,
    Reversed,
    Italic,
    Blink,
    Hidden,
    Strikethrough,
}

impl Styles {
    fn to_str<'a>(self) -> &'a str {
        match self {
            Styles::Clear => "", // unreachable, but we don't want to panic
            Styles::Bold => "1",
            Styles::Dimmed => "2",
            Styles::Italic => "3",
            Styles::Underline => "4",
            Styles::Blink => "5",
            Styles::Reversed => "7",
            Styles::Hidden => "8",
            Styles::Strikethrough => "9",
        }
    }

    fn to_u8(self) -> u8 {
        match self {
            Styles::Clear => CLEARV,
            Styles::Bold => BOLD,
            Styles::Dimmed => DIMMED,
            Styles::Italic => ITALIC,
            Styles::Underline => UNDERLINE,
            Styles::Blink => BLINK,
            Styles::Reversed => REVERSED,
            Styles::Hidden => HIDDEN,
            Styles::Strikethrough => STRIKETHROUGH,
        }
    }

    fn from_u8(u: u8) -> Option<Vec<Styles>> {
        if u == CLEARV {
            return None;
        }

        let res: Vec<Styles> = STYLES
            .into_iter()
            .filter(|&&(ref mask, _)| (0 != (u & mask)))
            .map(|&(_, value)| value)
            .collect();
        if res.is_empty() {
            None
        } else {
            Some(res)
        }
    }
}

impl Style {
    pub fn to_str(&self) -> String {
        let styles = match Styles::from_u8(self.0) {
            None => return String::new(),
            Some(s) => s,
        };
        let mut res = String::new();
        let mut first = true;

        for style in styles.iter().map(|s| s.to_str()) {
            if first {
                res.push_str(style);
                first = false;
                continue;
            } else {
                res.push(';');
                res.push_str(style)
            }
        }
        res
    }

    pub fn new(from: Styles) -> Style {
        Style(from.to_u8())
    }

    pub fn from_both(one: Style, two: Styles) -> Style {
        Style(one.0 | two.to_u8())
    }
}

#[cfg(test)]
mod tests {

    mod u8_to_styles_invalid_is_none {
        use super::super::CLEARV;
        use super::super::{Style, Styles};

        #[test]
        fn empty_is_none() {
            assert_eq!(None, Styles::from_u8(CLEARV))
        }

    }

    mod u8_to_styles_isomorphism {
        use super::super::Styles;
        use super::super::{
            BLINK, BOLD, DIMMED, HIDDEN, ITALIC, REVERSED, STRIKETHROUGH, UNDERLINE,
        };

        macro_rules! value_isomorph {
            ($name:ident, $value:expr) => {
                #[test]
                fn $name() {
                    let u = Styles::from_u8($value);
                    assert!(
                        u.is_some(),
                        "{}: Styles::from_u8 -> None",
                        stringify!($value)
                    );
                    let u = u.unwrap();
                    assert!(
                        u.len() == 1,
                        "{}: Styles::from_u8 found {} styles (expected 1)",
                        stringify!($value),
                        u.len()
                    );
                    assert!(
                        u[0].to_u8() == $value,
                        "{}: to_u8() doesn't match its const value",
                        stringify!($value)
                    );
                }
            };
        }

        value_isomorph!(bold, BOLD);
        value_isomorph!(underline, UNDERLINE);
        value_isomorph!(reversed, REVERSED);
        value_isomorph!(italic, ITALIC);
        value_isomorph!(blink, BLINK);
        value_isomorph!(hidden, HIDDEN);
        value_isomorph!(dimmed, DIMMED);
        value_isomorph!(strikethrough, STRIKETHROUGH);
    }

    mod styles_combine_complex {
        use super::super::Styles::*;
        use super::super::{Style, Styles};
        use super::super::{
            BLINK, BOLD, DIMMED, HIDDEN, ITALIC, REVERSED, STRIKETHROUGH, UNDERLINE,
        };

        fn style_from_multiples(styles: &[Styles]) -> Style {
            let mut res = Style::new(styles[0]);
            for s in &styles[1..] {
                res = Style::from_both(res, *s)
            }
            res
        }

        macro_rules! test_aggreg {
            ($styles:expr, $expect:expr) => {{
                let v = style_from_multiples($styles);
                let r = Styles::from_u8(v.0).expect("should find styles");
                assert_eq!(&$expect as &[Styles], &r[..])
            }};
        }

        #[test]
        fn aggreg1() {
            let styles: &[Styles] = &[Bold, Bold, Bold];
            test_aggreg!(styles, [Bold])
        }

        #[test]
        fn aggreg2() {
            let styles: &[Styles] = &[Italic, Italic, Bold, Bold];
            test_aggreg!(styles, [Bold, Italic])
        }

        #[test]
        fn aggreg3() {
            let styles: &[Styles] = &[Bold, Italic, Bold];
            test_aggreg!(styles, [Bold, Italic])
        }

        macro_rules! test_combine {
            ($styles:expr) => {{
                let v = style_from_multiples($styles);
                let r = Styles::from_u8(v.0).expect("should find styles");
                assert_eq!($styles, &r[..])
            }};
        }

        #[test]
        fn two1() {
            let s: &[Styles] = &[Bold, Underline];
            test_combine!(s)
        }

        #[test]
        fn two2() {
            let s: &[Styles] = &[Underline, Italic];
            test_combine!(s)
        }

        #[test]
        fn two3() {
            let s: &[Styles] = &[Bold, Italic];
            test_combine!(s)
        }

        #[test]
        fn three1() {
            let s: &[Styles] = &[Bold, Underline, Italic];
            test_combine!(s)
        }

        #[test]
        fn three2() {
            let s: &[Styles] = &[Dimmed, Underline, Italic];
            test_combine!(s)
        }

        #[test]
        fn four() {
            let s: &[Styles] = &[Dimmed, Underline, Italic, Hidden];
            test_combine!(s)
        }

        #[test]
        fn five() {
            let s: &[Styles] = &[Dimmed, Underline, Italic, Blink, Hidden];
            test_combine!(s)
        }

        #[test]
        fn six() {
            let s: &[Styles] = &[Bold, Dimmed, Underline, Italic, Blink, Hidden];
            test_combine!(s)
        }

        #[test]
        fn all() {
            let s: &[Styles] = &[
                Bold,
                Dimmed,
                Underline,
                Reversed,
                Italic,
                Blink,
                Hidden,
                Strikethrough,
            ];
            test_combine!(s)
        }

    }
}
<commit_msg>pass by value for performance (#42)<commit_after>const CLEARV: u8 = 0b0000_0000;
const BOLD: u8 = 0b0000_0001;
const UNDERLINE: u8 = 0b0000_0010;
const REVERSED: u8 = 0b0000_0100;
const ITALIC: u8 = 0b0000_1000;
const BLINK: u8 = 0b0001_0000;
const HIDDEN: u8 = 0b0010_0000;
const DIMMED: u8 = 0b0100_0000;
const STRIKETHROUGH: u8 = 0b1000_0000;

static STYLES: [(u8, Styles); 8] = [
    (BOLD, Styles::Bold),
    (DIMMED, Styles::Dimmed),
    (UNDERLINE, Styles::Underline),
    (REVERSED, Styles::Reversed),
    (ITALIC, Styles::Italic),
    (BLINK, Styles::Blink),
    (HIDDEN, Styles::Hidden),
    (STRIKETHROUGH, Styles::Strikethrough),
];

pub static CLEAR: Style = Style(CLEARV);

#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub struct Style(u8);

#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum Styles {
    Clear,
    Bold,
    Dimmed,
    Underline,
    Reversed,
    Italic,
    Blink,
    Hidden,
    Strikethrough,
}

impl Styles {
    fn to_str<'a>(self) -> &'a str {
        match self {
            Styles::Clear => "", // unreachable, but we don't want to panic
            Styles::Bold => "1",
            Styles::Dimmed => "2",
            Styles::Italic => "3",
            Styles::Underline => "4",
            Styles::Blink => "5",
            Styles::Reversed => "7",
            Styles::Hidden => "8",
            Styles::Strikethrough => "9",
        }
    }

    fn to_u8(self) -> u8 {
        match self {
            Styles::Clear => CLEARV,
            Styles::Bold => BOLD,
            Styles::Dimmed => DIMMED,
            Styles::Italic => ITALIC,
            Styles::Underline => UNDERLINE,
            Styles::Blink => BLINK,
            Styles::Reversed => REVERSED,
            Styles::Hidden => HIDDEN,
            Styles::Strikethrough => STRIKETHROUGH,
        }
    }

    fn from_u8(u: u8) -> Option<Vec<Styles>> {
        if u == CLEARV {
            return None;
        }

        let res: Vec<Styles> = STYLES
            .into_iter()
            .filter(|&&(ref mask, _)| (0 != (u & mask)))
            .map(|&(_, value)| value)
            .collect();
        if res.is_empty() {
            None
        } else {
            Some(res)
        }
    }
}

impl Style {
    pub fn to_str(self) -> String {
        let styles = match Styles::from_u8(self.0) {
            None => return String::new(),
            Some(s) => s,
        };
        let mut res = String::new();
        let mut first = true;

        for style in styles.iter().map(|s| s.to_str()) {
            if first {
                res.push_str(style);
                first = false;
                continue;
            } else {
                res.push(';');
                res.push_str(style)
            }
        }
        res
    }

    pub fn new(from: Styles) -> Style {
        Style(from.to_u8())
    }

    pub fn from_both(one: Style, two: Styles) -> Style {
        Style(one.0 | two.to_u8())
    }
}

#[cfg(test)]
mod tests {

    mod u8_to_styles_invalid_is_none {
        use super::super::CLEARV;
        use super::super::{Style, Styles};

        #[test]
        fn empty_is_none() {
            assert_eq!(None, Styles::from_u8(CLEARV))
        }

    }

    mod u8_to_styles_isomorphism {
        use super::super::Styles;
        use super::super::{
            BLINK, BOLD, DIMMED, HIDDEN, ITALIC, REVERSED, STRIKETHROUGH, UNDERLINE,
        };

        macro_rules! value_isomorph {
            ($name:ident, $value:expr) => {
                #[test]
                fn $name() {
                    let u = Styles::from_u8($value);
                    assert!(
                        u.is_some(),
                        "{}: Styles::from_u8 -> None",
                        stringify!($value)
                    );
                    let u = u.unwrap();
                    assert!(
                        u.len() == 1,
                        "{}: Styles::from_u8 found {} styles (expected 1)",
                        stringify!($value),
                        u.len()
                    );
                    assert!(
                        u[0].to_u8() == $value,
                        "{}: to_u8() doesn't match its const value",
                        stringify!($value)
                    );
                }
            };
        }

        value_isomorph!(bold, BOLD);
        value_isomorph!(underline, UNDERLINE);
        value_isomorph!(reversed, REVERSED);
        value_isomorph!(italic, ITALIC);
        value_isomorph!(blink, BLINK);
        value_isomorph!(hidden, HIDDEN);
        value_isomorph!(dimmed, DIMMED);
        value_isomorph!(strikethrough, STRIKETHROUGH);
    }

    mod styles_combine_complex {
        use super::super::Styles::*;
        use super::super::{Style, Styles};
        use super::super::{
            BLINK, BOLD, DIMMED, HIDDEN, ITALIC, REVERSED, STRIKETHROUGH, UNDERLINE,
        };

        fn style_from_multiples(styles: &[Styles]) -> Style {
            let mut res = Style::new(styles[0]);
            for s in &styles[1..] {
                res = Style::from_both(res, *s)
            }
            res
        }

        macro_rules! test_aggreg {
            ($styles:expr, $expect:expr) => {{
                let v = style_from_multiples($styles);
                let r = Styles::from_u8(v.0).expect("should find styles");
                assert_eq!(&$expect as &[Styles], &r[..])
            }};
        }

        #[test]
        fn aggreg1() {
            let styles: &[Styles] = &[Bold, Bold, Bold];
            test_aggreg!(styles, [Bold])
        }

        #[test]
        fn aggreg2() {
            let styles: &[Styles] = &[Italic, Italic, Bold, Bold];
            test_aggreg!(styles, [Bold, Italic])
        }

        #[test]
        fn aggreg3() {
            let styles: &[Styles] = &[Bold, Italic, Bold];
            test_aggreg!(styles, [Bold, Italic])
        }

        macro_rules! test_combine {
            ($styles:expr) => {{
                let v = style_from_multiples($styles);
                let r = Styles::from_u8(v.0).expect("should find styles");
                assert_eq!($styles, &r[..])
            }};
        }

        #[test]
        fn two1() {
            let s: &[Styles] = &[Bold, Underline];
            test_combine!(s)
        }

        #[test]
        fn two2() {
            let s: &[Styles] = &[Underline, Italic];
            test_combine!(s)
        }

        #[test]
        fn two3() {
            let s: &[Styles] = &[Bold, Italic];
            test_combine!(s)
        }

        #[test]
        fn three1() {
            let s: &[Styles] = &[Bold, Underline, Italic];
            test_combine!(s)
        }

        #[test]
        fn three2() {
            let s: &[Styles] = &[Dimmed, Underline, Italic];
            test_combine!(s)
        }

        #[test]
        fn four() {
            let s: &[Styles] = &[Dimmed, Underline, Italic, Hidden];
            test_combine!(s)
        }

        #[test]
        fn five() {
            let s: &[Styles] = &[Dimmed, Underline, Italic, Blink, Hidden];
            test_combine!(s)
        }

        #[test]
        fn six() {
            let s: &[Styles] = &[Bold, Dimmed, Underline, Italic, Blink, Hidden];
            test_combine!(s)
        }

        #[test]
        fn all() {
            let s: &[Styles] = &[
                Bold,
                Dimmed,
                Underline,
                Reversed,
                Italic,
                Blink,
                Hidden,
                Strikethrough,
            ];
            test_combine!(s)
        }

    }
}
<|endoftext|>


<commit_before>#![allow(unused_imports)]

use rand::{Rng, task_rng};
use stdtest::Bencher;
use std::str;
use super::super::{Regexp, NoExpand};

fn bench_assert_match(b: &mut Bencher, re: Regexp, text: &str) {
    b.iter(|| if !re.is_match(text) { fail!("no match") });
}

#[bench]
fn no_exponential(b: &mut Bencher) {
    let n = 100;
    let re = Regexp::new("a?".repeat(n) + "a".repeat(n)).unwrap();
    let text = "a".repeat(n);
    bench_assert_match(b, re, text);
}

#[bench]
fn literal(b: &mut Bencher) {
    let re = Regexp::new("y").unwrap();
    let text = "x".repeat(50) + "y";
    bench_assert_match(b, re, text);
}

#[bench]
fn not_literal(b: &mut Bencher) {
    let re = Regexp::new(".y").unwrap();
    let text = "x".repeat(50) + "y";
    bench_assert_match(b, re, text);
}

#[bench]
fn match_class(b: &mut Bencher) {
    let re = Regexp::new("[abcdw]").unwrap();
    let text = "xxxx".repeat(20) + "w";
    bench_assert_match(b, re, text);
}

#[bench]
fn match_class_in_range(b: &mut Bencher) {
    // 'b' is between 'a' and 'c', so the class range checking doesn't help.
    let re = Regexp::new("[ac]").unwrap();
    let text = "bbbb".repeat(20) + "c";
    bench_assert_match(b, re, text);
}

#[bench]
fn replace_all(b: &mut Bencher) {
    let re = Regexp::new("[cjrw]").unwrap();
	let text = "abcdefghijklmnopqrstuvwxyz";
    // FIXME: This isn't using the $name expand stuff.
    // It's possible RE2/Go is using it, but currently, the expand in this
    // crate is actually compiling a regex, so it's incredibly slow.
    b.iter(|| re.replace_all(text, NoExpand("")));
}

#[bench]
fn anchored_literal_short_non_match(b: &mut Bencher) {
    let re = Regexp::new("^zbc(d|e)").unwrap();
    let text = "abcdefghijklmnopqrstuvwxyz";
    b.iter(|| re.is_match(text));
}

#[bench]
fn anchored_literal_long_non_match(b: &mut Bencher) {
    let re = Regexp::new("^zbc(d|e)").unwrap();
    let text = "abcdefghijklmnopqrstuvwxyz".repeat(15);
    b.iter(|| re.is_match(text));
}

#[bench]
fn anchored_literal_short_match(b: &mut Bencher) {
    let re = Regexp::new("^.bc(d|e)").unwrap();
    let text = "abcdefghijklmnopqrstuvwxyz";
    b.iter(|| re.is_match(text));
}

#[bench]
fn anchored_literal_long_match(b: &mut Bencher) {
    let re = Regexp::new("^.bc(d|e)").unwrap();
    let text = "abcdefghijklmnopqrstuvwxyz".repeat(15);
    b.iter(|| re.is_match(text));
}

#[bench]
fn one_pass_short_a(b: &mut Bencher) {
    let re = Regexp::new("^.bc(d|e)*$").unwrap();
    let text = "abcddddddeeeededd";
    b.iter(|| re.is_match(text));
}

#[bench]
fn one_pass_short_a_not(b: &mut Bencher) {
    let re = Regexp::new(".bc(d|e)*$").unwrap();
    let text = "abcddddddeeeededd";
    b.iter(|| re.is_match(text));
}

#[bench]
fn one_pass_short_b(b: &mut Bencher) {
    let re = Regexp::new("^.bc(?:d|e)*$").unwrap();
    let text = "abcddddddeeeededd";
    b.iter(|| re.is_match(text));
}

#[bench]
fn one_pass_short_b_not(b: &mut Bencher) {
    let re = Regexp::new(".bc(?:d|e)*$").unwrap();
    let text = "abcddddddeeeededd";
    b.iter(|| re.is_match(text));
}

#[bench]
fn one_pass_long_prefix(b: &mut Bencher) {
    let re = Regexp::new("^abcdefghijklmnopqrstuvwxyz.*$").unwrap();
    let text = "abcdefghijklmnopqrstuvwxyz";
    b.iter(|| re.is_match(text));
}

#[bench]
fn one_pass_long_prefix_not(b: &mut Bencher) {
    let re = Regexp::new("^.bcdefghijklmnopqrstuvwxyz.*$").unwrap();
    let text = "abcdefghijklmnopqrstuvwxyz";
    b.iter(|| re.is_match(text));
}

macro_rules! throughput(
    ($name:ident, $regex:ident, $size:expr) => (
        #[bench]
        fn $name(b: &mut Bencher) {
            let re = Regexp::new($regex).unwrap();
            let text = gen_text($size);
            b.bytes = $size;
            b.iter(|| if re.is_match(text) { fail!("match") });
        }
    );
)

static EASY0: &'static str  = "ABCDEFGHIJKLMNOPQRSTUVWXYZ$";
static EASY1: &'static str  = "A[AB]B[BC]C[CD]D[DE]E[EF]F[FG]G[GH]H[HI]I[IJ]J$";
static MEDIUM: &'static str = "[XYZ]ABCDEFGHIJKLMNOPQRSTUVWXYZ$";
static HARD: &'static str   = "[ -~]*ABCDEFGHIJKLMNOPQRSTUVWXYZ$";

fn gen_text(n: uint) -> ~str {
    let mut rng = task_rng();
    let mut bytes = rng.gen_ascii_str(n).into_bytes();
    for (i, b) in bytes.mut_iter().enumerate() {
        if i % 20 == 0 {
            *b = '\n' as u8
        }
    }
    str::from_utf8(bytes).unwrap().to_owned()
}

// The 32MB tests take too damn long.
// I actually think this is the fault of the microbenchmark facilities built
// into rustc. Go's microbenchmarking seems to handle things fine.

throughput!(easy0_32, EASY0, 32)
throughput!(easy0_1K, EASY0, 1<<10)
throughput!(easy0_32K, EASY0, 32<<10)
throughput!(easy0_1M, EASY0, 1<<20)
// throughput!(easy0_32M, EASY0, 32<<20)

throughput!(easy1_32, EASY1, 32)
throughput!(easy1_1K, EASY1, 1<<10)
throughput!(easy1_32K, EASY1, 32<<10)
throughput!(easy1_1M, EASY1, 1<<20)
// throughput!(easy1_32M, EASY1, 32<<20)

throughput!(medium_32, MEDIUM, 32)
throughput!(medium_1K, MEDIUM, 1<<10)
throughput!(medium_32K, MEDIUM, 32<<10)
throughput!(medium_1M, MEDIUM, 1<<20)
// throughput!(medium_32M, MEDIUM, 32<<20)

throughput!(hard_32, HARD, 32)
throughput!(hard_1K, HARD, 1<<10)
throughput!(hard_32K, HARD, 32<<10)
throughput!(hard_1M, HARD, 1<<20)
// throughput!(hard_32M, HARD, 32<<20)

<commit_msg>Somehow a tab character snuck in.<commit_after>#![allow(unused_imports)]

use rand::{Rng, task_rng};
use stdtest::Bencher;
use std::str;
use super::super::{Regexp, NoExpand};

fn bench_assert_match(b: &mut Bencher, re: Regexp, text: &str) {
    b.iter(|| if !re.is_match(text) { fail!("no match") });
}

#[bench]
fn no_exponential(b: &mut Bencher) {
    let n = 100;
    let re = Regexp::new("a?".repeat(n) + "a".repeat(n)).unwrap();
    let text = "a".repeat(n);
    bench_assert_match(b, re, text);
}

#[bench]
fn literal(b: &mut Bencher) {
    let re = Regexp::new("y").unwrap();
    let text = "x".repeat(50) + "y";
    bench_assert_match(b, re, text);
}

#[bench]
fn not_literal(b: &mut Bencher) {
    let re = Regexp::new(".y").unwrap();
    let text = "x".repeat(50) + "y";
    bench_assert_match(b, re, text);
}

#[bench]
fn match_class(b: &mut Bencher) {
    let re = Regexp::new("[abcdw]").unwrap();
    let text = "xxxx".repeat(20) + "w";
    bench_assert_match(b, re, text);
}

#[bench]
fn match_class_in_range(b: &mut Bencher) {
    // 'b' is between 'a' and 'c', so the class range checking doesn't help.
    let re = Regexp::new("[ac]").unwrap();
    let text = "bbbb".repeat(20) + "c";
    bench_assert_match(b, re, text);
}

#[bench]
fn replace_all(b: &mut Bencher) {
    let re = Regexp::new("[cjrw]").unwrap();
    let text = "abcdefghijklmnopqrstuvwxyz";
    // FIXME: This isn't using the $name expand stuff.
    // It's possible RE2/Go is using it, but currently, the expand in this
    // crate is actually compiling a regex, so it's incredibly slow.
    b.iter(|| re.replace_all(text, NoExpand("")));
}

#[bench]
fn anchored_literal_short_non_match(b: &mut Bencher) {
    let re = Regexp::new("^zbc(d|e)").unwrap();
    let text = "abcdefghijklmnopqrstuvwxyz";
    b.iter(|| re.is_match(text));
}

#[bench]
fn anchored_literal_long_non_match(b: &mut Bencher) {
    let re = Regexp::new("^zbc(d|e)").unwrap();
    let text = "abcdefghijklmnopqrstuvwxyz".repeat(15);
    b.iter(|| re.is_match(text));
}

#[bench]
fn anchored_literal_short_match(b: &mut Bencher) {
    let re = Regexp::new("^.bc(d|e)").unwrap();
    let text = "abcdefghijklmnopqrstuvwxyz";
    b.iter(|| re.is_match(text));
}

#[bench]
fn anchored_literal_long_match(b: &mut Bencher) {
    let re = Regexp::new("^.bc(d|e)").unwrap();
    let text = "abcdefghijklmnopqrstuvwxyz".repeat(15);
    b.iter(|| re.is_match(text));
}

#[bench]
fn one_pass_short_a(b: &mut Bencher) {
    let re = Regexp::new("^.bc(d|e)*$").unwrap();
    let text = "abcddddddeeeededd";
    b.iter(|| re.is_match(text));
}

#[bench]
fn one_pass_short_a_not(b: &mut Bencher) {
    let re = Regexp::new(".bc(d|e)*$").unwrap();
    let text = "abcddddddeeeededd";
    b.iter(|| re.is_match(text));
}

#[bench]
fn one_pass_short_b(b: &mut Bencher) {
    let re = Regexp::new("^.bc(?:d|e)*$").unwrap();
    let text = "abcddddddeeeededd";
    b.iter(|| re.is_match(text));
}

#[bench]
fn one_pass_short_b_not(b: &mut Bencher) {
    let re = Regexp::new(".bc(?:d|e)*$").unwrap();
    let text = "abcddddddeeeededd";
    b.iter(|| re.is_match(text));
}

#[bench]
fn one_pass_long_prefix(b: &mut Bencher) {
    let re = Regexp::new("^abcdefghijklmnopqrstuvwxyz.*$").unwrap();
    let text = "abcdefghijklmnopqrstuvwxyz";
    b.iter(|| re.is_match(text));
}

#[bench]
fn one_pass_long_prefix_not(b: &mut Bencher) {
    let re = Regexp::new("^.bcdefghijklmnopqrstuvwxyz.*$").unwrap();
    let text = "abcdefghijklmnopqrstuvwxyz";
    b.iter(|| re.is_match(text));
}

macro_rules! throughput(
    ($name:ident, $regex:ident, $size:expr) => (
        #[bench]
        fn $name(b: &mut Bencher) {
            let re = Regexp::new($regex).unwrap();
            let text = gen_text($size);
            b.bytes = $size;
            b.iter(|| if re.is_match(text) { fail!("match") });
        }
    );
)

static EASY0: &'static str  = "ABCDEFGHIJKLMNOPQRSTUVWXYZ$";
static EASY1: &'static str  = "A[AB]B[BC]C[CD]D[DE]E[EF]F[FG]G[GH]H[HI]I[IJ]J$";
static MEDIUM: &'static str = "[XYZ]ABCDEFGHIJKLMNOPQRSTUVWXYZ$";
static HARD: &'static str   = "[ -~]*ABCDEFGHIJKLMNOPQRSTUVWXYZ$";

fn gen_text(n: uint) -> ~str {
    let mut rng = task_rng();
    let mut bytes = rng.gen_ascii_str(n).into_bytes();
    for (i, b) in bytes.mut_iter().enumerate() {
        if i % 20 == 0 {
            *b = '\n' as u8
        }
    }
    str::from_utf8(bytes).unwrap().to_owned()
}

// The 32MB tests take too damn long.
// I actually think this is the fault of the microbenchmark facilities built
// into rustc. Go's microbenchmarking seems to handle things fine.

throughput!(easy0_32, EASY0, 32)
throughput!(easy0_1K, EASY0, 1<<10)
throughput!(easy0_32K, EASY0, 32<<10)
throughput!(easy0_1M, EASY0, 1<<20)
// throughput!(easy0_32M, EASY0, 32<<20)

throughput!(easy1_32, EASY1, 32)
throughput!(easy1_1K, EASY1, 1<<10)
throughput!(easy1_32K, EASY1, 32<<10)
throughput!(easy1_1M, EASY1, 1<<20)
// throughput!(easy1_32M, EASY1, 32<<20)

throughput!(medium_32, MEDIUM, 32)
throughput!(medium_1K, MEDIUM, 1<<10)
throughput!(medium_32K, MEDIUM, 32<<10)
throughput!(medium_1M, MEDIUM, 1<<20)
// throughput!(medium_32M, MEDIUM, 32<<20)

throughput!(hard_32, HARD, 32)
throughput!(hard_1K, HARD, 1<<10)
throughput!(hard_32K, HARD, 32<<10)
throughput!(hard_1M, HARD, 1<<20)
// throughput!(hard_32M, HARD, 32<<20)

<|endoftext|>


<commit_before>//
// Copyright 2018 The Project Oak Authors
//
// 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.
//

extern crate byteorder;
extern crate fmt;
#[macro_use]
extern crate log;
extern crate protobuf;

use byteorder::WriteBytesExt;
use proto::oak_api::OakStatus;
use protobuf::{Message, ProtobufEnum};
use std::io;
use std::io::Write;

pub mod proto;
pub mod storage;
#[cfg(test)]
mod tests;

#[cfg(test)]
#[macro_use]
extern crate assert_matches;

pub type GrpcResult<T> = Result<T, proto::status::Status>;

/// Trait to allow repeated writing of responses for server-streaming gRPC methods.
pub trait ResponseWriter<T: protobuf::Message> {
    fn write(&mut self, rsp: T);
}

/// Implementation of ResponseWriter that encapsulates response messages into
/// GrpcResponse wrapper messages and writes serialized versions to a (mutably
/// borrowed) send channel.
pub struct ChannelResponseWriter<'a> {
    pub channel: &'a mut SendChannelHalf,
}

impl<'a, T> ResponseWriter<T> for ChannelResponseWriter<'a>
where
    T: protobuf::Message,
{
    fn write(&mut self, rsp: T) {
        // Put the serialized response into a GrpcResponse message wrapper.
        let mut grpc_rsp = proto::grpc_encap::GrpcResponse::new();
        let mut any = protobuf::well_known_types::Any::new();
        rsp.write_to_writer(&mut any.value).unwrap();
        grpc_rsp.set_rsp_msg(any);
        // Serialize the GrpcResponse into the send channel.
        grpc_rsp.write_to_writer(&mut self.channel).unwrap();
    }
}

pub type Handle = u64;

/// Map an OakStatus to the nearest available std::io::Result.
fn result_from_status<T>(status: Option<OakStatus>, val: T) -> std::io::Result<T> {
    match status {
        Some(OakStatus::OAK_STATUS_UNSPECIFIED) => Err(io::Error::new(
            io::ErrorKind::Other,
            "Unspecified Oak status value",
        )),
        Some(OakStatus::OK) => Ok(val),
        Some(OakStatus::ERR_BAD_HANDLE) => {
            Err(io::Error::new(io::ErrorKind::NotConnected, "Bad handle"))
        }
        Some(OakStatus::ERR_INVALID_ARGS) => Err(io::Error::new(
            io::ErrorKind::InvalidInput,
            "Invalid arguments",
        )),
        Some(OakStatus::ERR_CHANNEL_CLOSED) => Err(io::Error::new(
            io::ErrorKind::ConnectionReset,
            "Channel closed",
        )),
        Some(OakStatus::ERR_BUFFER_TOO_SMALL) => Err(io::Error::new(
            io::ErrorKind::UnexpectedEof,
            "Buffer too small",
        )),
        Some(OakStatus::ERR_OUT_OF_RANGE) => {
            Err(io::Error::new(io::ErrorKind::NotConnected, "Out of range"))
        }
        Some(OakStatus::ERR_INTERNAL) => {
            Err(io::Error::new(io::ErrorKind::Other, "Internal error"))
        }
        None => Err(io::Error::new(
            io::ErrorKind::Other,
            "Unknown Oak status value",
        )),
    }
}

mod wasm {
    // See https://rustwasm.github.io/book/reference/js-ffi.html
    #[link(wasm_import_module = "oak")]
    extern "C" {
        pub fn wait_on_channels(buf: *mut u8, count: u32) -> i32;
        pub fn channel_read(handle: u64, buf: *mut u8, size: usize, actual_size: *mut u32) -> i32;
        pub fn channel_write(handle: u64, buf: *const u8, size: usize) -> i32;
        pub fn channel_find(buf: *const u8, len: usize) -> u64;
    }
}

pub fn channel_find(port_name: &str) -> Handle {
    unsafe { wasm::channel_find(port_name.as_ptr(), port_name.len()) }
}

pub const SPACE_BYTES_PER_HANDLE: usize = 9;

// Build a chunk of memory that is suitable for passing to wasm::wait_on_channels,
// holding the given collection of channel handles.
fn new_handle_space(handles: &[Handle]) -> Vec<u8> {
    let mut space = Vec::with_capacity(SPACE_BYTES_PER_HANDLE * handles.len());
    for handle in handles {
        space.write_u64::<byteorder::LittleEndian>(*handle).unwrap();
        space.push(0x00);
    }
    space
}

// Prepare a handle space for polling by clearing all of the message-pending
// indicator bytes.
fn prep_handle_space(space: &mut [u8]) {
    let count = space.len() / 8;
    for i in 0..count {
        space[i * SPACE_BYTES_PER_HANDLE + (SPACE_BYTES_PER_HANDLE - 1)] = 0;
    }
}

pub struct SendChannelHalf {
    handle: Handle,
}

impl SendChannelHalf {
    pub fn new(handle: Handle) -> SendChannelHalf {
        SendChannelHalf { handle }
    }

    pub fn write_message(&mut self, buf: &[u8]) -> std::io::Result<()> {
        result_from_status(
            OakStatus::from_i32(unsafe {
                wasm::channel_write(self.handle, buf.as_ptr(), buf.len())
            }),
            (),
        )
    }
}

// Implement the Write trait on a send channel for convenience, particularly for
// the logging channel.
impl Write for SendChannelHalf {
    fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
        match self.write_message(buf) {
            Ok(_) => Ok(buf.len()),
            Err(e) => Err(e),
        }
    }
    fn flush(&mut self) -> std::io::Result<()> {
        Ok(())
    }
}

pub fn logging_channel() -> impl Write {
    let logging_channel = SendChannelHalf::new(channel_find("log"));
    // Only flush logging channel on newlines.
    std::io::LineWriter::new(logging_channel)
}

pub struct ReceiveChannelHalf {
    handle: Handle,
}

impl ReceiveChannelHalf {
    pub fn new(handle: Handle) -> ReceiveChannelHalf {
        ReceiveChannelHalf { handle }
    }

    pub fn read_message(&mut self, buf: &mut Vec<u8>) -> std::io::Result<usize> {
        // Try reading from the channel twice: first with provided vector,
        // then with a vector that's been resized to meet size requirements.
        for resized in &[false, true] {
            let mut actual_size: u32 = 0;
            let status = OakStatus::from_i32(unsafe {
                wasm::channel_read(
                    self.handle,
                    buf.as_mut_ptr(),
                    buf.capacity(),
                    &mut actual_size,
                )
            });
            match status {
                Some(OakStatus::OK) => {
                    unsafe {
                        buf.set_len(actual_size as usize);
                    };
                    return Ok(actual_size as usize);
                }
                Some(OakStatus::ERR_BUFFER_TOO_SMALL) => {
                    if *resized {
                        return result_from_status(status, 0);
                    }
                    // Can escape the match if buffer is too small and !resized.
                }
                _ => return result_from_status(status, 0),
            }

            // Extend the vector to be large enough for the message
            debug!("Got {}, need {}", buf.capacity(), actual_size);
            if (actual_size as usize) < buf.len() {
                return Err(io::Error::new(
                    io::ErrorKind::Other,
                    format!(
                        "Internal error: provided {} bytes for receive, asked for {}",
                        buf.len(),
                        actual_size
                    ),
                ));
            }
            let extra = (actual_size as usize) - buf.len();
            buf.reserve(extra);
        }
        Err(io::Error::new(io::ErrorKind::Other, "Unreachable reached!"))
    }
}

/// Convenience wrapper for a send/receive pair of channels.
pub struct ChannelPair {
    pub receive: ReceiveChannelHalf,
    pub send: SendChannelHalf,
}

impl ChannelPair {
    pub fn new(in_handle: Handle, out_handle: Handle) -> ChannelPair {
        ChannelPair {
            receive: ReceiveChannelHalf::new(in_handle),
            send: SendChannelHalf::new(out_handle),
        }
    }
}

/// Trait encapsulating the operations required for an Oak Node.
pub trait OakNode {
    fn new() -> Self
    where
        Self: Sized;
    fn invoke(&mut self, method: &str, req: &[u8], out: &mut SendChannelHalf);
}

/// Perform a gRPC event loop on the given channels, invoking the given node.
pub fn grpc_event_loop<T: OakNode>(
    mut node: T,
    grpc_in_handle: Handle,
    grpc_out_handle: Handle,
) -> i32 {
    info!(
        "start event loop for node with handles in:{} out:{}",
        grpc_in_handle, grpc_out_handle
    );
    if grpc_in_handle == 0 || grpc_out_handle == 0 {
        return OakStatus::ERR_CHANNEL_CLOSED.value();
    }
    set_panic_hook();

    let read_handles = vec![grpc_in_handle];
    let mut space = new_handle_space(&read_handles);

    let mut grpc_in_channel = ReceiveChannelHalf::new(grpc_in_handle);
    let mut grpc_out_channel = SendChannelHalf::new(grpc_out_handle);
    loop {
        // Block until there is a message to read on an input channel.
        prep_handle_space(&mut space);
        unsafe {
            let status = wasm::wait_on_channels(space.as_mut_ptr(), read_handles.len() as u32);
            match OakStatus::from_i32(status) {
                Some(OakStatus::OK) => (),
                Some(err) => return err as i32,
                None => return OakStatus::OAK_STATUS_UNSPECIFIED.value(),
            }
        }

        let mut buf = Vec::<u8>::with_capacity(1024);
        grpc_in_channel.read_message(&mut buf).unwrap();
        if buf.is_empty() {
            info!("no pending message; poll again");
            continue;
        }
        let req: proto::grpc_encap::GrpcRequest = protobuf::parse_from_bytes(&buf).unwrap();
        if !req.last {
            panic!("Support for streaming requests not yet implemented");
        }
        node.invoke(
            &req.method_name,
            req.get_req_msg().value.as_slice(),
            &mut grpc_out_channel,
        );
    }
}

/// Install a panic hook so that panics are logged to the logging channel, if one is set.
/// See https://doc.rust-lang.org/std/panic/struct.PanicInfo.html.
fn set_panic_hook() {
    std::panic::set_hook(Box::new(|panic_info| {
        let msg = panic_info
            .payload()
            .downcast_ref::<&str>()
            .unwrap_or(&"<UNKWOWN MESSAGE>");
        let (file, line) = match panic_info.location() {
            Some(location) => (location.file(), location.line()),
            None => ("<UNKNOWN FILE>", 0),
        };
        error!(
            "panic occurred in file '{}' at line {}: {}",
            file, line, msg
        );
    }));
}
<commit_msg>Add Rust wrapper for wait_on_channels<commit_after>//
// Copyright 2018 The Project Oak Authors
//
// 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.
//

extern crate byteorder;
extern crate fmt;
#[macro_use]
extern crate log;
extern crate protobuf;

use byteorder::WriteBytesExt;
use proto::oak_api::OakStatus;
use protobuf::{Message, ProtobufEnum};
use std::io;
use std::io::Write;

pub mod proto;
pub mod storage;
#[cfg(test)]
mod tests;

#[cfg(test)]
#[macro_use]
extern crate assert_matches;

pub type GrpcResult<T> = Result<T, proto::status::Status>;

/// Trait to allow repeated writing of responses for server-streaming gRPC methods.
pub trait ResponseWriter<T: protobuf::Message> {
    fn write(&mut self, rsp: T);
}

/// Implementation of ResponseWriter that encapsulates response messages into
/// GrpcResponse wrapper messages and writes serialized versions to a (mutably
/// borrowed) send channel.
pub struct ChannelResponseWriter<'a> {
    pub channel: &'a mut SendChannelHalf,
}

impl<'a, T> ResponseWriter<T> for ChannelResponseWriter<'a>
where
    T: protobuf::Message,
{
    fn write(&mut self, rsp: T) {
        // Put the serialized response into a GrpcResponse message wrapper.
        let mut grpc_rsp = proto::grpc_encap::GrpcResponse::new();
        let mut any = protobuf::well_known_types::Any::new();
        rsp.write_to_writer(&mut any.value).unwrap();
        grpc_rsp.set_rsp_msg(any);
        // Serialize the GrpcResponse into the send channel.
        grpc_rsp.write_to_writer(&mut self.channel).unwrap();
    }
}

pub type Handle = u64;

/// Map an OakStatus to the nearest available std::io::Result.
fn result_from_status<T>(status: Option<OakStatus>, val: T) -> std::io::Result<T> {
    match status {
        Some(OakStatus::OAK_STATUS_UNSPECIFIED) => Err(io::Error::new(
            io::ErrorKind::Other,
            "Unspecified Oak status value",
        )),
        Some(OakStatus::OK) => Ok(val),
        Some(OakStatus::ERR_BAD_HANDLE) => {
            Err(io::Error::new(io::ErrorKind::NotConnected, "Bad handle"))
        }
        Some(OakStatus::ERR_INVALID_ARGS) => Err(io::Error::new(
            io::ErrorKind::InvalidInput,
            "Invalid arguments",
        )),
        Some(OakStatus::ERR_CHANNEL_CLOSED) => Err(io::Error::new(
            io::ErrorKind::ConnectionReset,
            "Channel closed",
        )),
        Some(OakStatus::ERR_BUFFER_TOO_SMALL) => Err(io::Error::new(
            io::ErrorKind::UnexpectedEof,
            "Buffer too small",
        )),
        Some(OakStatus::ERR_OUT_OF_RANGE) => {
            Err(io::Error::new(io::ErrorKind::NotConnected, "Out of range"))
        }
        Some(OakStatus::ERR_INTERNAL) => {
            Err(io::Error::new(io::ErrorKind::Other, "Internal error"))
        }
        None => Err(io::Error::new(
            io::ErrorKind::Other,
            "Unknown Oak status value",
        )),
    }
}

mod wasm {
    // See https://rustwasm.github.io/book/reference/js-ffi.html
    #[link(wasm_import_module = "oak")]
    extern "C" {
        pub fn wait_on_channels(buf: *mut u8, count: u32) -> i32;
        pub fn channel_read(handle: u64, buf: *mut u8, size: usize, actual_size: *mut u32) -> i32;
        pub fn channel_write(handle: u64, buf: *const u8, size: usize) -> i32;
        pub fn channel_find(buf: *const u8, len: usize) -> u64;
    }
}

pub fn channel_find(port_name: &str) -> Handle {
    unsafe { wasm::channel_find(port_name.as_ptr(), port_name.len()) }
}

pub const SPACE_BYTES_PER_HANDLE: usize = 9;

// Build a chunk of memory that is suitable for passing to wasm::wait_on_channels,
// holding the given collection of channel handles.
fn new_handle_space(handles: &[Handle]) -> Vec<u8> {
    let mut space = Vec::with_capacity(SPACE_BYTES_PER_HANDLE * handles.len());
    for handle in handles {
        space.write_u64::<byteorder::LittleEndian>(*handle).unwrap();
        space.push(0x00);
    }
    space
}

// Prepare a handle space for polling by clearing all of the message-pending
// indicator bytes.
fn prep_handle_space(space: &mut [u8]) {
    let count = space.len() / 8;
    for i in 0..count {
        space[i * SPACE_BYTES_PER_HANDLE + (SPACE_BYTES_PER_HANDLE - 1)] = 0;
    }
}

// Convenience wrapper around wait_on_channels host function. This version is
// easier to use in Rust but is less efficient (because the notification space
// is re-created on each invocation).
pub fn wait_on_channels(handles: &[Handle]) -> Result<Vec<Handle>, OakStatus> {
    let mut space = new_handle_space(handles);
    unsafe {
        let status = wasm::wait_on_channels(space.as_mut_ptr(), handles.len() as u32);
        match OakStatus::from_i32(status) {
            Some(OakStatus::OK) => (),
            Some(err) => return Err(err),
            None => return Err(OakStatus::OAK_STATUS_UNSPECIFIED),
        }
        let mut results = Vec::with_capacity(handles.len());
        for i in 0..handles.len() {
            if space[i * SPACE_BYTES_PER_HANDLE + (SPACE_BYTES_PER_HANDLE - 1)] != 0 {
                results.push(handles[i]);
            }
        }
        Ok(results)
    }
}

pub struct SendChannelHalf {
    handle: Handle,
}

impl SendChannelHalf {
    pub fn new(handle: Handle) -> SendChannelHalf {
        SendChannelHalf { handle }
    }

    pub fn write_message(&mut self, buf: &[u8]) -> std::io::Result<()> {
        result_from_status(
            OakStatus::from_i32(unsafe {
                wasm::channel_write(self.handle, buf.as_ptr(), buf.len())
            }),
            (),
        )
    }
}

// Implement the Write trait on a send channel for convenience, particularly for
// the logging channel.
impl Write for SendChannelHalf {
    fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
        match self.write_message(buf) {
            Ok(_) => Ok(buf.len()),
            Err(e) => Err(e),
        }
    }
    fn flush(&mut self) -> std::io::Result<()> {
        Ok(())
    }
}

pub fn logging_channel() -> impl Write {
    let logging_channel = SendChannelHalf::new(channel_find("log"));
    // Only flush logging channel on newlines.
    std::io::LineWriter::new(logging_channel)
}

pub struct ReceiveChannelHalf {
    handle: Handle,
}

impl ReceiveChannelHalf {
    pub fn new(handle: Handle) -> ReceiveChannelHalf {
        ReceiveChannelHalf { handle }
    }

    pub fn read_message(&mut self, buf: &mut Vec<u8>) -> std::io::Result<usize> {
        // Try reading from the channel twice: first with provided vector,
        // then with a vector that's been resized to meet size requirements.
        for resized in &[false, true] {
            let mut actual_size: u32 = 0;
            let status = OakStatus::from_i32(unsafe {
                wasm::channel_read(
                    self.handle,
                    buf.as_mut_ptr(),
                    buf.capacity(),
                    &mut actual_size,
                )
            });
            match status {
                Some(OakStatus::OK) => {
                    unsafe {
                        buf.set_len(actual_size as usize);
                    };
                    return Ok(actual_size as usize);
                }
                Some(OakStatus::ERR_BUFFER_TOO_SMALL) => {
                    if *resized {
                        return result_from_status(status, 0);
                    }
                    // Can escape the match if buffer is too small and !resized.
                }
                _ => return result_from_status(status, 0),
            }

            // Extend the vector to be large enough for the message
            debug!("Got {}, need {}", buf.capacity(), actual_size);
            if (actual_size as usize) < buf.len() {
                return Err(io::Error::new(
                    io::ErrorKind::Other,
                    format!(
                        "Internal error: provided {} bytes for receive, asked for {}",
                        buf.len(),
                        actual_size
                    ),
                ));
            }
            let extra = (actual_size as usize) - buf.len();
            buf.reserve(extra);
        }
        Err(io::Error::new(io::ErrorKind::Other, "Unreachable reached!"))
    }
}

/// Convenience wrapper for a send/receive pair of channels.
pub struct ChannelPair {
    pub receive: ReceiveChannelHalf,
    pub send: SendChannelHalf,
}

impl ChannelPair {
    pub fn new(in_handle: Handle, out_handle: Handle) -> ChannelPair {
        ChannelPair {
            receive: ReceiveChannelHalf::new(in_handle),
            send: SendChannelHalf::new(out_handle),
        }
    }
}

/// Trait encapsulating the operations required for an Oak Node.
pub trait OakNode {
    fn new() -> Self
    where
        Self: Sized;
    fn invoke(&mut self, method: &str, req: &[u8], out: &mut SendChannelHalf);
}

/// Perform a gRPC event loop on the given channels, invoking the given node.
pub fn grpc_event_loop<T: OakNode>(
    mut node: T,
    grpc_in_handle: Handle,
    grpc_out_handle: Handle,
) -> i32 {
    info!(
        "start event loop for node with handles in:{} out:{}",
        grpc_in_handle, grpc_out_handle
    );
    if grpc_in_handle == 0 || grpc_out_handle == 0 {
        return OakStatus::ERR_CHANNEL_CLOSED.value();
    }
    set_panic_hook();

    let read_handles = vec![grpc_in_handle];
    let mut space = new_handle_space(&read_handles);

    let mut grpc_in_channel = ReceiveChannelHalf::new(grpc_in_handle);
    let mut grpc_out_channel = SendChannelHalf::new(grpc_out_handle);
    loop {
        // Block until there is a message to read on an input channel.
        prep_handle_space(&mut space);
        unsafe {
            let status = wasm::wait_on_channels(space.as_mut_ptr(), read_handles.len() as u32);
            match OakStatus::from_i32(status) {
                Some(OakStatus::OK) => (),
                Some(err) => return err as i32,
                None => return OakStatus::OAK_STATUS_UNSPECIFIED.value(),
            }
        }

        let mut buf = Vec::<u8>::with_capacity(1024);
        grpc_in_channel.read_message(&mut buf).unwrap();
        if buf.is_empty() {
            info!("no pending message; poll again");
            continue;
        }
        let req: proto::grpc_encap::GrpcRequest = protobuf::parse_from_bytes(&buf).unwrap();
        if !req.last {
            panic!("Support for streaming requests not yet implemented");
        }
        node.invoke(
            &req.method_name,
            req.get_req_msg().value.as_slice(),
            &mut grpc_out_channel,
        );
    }
}

/// Install a panic hook so that panics are logged to the logging channel, if one is set.
/// See https://doc.rust-lang.org/std/panic/struct.PanicInfo.html.
fn set_panic_hook() {
    std::panic::set_hook(Box::new(|panic_info| {
        let msg = panic_info
            .payload()
            .downcast_ref::<&str>()
            .unwrap_or(&"<UNKWOWN MESSAGE>");
        let (file, line) = match panic_info.location() {
            Some(location) => (location.file(), location.line()),
            None => ("<UNKNOWN FILE>", 0),
        };
        error!(
            "panic occurred in file '{}' at line {}: {}",
            file, line, msg
        );
    }));
}
<|endoftext|>


<commit_before>use crate::error::{AuthErrorOr, Error};

use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};

/// Represents an access token returned by oauth2 servers. All access tokens are
/// Bearer tokens. Other types of tokens are not supported.
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Deserialize, Serialize)]
pub struct AccessToken {
    value: String,
    expires_at: Option<DateTime<Utc>>,
}

impl AccessToken {
    /// A string representation of the access token.
    pub fn as_str(&self) -> &str {
        &self.value
    }

    /// The time the access token will expire, if any.
    pub fn expiration_time(&self) -> Option<DateTime<Utc>> {
        self.expires_at
    }

    /// Determine if the access token is expired.
    /// This will report that the token is expired 1 minute prior to the
    /// expiration time to ensure that when the token is actually sent to the
    /// server it's still valid.
    pub fn is_expired(&self) -> bool {
        // Consider the token expired if it's within 1 minute of it's expiration
        // time.
        self.expires_at
            .map(|expiration_time| expiration_time - chrono::Duration::minutes(1) <= Utc::now())
            .unwrap_or(false)
    }
}

impl AsRef<str> for AccessToken {
    fn as_ref(&self) -> &str {
        self.as_str()
    }
}

impl From<TokenInfo> for AccessToken {
    fn from(value: TokenInfo) -> Self {
        AccessToken {
            value: value.access_token,
            expires_at: value.expires_at,
        }
    }
}

/// Represents a token as returned by OAuth2 servers.
///
/// It is produced by all authentication flows.
/// It authenticates certain operations, and must be refreshed once
/// it reached it's expiry date.
#[derive(Clone, PartialEq, Debug, Deserialize, Serialize)]
pub(crate) struct TokenInfo {
    /// used when authenticating calls to oauth2 enabled services.
    pub(crate) access_token: String,
    /// used to refresh an expired access_token.
    pub(crate) refresh_token: Option<String>,
    /// The time when the token expires.
    pub(crate) expires_at: Option<DateTime<Utc>>,
}

impl TokenInfo {
    pub(crate) fn from_json(json_data: &[u8]) -> Result<TokenInfo, Error> {
        #[derive(Deserialize)]
        struct RawToken {
            access_token: String,
            refresh_token: Option<String>,
            token_type: String,
            expires_in: Option<i64>,
        }

        let RawToken {
            access_token,
            refresh_token,
            token_type,
            expires_in,
        } = serde_json::from_slice::<AuthErrorOr<RawToken>>(json_data)?.into_result()?;

        if token_type.to_lowercase().as_str() != "bearer" {
            use std::io;
            return Err(io::Error::new(
                io::ErrorKind::InvalidData,
                format!(
                    r#"unknown token type returned; expected "bearer" found {}"#,
                    token_type
                ),
            )
            .into());
        }

        let expires_at = expires_in
            .map(|seconds_from_now| Utc::now() + chrono::Duration::seconds(seconds_from_now));

        Ok(TokenInfo {
            access_token,
            refresh_token,
            expires_at,
        })
    }

    /// Returns true if we are expired.
    pub fn is_expired(&self) -> bool {
        self.expires_at
            .map(|expiration_time| expiration_time - chrono::Duration::minutes(1) <= Utc::now())
            .unwrap_or(false)
    }
}

/// Represents either 'installed' or 'web' applications in a json secrets file.
/// See `ConsoleApplicationSecret` for more information
#[derive(Deserialize, Serialize, Clone, Default, Debug)]
pub struct ApplicationSecret {
    /// The client ID.
    pub client_id: String,
    /// The client secret.
    pub client_secret: String,
    /// The token server endpoint URI.
    pub token_uri: String,
    /// The authorization server endpoint URI.
    pub auth_uri: String,
    /// The redirect uris.
    pub redirect_uris: Vec<String>,
    /// Name of the google project the credentials are associated with
    pub project_id: Option<String>,
    /// The service account email associated with the client.
    pub client_email: Option<String>,
    /// The URL of the public x509 certificate, used to verify the signature on JWTs, such
    /// as ID tokens, signed by the authentication provider.
    pub auth_provider_x509_cert_url: Option<String>,
    ///  The URL of the public x509 certificate, used to verify JWTs signed by the client.
    pub client_x509_cert_url: Option<String>,
}

/// A type to facilitate reading and writing the json secret file
/// as returned by the [google developer console](https://code.google.com/apis/console)
#[derive(Deserialize, Serialize, Default, Debug)]
pub struct ConsoleApplicationSecret {
    /// web app secret
    pub web: Option<ApplicationSecret>,
    /// installed app secret
    pub installed: Option<ApplicationSecret>,
}

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

    pub const SECRET: &'static str =
        "{\"installed\":{\"auth_uri\":\"https://accounts.google.com/o/oauth2/auth\",\
         \"client_secret\":\"UqkDJd5RFwnHoiG5x5Rub8SI\",\"token_uri\":\"https://accounts.google.\
         com/o/oauth2/token\",\"client_email\":\"\",\"redirect_uris\":[\"urn:ietf:wg:oauth:2.0:\
         oob\",\"oob\"],\"client_x509_cert_url\":\"\",\"client_id\":\
         \"14070749909-vgip2f1okm7bkvajhi9jugan6126io9v.apps.googleusercontent.com\",\
         \"auth_provider_x509_cert_url\":\"https://www.googleapis.com/oauth2/v1/certs\"}}";

    #[test]
    fn console_secret() {
        use serde_json as json;
        match json::from_str::<ConsoleApplicationSecret>(SECRET) {
            Ok(s) => assert!(s.installed.is_some() && s.web.is_none()),
            Err(err) => panic!("Encountered error parsing ConsoleApplicationSecret: {}", err),
        }
    }
}
<commit_msg>Run rustfmt<commit_after>use crate::error::{AuthErrorOr, Error};

use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};

/// Represents an access token returned by oauth2 servers. All access tokens are
/// Bearer tokens. Other types of tokens are not supported.
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Deserialize, Serialize)]
pub struct AccessToken {
    value: String,
    expires_at: Option<DateTime<Utc>>,
}

impl AccessToken {
    /// A string representation of the access token.
    pub fn as_str(&self) -> &str {
        &self.value
    }

    /// The time the access token will expire, if any.
    pub fn expiration_time(&self) -> Option<DateTime<Utc>> {
        self.expires_at
    }

    /// Determine if the access token is expired.
    /// This will report that the token is expired 1 minute prior to the
    /// expiration time to ensure that when the token is actually sent to the
    /// server it's still valid.
    pub fn is_expired(&self) -> bool {
        // Consider the token expired if it's within 1 minute of it's expiration
        // time.
        self.expires_at
            .map(|expiration_time| expiration_time - chrono::Duration::minutes(1) <= Utc::now())
            .unwrap_or(false)
    }
}

impl AsRef<str> for AccessToken {
    fn as_ref(&self) -> &str {
        self.as_str()
    }
}

impl From<TokenInfo> for AccessToken {
    fn from(value: TokenInfo) -> Self {
        AccessToken {
            value: value.access_token,
            expires_at: value.expires_at,
        }
    }
}

/// Represents a token as returned by OAuth2 servers.
///
/// It is produced by all authentication flows.
/// It authenticates certain operations, and must be refreshed once
/// it reached it's expiry date.
#[derive(Clone, PartialEq, Debug, Deserialize, Serialize)]
pub(crate) struct TokenInfo {
    /// used when authenticating calls to oauth2 enabled services.
    pub(crate) access_token: String,
    /// used to refresh an expired access_token.
    pub(crate) refresh_token: Option<String>,
    /// The time when the token expires.
    pub(crate) expires_at: Option<DateTime<Utc>>,
}

impl TokenInfo {
    pub(crate) fn from_json(json_data: &[u8]) -> Result<TokenInfo, Error> {
        #[derive(Deserialize)]
        struct RawToken {
            access_token: String,
            refresh_token: Option<String>,
            token_type: String,
            expires_in: Option<i64>,
        }

        let RawToken {
            access_token,
            refresh_token,
            token_type,
            expires_in,
        } = serde_json::from_slice::<AuthErrorOr<RawToken>>(json_data)?.into_result()?;

        if token_type.to_lowercase().as_str() != "bearer" {
            use std::io;
            return Err(io::Error::new(
                io::ErrorKind::InvalidData,
                format!(
                    r#"unknown token type returned; expected "bearer" found {}"#,
                    token_type
                ),
            )
            .into());
        }

        let expires_at = expires_in
            .map(|seconds_from_now| Utc::now() + chrono::Duration::seconds(seconds_from_now));

        Ok(TokenInfo {
            access_token,
            refresh_token,
            expires_at,
        })
    }

    /// Returns true if we are expired.
    pub fn is_expired(&self) -> bool {
        self.expires_at
            .map(|expiration_time| expiration_time - chrono::Duration::minutes(1) <= Utc::now())
            .unwrap_or(false)
    }
}

/// Represents either 'installed' or 'web' applications in a json secrets file.
/// See `ConsoleApplicationSecret` for more information
#[derive(Deserialize, Serialize, Clone, Default, Debug)]
pub struct ApplicationSecret {
    /// The client ID.
    pub client_id: String,
    /// The client secret.
    pub client_secret: String,
    /// The token server endpoint URI.
    pub token_uri: String,
    /// The authorization server endpoint URI.
    pub auth_uri: String,
    /// The redirect uris.
    pub redirect_uris: Vec<String>,
    /// Name of the google project the credentials are associated with
    pub project_id: Option<String>,
    /// The service account email associated with the client.
    pub client_email: Option<String>,
    /// The URL of the public x509 certificate, used to verify the signature on JWTs, such
    /// as ID tokens, signed by the authentication provider.
    pub auth_provider_x509_cert_url: Option<String>,
    ///  The URL of the public x509 certificate, used to verify JWTs signed by the client.
    pub client_x509_cert_url: Option<String>,
}

/// A type to facilitate reading and writing the json secret file
/// as returned by the [google developer console](https://code.google.com/apis/console)
#[derive(Deserialize, Serialize, Default, Debug)]
pub struct ConsoleApplicationSecret {
    /// web app secret
    pub web: Option<ApplicationSecret>,
    /// installed app secret
    pub installed: Option<ApplicationSecret>,
}

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

    pub const SECRET: &'static str =
        "{\"installed\":{\"auth_uri\":\"https://accounts.google.com/o/oauth2/auth\",\
         \"client_secret\":\"UqkDJd5RFwnHoiG5x5Rub8SI\",\"token_uri\":\"https://accounts.google.\
         com/o/oauth2/token\",\"client_email\":\"\",\"redirect_uris\":[\"urn:ietf:wg:oauth:2.0:\
         oob\",\"oob\"],\"client_x509_cert_url\":\"\",\"client_id\":\
         \"14070749909-vgip2f1okm7bkvajhi9jugan6126io9v.apps.googleusercontent.com\",\
         \"auth_provider_x509_cert_url\":\"https://www.googleapis.com/oauth2/v1/certs\"}}";

    #[test]
    fn console_secret() {
        use serde_json as json;
        match json::from_str::<ConsoleApplicationSecret>(SECRET) {
            Ok(s) => assert!(s.installed.is_some() && s.web.is_none()),
            Err(err) => panic!(
                "Encountered error parsing ConsoleApplicationSecret: {}",
                err
            ),
        }
    }
}
<|endoftext|>