<commit_before>//! Keyboard utility functions; allow querying state of keyboard keys and modifiers.
//!
//! Example:
//!
//! ```rust, no-run
//! use ggez::event::{EventHandler, KeyCode, KeyMods};
//! use ggez::{graphics, keyboard, nalgebra as na, timer};
//! use ggez::{Context, GameResult};
//!
//! struct MainState {
//!     position_x: f32,
//! }
//!
//! impl EventHandler for MainState {
//!     fn update(&mut self, ctx: &mut Context) -> GameResult {
//!         // Increase or decrease `position_x` by 0.5, or by 5.0 if Shift is held.
//!         if keyboard::is_key_pressed(ctx, KeyCode::Right) {
//!             if keyboard::is_mod_active(ctx, KeyMods::SHIFT) {
//!                 self.position_x += 4.5;
//!             }
//!             self.position_x += 0.5;
//!         } else if keyboard::is_key_pressed(ctx, KeyCode::Left) {
//!             if keyboard::is_mod_active(ctx, KeyMods::SHIFT) {
//!                 self.position_x -= 4.5;
//!             }
//!             self.position_x -= 0.5;
//!         }
//!         Ok(())
//!     }
//!
//!     fn draw(&mut self, ctx: &mut Context) -> GameResult {
//!         graphics::clear(ctx, [0.1, 0.2, 0.3, 1.0].into());
//!         // Draw a circle at `position_x`.
//!         graphics::circle(
//!             ctx,
//!             graphics::WHITE,
//!             graphics::DrawMode::Fill,
//!             na::Point2::new(self.position_x, 380.0),
//!             100.0,
//!             2.0,
//!         )?;
//!         graphics::present(ctx)?;
//!         timer::yield_now();
//!         Ok(())
//!     }
//!
//!     fn key_down_event(&mut self, ctx: &mut Context, key: KeyCode, mods: KeyMods, _: bool) {
//!         match key {
//!             // Quit if Shift+Ctrl+Q is pressed.
//!             KeyCode::Q => {
//!                 if mods.contains(KeyMods::SHIFT | KeyMods::CTRL) {
//!                     println!("Terminating!");
//!                     ctx.quit();
//!                 } else if mods.contains(KeyMods::SHIFT) || mods.contains(KeyMods::CTRL) {
//!                     println!("You need to hold both Shift and Control to quit.");
//!                 } else {
//!                     println!("Now you're not even trying!");
//!                 }
//!             }
//!             _ => (),
//!         }
//!     }
//! }
//! ```

use context::Context;
use event::winit_event::ModifiersState;
use event::KeyCode;
use std::collections::VecDeque;

bitflags! {
    /// Bitflags describing state of keyboard modifiers, such as Control or Shift.
    #[derive(Default)]
    pub struct KeyMods: u8 {
        /// No modifiers; equivalent to `KeyMods::default()` and `KeyMods::empty()`.
        const NONE  = 0b00000000;
        /// Left or right Shift key.
        const SHIFT = 0b00000001;
        /// Left or right Control key.
        const CTRL  = 0b00000010;
        /// Left or right Alt key.
        const ALT   = 0b00000100;
        /// Left or right Win/Cmd/equivalent key.
        const LOGO  = 0b00001000;
    }
}

impl KeyMods {
    /// Amount of flags set (Kernighan/Wegner/Lehmer method).
    pub fn count(&self) -> usize {
        let mut num_set = 0;
        let mut bits = self.bits();
        loop {
            if num_set >= bits {
                break;
            }
            bits &= bits - 1;
            num_set += 1;
        }
        num_set as usize
    }
}

impl From<ModifiersState> for KeyMods {
    fn from(state: ModifiersState) -> Self {
        let mut keymod = KeyMods::empty();
        if state.shift {
            keymod |= Self::SHIFT;
        }
        if state.ctrl {
            keymod |= Self::CTRL;
        }
        if state.alt {
            keymod |= Self::ALT;
        }
        if state.logo {
            keymod |= Self::LOGO;
        }
        keymod
    }
}

/// Tracks held down keyboard keys, active keyboard modifiers,
/// and figures out if the system is sending repeat keystrokes.
#[derive(Clone, Debug)]
pub struct KeyboardContext {
    active_modifiers: KeyMods,
    pressed_keys: Vec<KeyCode>,
    last_pressed: VecDeque<KeyCode>,
}

impl KeyboardContext {
    pub(crate) fn new() -> Self {
        Self {
            active_modifiers: KeyMods::empty(),
            pressed_keys: Vec::with_capacity(16),
            last_pressed: VecDeque::with_capacity(3),
        }
    }

    pub(crate) fn set_key(&mut self, key: KeyCode, pressed: bool) {
        if pressed {
            if !self.pressed_keys.contains(&key) {
                self.pressed_keys.push(key);
            }

            self.last_pressed.push_back(key);
            if self.last_pressed.len() > 2 {
                self.last_pressed.pop_front();
            }
        } else {
            if let Some(i) = self
                .pressed_keys
                .iter()
                .enumerate()
                .find(|(_i, pressed_key)| **pressed_key == key)
                .map(|(i, _)| i)
            {
                self.pressed_keys.swap_remove(i);
            }

            self.last_pressed.clear();

            // This ensures `active_modifiers` are correct in repeated keystroke edge cases.
            match key {
                KeyCode::LShift | KeyCode::RShift => self.active_modifiers -= KeyMods::SHIFT,
                KeyCode::LControl | KeyCode::RControl => self.active_modifiers -= KeyMods::CTRL,
                KeyCode::LAlt | KeyCode::RAlt => self.active_modifiers -= KeyMods::ALT,
                KeyCode::LWin | KeyCode::RWin => self.active_modifiers -= KeyMods::LOGO,
                _ => (),
            }
        }
    }

    pub(crate) fn set_modifiers(&mut self, keymods: KeyMods) {
        self.active_modifiers = keymods;
    }

    pub(crate) fn is_key_pressed(&self, key: KeyCode) -> bool {
        self.pressed_keys.contains(&key)
    }

    pub(crate) fn is_key_repeated(&self) -> bool {
        if let Some(key1) = self.last_pressed.get(0) {
            if let Some(key2) = self.last_pressed.get(1) {
                return key1 == key2;
            }
        }
        false
    }

    pub(crate) fn get_pressed_keys(&self) -> &[KeyCode] {
        &self.pressed_keys
    }

    pub(crate) fn get_active_mods(&self) -> KeyMods {
        self.active_modifiers
    }
}

impl Default for KeyboardContext {
    fn default() -> Self {
        Self::new()
    }
}

/// Checks if a key is currently pressed down.
pub fn is_key_pressed(ctx: &Context, key: KeyCode) -> bool {
    ctx.keyboard_context.is_key_pressed(key)
}

/// Checks if the last keystroke sent by the system is repeated,
/// like when a key is held down for a period of time.
pub fn is_key_repeated(ctx: &Context) -> bool {
    ctx.keyboard_context.is_key_repeated()
}

/// Returns a slice with currently held keys.
pub fn get_pressed_keys(ctx: &Context) -> &[KeyCode] {
    ctx.keyboard_context.get_pressed_keys()
}

/// Checks if keyboard modifier (or several) is active.
pub fn is_mod_active(ctx: &Context, keymods: KeyMods) -> bool {
    ctx.keyboard_context.get_active_mods().contains(keymods)
}

/// Returns currently active keyboard modifiers.
pub fn get_active_mods(ctx: &Context) -> KeyMods {
    ctx.keyboard_context.get_active_mods()
}

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

    #[test]
    fn key_mod_conversions() {
        assert_eq!(
            KeyMods::empty(),
            KeyMods::from(ModifiersState {
                shift: false,
                ctrl: false,
                alt: false,
                logo: false,
            })
        );
        assert_eq!(
            KeyMods::SHIFT,
            KeyMods::from(ModifiersState {
                shift: true,
                ctrl: false,
                alt: false,
                logo: false,
            })
        );
        assert_eq!(
            KeyMods::SHIFT | KeyMods::ALT,
            KeyMods::from(ModifiersState {
                shift: true,
                ctrl: false,
                alt: true,
                logo: false,
            })
        );
        assert_eq!(
            KeyMods::SHIFT | KeyMods::ALT | KeyMods::CTRL,
            KeyMods::from(ModifiersState {
                shift: true,
                ctrl: true,
                alt: true,
                logo: false,
            })
        );
        assert_eq!(
            KeyMods::SHIFT - KeyMods::ALT,
            KeyMods::from(ModifiersState {
                shift: true,
                ctrl: false,
                alt: false,
                logo: false,
            })
        );
        assert_eq!(
            (KeyMods::SHIFT | KeyMods::ALT) - KeyMods::ALT,
            KeyMods::from(ModifiersState {
                shift: true,
                ctrl: false,
                alt: false,
                logo: false,
            })
        );
        assert_eq!(
            KeyMods::SHIFT - (KeyMods::ALT | KeyMods::SHIFT),
            KeyMods::from(ModifiersState {
                shift: false,
                ctrl: false,
                alt: false,
                logo: false,
            })
        );
    }

    #[test]
    fn key_mod_set_bit_count() {
        assert_eq!(KeyMods::empty().count(), 0);
        assert_eq!(KeyMods::SHIFT.count(), 1);
        assert_eq!(KeyMods::CTRL.count(), 1);
        assert_eq!(KeyMods::ALT.count(), 1);
        assert_eq!(KeyMods::LOGO.count(), 1);
        assert_eq!((KeyMods::SHIFT | KeyMods::CTRL).count(), 2);
        assert_eq!((KeyMods::SHIFT | KeyMods::LOGO).count(), 2);
        assert_eq!((KeyMods::LOGO | KeyMods::SHIFT).count(), 2);
        assert_eq!((KeyMods::LOGO | KeyMods::SHIFT | KeyMods::ALT).count(), 3);
        assert_eq!((!KeyMods::ALT).count(), 3);
    }

    #[test]
    fn pressed_keys_tracking() {
        let mut keyboard = KeyboardContext::new();
        assert_eq!(keyboard.get_pressed_keys(), &[]);
        keyboard.set_key(KeyCode::A, true);
        assert_eq!(keyboard.get_pressed_keys(), &[KeyCode::A]);
        keyboard.set_key(KeyCode::A, false);
        assert_eq!(keyboard.get_pressed_keys(), &[]);
        keyboard.set_key(KeyCode::A, true);
        assert_eq!(keyboard.get_pressed_keys(), &[KeyCode::A]);
        keyboard.set_key(KeyCode::A, true);
        assert_eq!(keyboard.get_pressed_keys(), &[KeyCode::A]);
        keyboard.set_key(KeyCode::B, true);
        assert_eq!(keyboard.get_pressed_keys(), &[KeyCode::A, KeyCode::B]);
        keyboard.set_key(KeyCode::B, true);
        assert_eq!(keyboard.get_pressed_keys(), &[KeyCode::A, KeyCode::B]);
        keyboard.set_key(KeyCode::A, false);
        assert_eq!(keyboard.get_pressed_keys(), &[KeyCode::B]);
        keyboard.set_key(KeyCode::A, false);
        assert_eq!(keyboard.get_pressed_keys(), &[KeyCode::B]);
        keyboard.set_key(KeyCode::B, false);
        assert_eq!(keyboard.get_pressed_keys(), &[]);
    }

    #[test]
    fn repeated_keys_tracking() {
        let mut keyboard = KeyboardContext::new();
        assert_eq!(keyboard.is_key_repeated(), false);
        keyboard.set_key(KeyCode::A, true);
        assert_eq!(keyboard.is_key_repeated(), false);
        keyboard.set_key(KeyCode::A, false);
        assert_eq!(keyboard.is_key_repeated(), false);
        keyboard.set_key(KeyCode::A, true);
        assert_eq!(keyboard.is_key_repeated(), false);
        keyboard.set_key(KeyCode::A, true);
        assert_eq!(keyboard.is_key_repeated(), true);
        keyboard.set_key(KeyCode::A, false);
        assert_eq!(keyboard.is_key_repeated(), false);
        keyboard.set_key(KeyCode::A, true);
        assert_eq!(keyboard.is_key_repeated(), false);
        keyboard.set_key(KeyCode::B, true);
        assert_eq!(keyboard.is_key_repeated(), false);
        keyboard.set_key(KeyCode::A, true);
        assert_eq!(keyboard.is_key_repeated(), false);
        keyboard.set_key(KeyCode::A, true);
        assert_eq!(keyboard.is_key_repeated(), true);
        keyboard.set_key(KeyCode::B, true);
        assert_eq!(keyboard.is_key_repeated(), false);
        keyboard.set_key(KeyCode::B, true);
        assert_eq!(keyboard.is_key_repeated(), true);
    }
}
<commit_msg>Minor doc fix, minor test improvement.<commit_after>//! Keyboard utility functions; allow querying state of keyboard keys and modifiers.
//!
//! Example:
//!
//! ```rust, no-run
//! use ggez::event::{EventHandler, KeyCode, KeyMods};
//! use ggez::{graphics, keyboard, nalgebra as na, timer};
//! use ggez::{Context, GameResult};
//!
//! struct MainState {
//!     position_x: f32,
//! }
//!
//! impl EventHandler for MainState {
//!     fn update(&mut self, ctx: &mut Context) -> GameResult {
//!         // Increase or decrease `position_x` by 0.5, or by 5.0 if Shift is held.
//!         if keyboard::is_key_pressed(ctx, KeyCode::Right) {
//!             if keyboard::is_mod_active(ctx, KeyMods::SHIFT) {
//!                 self.position_x += 4.5;
//!             }
//!             self.position_x += 0.5;
//!         } else if keyboard::is_key_pressed(ctx, KeyCode::Left) {
//!             if keyboard::is_mod_active(ctx, KeyMods::SHIFT) {
//!                 self.position_x -= 4.5;
//!             }
//!             self.position_x -= 0.5;
//!         }
//!         Ok(())
//!     }
//!
//!     fn draw(&mut self, ctx: &mut Context) -> GameResult {
//!         graphics::clear(ctx, [0.1, 0.2, 0.3, 1.0].into());
//!         // Draw a circle at `position_x`.
//!         graphics::circle(
//!             ctx,
//!             graphics::WHITE,
//!             graphics::DrawMode::Fill,
//!             na::Point2::new(self.position_x, 380.0),
//!             100.0,
//!             2.0,
//!         )?;
//!         graphics::present(ctx)?;
//!         timer::yield_now();
//!         Ok(())
//!     }
//!
//!     fn key_down_event(&mut self, ctx: &mut Context, key: KeyCode, mods: KeyMods, _: bool) {
//!         match key {
//!             // Quit if Shift+Ctrl+Q is pressed.
//!             KeyCode::Q => {
//!                 if mods.contains(KeyMods::SHIFT | KeyMods::CTRL) {
//!                     println!("Terminating!");
//!                     ctx.quit();
//!                 } else if mods.contains(KeyMods::SHIFT) || mods.contains(KeyMods::CTRL) {
//!                     println!("You need to hold both Shift and Control to quit.");
//!                 } else {
//!                     println!("Now you're not even trying!");
//!                 }
//!             }
//!             _ => (),
//!         }
//!     }
//! }
//! ```

use context::Context;
use event::winit_event::ModifiersState;
use event::KeyCode;
use std::collections::VecDeque;

bitflags! {
    /// Bitflags describing state of keyboard modifiers, such as Control or Shift.
    #[derive(Default)]
    pub struct KeyMods: u8 {
        /// No modifiers; equivalent to `KeyMods::default()` and `KeyMods::empty()`.
        const NONE  = 0b00000000;
        /// Left or right Shift key.
        const SHIFT = 0b00000001;
        /// Left or right Control key.
        const CTRL  = 0b00000010;
        /// Left or right Alt key.
        const ALT   = 0b00000100;
        /// Left or right Win/Cmd/equivalent key.
        const LOGO  = 0b00001000;
    }
}

impl KeyMods {
    /// Amount of flags set (Kernighan/Wegner/Lehmer method).
    pub fn count(&self) -> usize {
        let mut num_set = 0;
        let mut bits = self.bits();
        loop {
            if num_set >= bits {
                break;
            }
            bits &= bits - 1;
            num_set += 1;
        }
        num_set as usize
    }
}

impl From<ModifiersState> for KeyMods {
    fn from(state: ModifiersState) -> Self {
        let mut keymod = KeyMods::empty();
        if state.shift {
            keymod |= Self::SHIFT;
        }
        if state.ctrl {
            keymod |= Self::CTRL;
        }
        if state.alt {
            keymod |= Self::ALT;
        }
        if state.logo {
            keymod |= Self::LOGO;
        }
        keymod
    }
}

/// Tracks held down keyboard keys, active keyboard modifiers,
/// and figures out if the system is sending repeat keystrokes.
#[derive(Clone, Debug)]
pub struct KeyboardContext {
    active_modifiers: KeyMods,
    pressed_keys: Vec<KeyCode>,
    last_pressed: VecDeque<KeyCode>,
}

impl KeyboardContext {
    pub(crate) fn new() -> Self {
        Self {
            active_modifiers: KeyMods::empty(),
            pressed_keys: Vec::with_capacity(16),
            last_pressed: VecDeque::with_capacity(3),
        }
    }

    pub(crate) fn set_key(&mut self, key: KeyCode, pressed: bool) {
        if pressed {
            if !self.pressed_keys.contains(&key) {
                self.pressed_keys.push(key);
            }

            self.last_pressed.push_back(key);
            if self.last_pressed.len() > 2 {
                self.last_pressed.pop_front();
            }
        } else {
            if let Some(i) = self
                .pressed_keys
                .iter()
                .enumerate()
                .find(|(_i, pressed_key)| **pressed_key == key)
                .map(|(i, _)| i)
            {
                self.pressed_keys.swap_remove(i);
            }

            self.last_pressed.clear();

            // This ensures `active_modifiers` are correct in repeated keystroke edge cases.
            match key {
                KeyCode::LShift | KeyCode::RShift => self.active_modifiers -= KeyMods::SHIFT,
                KeyCode::LControl | KeyCode::RControl => self.active_modifiers -= KeyMods::CTRL,
                KeyCode::LAlt | KeyCode::RAlt => self.active_modifiers -= KeyMods::ALT,
                KeyCode::LWin | KeyCode::RWin => self.active_modifiers -= KeyMods::LOGO,
                _ => (),
            }
        }
    }

    pub(crate) fn set_modifiers(&mut self, keymods: KeyMods) {
        self.active_modifiers = keymods;
    }

    pub(crate) fn is_key_pressed(&self, key: KeyCode) -> bool {
        self.pressed_keys.contains(&key)
    }

    pub(crate) fn is_key_repeated(&self) -> bool {
        if let Some(key1) = self.last_pressed.get(0) {
            if let Some(key2) = self.last_pressed.get(1) {
                return key1 == key2;
            }
        }
        false
    }

    pub(crate) fn get_pressed_keys(&self) -> &[KeyCode] {
        &self.pressed_keys
    }

    pub(crate) fn get_active_mods(&self) -> KeyMods {
        self.active_modifiers
    }
}

impl Default for KeyboardContext {
    fn default() -> Self {
        Self::new()
    }
}

/// Checks if a key is currently pressed down.
pub fn is_key_pressed(ctx: &Context, key: KeyCode) -> bool {
    ctx.keyboard_context.is_key_pressed(key)
}

/// Checks if the last keystroke sent by the system is repeated,
/// like when a key is held down for a period of time.
pub fn is_key_repeated(ctx: &Context) -> bool {
    ctx.keyboard_context.is_key_repeated()
}

/// Returns a slice with currently pressed down keys.
pub fn get_pressed_keys(ctx: &Context) -> &[KeyCode] {
    ctx.keyboard_context.get_pressed_keys()
}

/// Checks if keyboard modifier (or several) is active.
pub fn is_mod_active(ctx: &Context, keymods: KeyMods) -> bool {
    ctx.keyboard_context.get_active_mods().contains(keymods)
}

/// Returns currently active keyboard modifiers.
pub fn get_active_mods(ctx: &Context) -> KeyMods {
    ctx.keyboard_context.get_active_mods()
}

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

    #[test]
    fn key_mod_conversions() {
        assert_eq!(
            KeyMods::empty(),
            KeyMods::from(ModifiersState {
                shift: false,
                ctrl: false,
                alt: false,
                logo: false,
            })
        );
        assert_eq!(
            KeyMods::SHIFT,
            KeyMods::from(ModifiersState {
                shift: true,
                ctrl: false,
                alt: false,
                logo: false,
            })
        );
        assert_eq!(
            KeyMods::SHIFT | KeyMods::ALT,
            KeyMods::from(ModifiersState {
                shift: true,
                ctrl: false,
                alt: true,
                logo: false,
            })
        );
        assert_eq!(
            KeyMods::SHIFT | KeyMods::ALT | KeyMods::CTRL,
            KeyMods::from(ModifiersState {
                shift: true,
                ctrl: true,
                alt: true,
                logo: false,
            })
        );
        assert_eq!(
            KeyMods::SHIFT - KeyMods::ALT,
            KeyMods::from(ModifiersState {
                shift: true,
                ctrl: false,
                alt: false,
                logo: false,
            })
        );
        assert_eq!(
            (KeyMods::SHIFT | KeyMods::ALT) - KeyMods::ALT,
            KeyMods::from(ModifiersState {
                shift: true,
                ctrl: false,
                alt: false,
                logo: false,
            })
        );
        assert_eq!(
            KeyMods::SHIFT - (KeyMods::ALT | KeyMods::SHIFT),
            KeyMods::from(ModifiersState {
                shift: false,
                ctrl: false,
                alt: false,
                logo: false,
            })
        );
    }

    #[test]
    fn key_mod_set_bit_count() {
        assert_eq!(KeyMods::empty().count(), 0);
        assert_eq!(KeyMods::SHIFT.count(), 1);
        assert_eq!(KeyMods::CTRL.count(), 1);
        assert_eq!(KeyMods::ALT.count(), 1);
        assert_eq!(KeyMods::LOGO.count(), 1);
        assert_eq!((KeyMods::SHIFT | KeyMods::CTRL).count(), 2);
        assert_eq!((KeyMods::SHIFT | KeyMods::LOGO).count(), 2);
        assert_eq!((KeyMods::LOGO | KeyMods::SHIFT).count(), 2);
        assert_eq!((KeyMods::LOGO | KeyMods::SHIFT | KeyMods::ALT).count(), 3);
        assert_eq!((!KeyMods::ALT).count(), 3);
    }

    #[test]
    fn pressed_keys_tracking() {
        let mut keyboard = KeyboardContext::new();
        assert_eq!(keyboard.get_pressed_keys(), &[]);
        assert!(!keyboard.is_key_pressed(KeyCode::A));
        keyboard.set_key(KeyCode::A, true);
        assert_eq!(keyboard.get_pressed_keys(), &[KeyCode::A]);
        assert!(keyboard.is_key_pressed(KeyCode::A));
        keyboard.set_key(KeyCode::A, false);
        assert_eq!(keyboard.get_pressed_keys(), &[]);
        assert!(!keyboard.is_key_pressed(KeyCode::A));
        keyboard.set_key(KeyCode::A, true);
        assert_eq!(keyboard.get_pressed_keys(), &[KeyCode::A]);
        assert!(keyboard.is_key_pressed(KeyCode::A));
        keyboard.set_key(KeyCode::A, true);
        assert_eq!(keyboard.get_pressed_keys(), &[KeyCode::A]);
        keyboard.set_key(KeyCode::B, true);
        assert_eq!(keyboard.get_pressed_keys(), &[KeyCode::A, KeyCode::B]);
        keyboard.set_key(KeyCode::B, true);
        assert_eq!(keyboard.get_pressed_keys(), &[KeyCode::A, KeyCode::B]);
        keyboard.set_key(KeyCode::A, false);
        assert_eq!(keyboard.get_pressed_keys(), &[KeyCode::B]);
        keyboard.set_key(KeyCode::A, false);
        assert_eq!(keyboard.get_pressed_keys(), &[KeyCode::B]);
        keyboard.set_key(KeyCode::B, false);
        assert_eq!(keyboard.get_pressed_keys(), &[]);
    }

    #[test]
    fn repeated_keys_tracking() {
        let mut keyboard = KeyboardContext::new();
        assert_eq!(keyboard.is_key_repeated(), false);
        keyboard.set_key(KeyCode::A, true);
        assert_eq!(keyboard.is_key_repeated(), false);
        keyboard.set_key(KeyCode::A, false);
        assert_eq!(keyboard.is_key_repeated(), false);
        keyboard.set_key(KeyCode::A, true);
        assert_eq!(keyboard.is_key_repeated(), false);
        keyboard.set_key(KeyCode::A, true);
        assert_eq!(keyboard.is_key_repeated(), true);
        keyboard.set_key(KeyCode::A, false);
        assert_eq!(keyboard.is_key_repeated(), false);
        keyboard.set_key(KeyCode::A, true);
        assert_eq!(keyboard.is_key_repeated(), false);
        keyboard.set_key(KeyCode::B, true);
        assert_eq!(keyboard.is_key_repeated(), false);
        keyboard.set_key(KeyCode::A, true);
        assert_eq!(keyboard.is_key_repeated(), false);
        keyboard.set_key(KeyCode::A, true);
        assert_eq!(keyboard.is_key_repeated(), true);
        keyboard.set_key(KeyCode::B, true);
        assert_eq!(keyboard.is_key_repeated(), false);
        keyboard.set_key(KeyCode::B, true);
        assert_eq!(keyboard.is_key_repeated(), true);
    }
}
<|endoftext|>


<commit_before>// Copyright (c) 2014 Richard Diamond & contributors.
//
// This file is part of the Rust PPApi project.
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

use super::{Callback, Resource, Instance, ToStringVar, ToVar, Code,
            StringVar, GenericResource, ResourceType, BlockUntilComplete,
            Var};
use super::ppb::{get_url_loader, get_url_loader_opt,
                 get_url_request_opt,
                 get_url_response};
use ppb::{self, URLRequestInfoIf, URLResponseInfoIf, URLLoaderIf};

use std::str::FromStr;
use std::ops::Deref;

use collections::enum_set::{CLike, EnumSet};

use hyper;
use hyper::header::Headers;
use httparse;

use super::ffi;
use super::ffi::bool_to_var;
use iurl::Url;

use fs::{SliceIo, FileIo, FileView};

#[derive(Hash, Eq, PartialEq, Debug)] pub struct UrlRequestInfo(ffi::PP_Resource);
#[derive(Hash, Eq, PartialEq, Debug)] pub struct ResponseInfo(ffi::PP_Resource);

impl_resource_for!(UrlRequestInfo, ResourceType::UrlRequestInfo);
impl_resource_for!(ResponseInfo, ResourceType::UrlResponseInfo);
impl_clone_drop_for!(UrlRequestInfo);
impl_clone_drop_for!(ResponseInfo);

pub type RequestProperties = EnumSet<RequestProperties_>;
#[derive(Eq, PartialEq, Clone, Hash, Copy, Debug)]
pub enum RequestProperties_ {
    AllowCrossOriginRequests,
    AllowCredentials,
    RecordUploadProgress,
    RecordDownloadProgress,
    FollowRedirects,
    StreamToFile,
}
impl CLike for RequestProperties_ {
    fn to_usize(&self) -> usize {
        match self {
            &RequestProperties_::AllowCrossOriginRequests => 0,
            &RequestProperties_::AllowCredentials => 1,
            &RequestProperties_::RecordUploadProgress => 2,
            &RequestProperties_::RecordDownloadProgress => 3,
            &RequestProperties_::FollowRedirects => 4,
            &RequestProperties_::StreamToFile => 5,
        }
    }
    fn from_usize(v: usize) -> RequestProperties_ {
        match v {
            0 => RequestProperties_::AllowCrossOriginRequests,
            1 => RequestProperties_::AllowCredentials,
            2 => RequestProperties_::RecordUploadProgress,
            3 => RequestProperties_::RecordDownloadProgress,
            4 => RequestProperties_::FollowRedirects,
            5 => RequestProperties_::StreamToFile,
            _ => unreachable!(),
        }
    }
}
impl Into<ffi::PP_URLRequestProperty> for RequestProperties_ {
    fn into(self) -> ffi::PP_URLRequestProperty {
        match self {
            RequestProperties_::AllowCrossOriginRequests =>
                ffi::PP_URLREQUESTPROPERTY_ALLOWCROSSORIGINREQUESTS,
            RequestProperties_::AllowCredentials =>
                ffi::PP_URLREQUESTPROPERTY_ALLOWCREDENTIALS,
            RequestProperties_::RecordUploadProgress =>
                ffi::PP_URLREQUESTPROPERTY_RECORDUPLOADPROGRESS,
            RequestProperties_::RecordDownloadProgress =>
                ffi::PP_URLREQUESTPROPERTY_RECORDDOWNLOADPROGRESS,
            RequestProperties_::FollowRedirects =>
                ffi::PP_URLREQUESTPROPERTY_FOLLOWREDIRECTS,
            RequestProperties_::StreamToFile =>
                ffi::PP_URLREQUESTPROPERTY_STREAMTOFILE,
        }
    }
}

#[derive(Clone, Debug)]
pub enum Body {
    File(SliceIo<FileIo>, Option<super::Time>),
    Blob(Vec<u8>),
}

pub type Method = hyper::method::Method;

trait CreateUrl {
    fn create_url_loader(&self) -> Code<ffi::PP_Resource>;
    fn create_url_request_info(&self) -> Code<UrlRequestInfo>;
}
impl CreateUrl for Instance {
    fn create_url_loader(&self) -> Code<ffi::PP_Resource> {
        get_url_loader_opt()
            .map(|i| {
                i.create(self.unwrap())
                    .map(|info| Code::Ok(info) )
                    .unwrap_or(Code::BadInstance)
            })
            .unwrap_or(Code::NoInterface)
    }
    fn create_url_request_info(&self) -> Code<UrlRequestInfo> {
        get_url_request_opt()
            .map(|i| {
                i.create(self.unwrap())
                    .map(|info| Code::Ok(UrlRequestInfo::new(info)) )
                    .unwrap_or(Code::BadInstance)
            })
            .unwrap_or(Code::NoInterface)
    }
}

#[derive(Clone, Debug)]
pub struct RequestInfo {
    pub url: Url,

    pub method: Method,

    pub prefetch_buffer: Option<(i32, i32)>,
    properties: RequestProperties,
    set_props:  RequestProperties,

    pub bodies: Vec<Body>,

    pub headers: Headers,
}
impl RequestInfo {
    fn clear_bit(bitfield: RequestProperties, prop: RequestProperties_) -> RequestProperties {
        let mut new = EnumSet::new();
        for p in bitfield
            .iter()
            .filter(|&p| p != prop ) {
                new.insert(p);
            }
        new
    }
    pub fn set_prop(&mut self, prop: RequestProperties_, bit: bool) {
        self.set_props.insert(prop);
        self.set_prop_value(prop, bit);
    }
    // unsets the 'this property is set' bit. Doesn't clear the actual property value bit.
    pub fn unset_prop(&mut self, prop: RequestProperties_) -> bool {
        let was_set = self.set_props.contains(&prop);
        self.set_props = RequestInfo::clear_bit(self.set_props, prop);
        was_set
    }

    // set the property value, but don't set the 'this property is set' bit.
    pub fn set_prop_value(&mut self, prop: RequestProperties_, bit: bool) -> bool {
        let mut new = RequestInfo::clear_bit(self.properties, prop);
        if bit {
            new.insert(prop);
        }

        let was_set = self.properties.contains(&prop);
        self.properties = new;
        was_set
    }

}
impl Into<Code<UrlRequestInfo>> for (Instance, RequestInfo) {
    fn into(self) -> Code<UrlRequestInfo> {
        let (instance, this) = self;

        let request = get_url_request_opt();
        if request.is_none() { return Code::NoInterface; }
        let request = request.unwrap();

        let res = try_code!(instance.create_url_request_info());

        let set_true = this.set_props.intersection(this.properties);
        let set_false = this.set_props - set_true;

        let true_var = unsafe { bool_to_var(1) };
        let false_var = unsafe { bool_to_var(0) };

        for prop in set_true.iter() {
            let set = request.property(res.unwrap(), prop.into(), true_var);
            if !set { return Code::Failed; }
        }
        for prop in set_false.iter() {
            let set = request.property(res.unwrap(), prop.into(), false_var);
            if !set { return Code::Failed; }
        }
        let RequestInfo {
            bodies, headers, url, method, ..
        } = this;
        for body in bodies.into_iter() {
            let success = match body {
                Body::File(ref slice, time) => {
                    request.append_file_to_body(res.unwrap(),
                                                slice.unwrap(),
                                                Some(slice.view_start() as i64),
                                                slice.view_len().map(|v| v as i64 ),
                                                time)
                }
                Body::Blob(ref blob) => {
                    request.append_blob_to_body(res.unwrap(),
                                                blob)
                }
            };
            if !success { return Code::Failed; }
        }
        let headers_str = headers.to_string();
        let headers_str = headers_str.to_string_var();
        let success = request.property(res.unwrap(),
                                       ffi::PP_URLREQUESTPROPERTY_HEADERS,
                                       headers_str.to_var());

        let url = url.to_string().to_string_var();
        let success = success && request.property(res.unwrap(),
                                                  ffi::PP_URLREQUESTPROPERTY_URL,
                                                  url.to_var());

        let method = method.to_string().to_string_var();
        let success = success && request.property(res.unwrap(),
                                                  ffi::PP_URLREQUESTPROPERTY_METHOD,
                                                  method.to_var());
        if !success { return Code::Failed; }
        Code::Ok(res)
    }
}

impl ResponseInfo {
    pub fn raw_url(&self) -> StringVar {
        let v = get_url_response()
            .property(self.unwrap(), ffi::PP_URLRESPONSEPROPERTY_URL);
        assert!(v.is_a_string());

        From::from(v)
    }
    pub fn url(&self) -> Url {
        let v = self.raw_url();
        FromStr::from_str(v.as_ref())
            .unwrap()
    }

    pub fn raw_redirect_url(&self) -> StringVar {
        let v = get_url_response()
            .property(self.unwrap(), ffi::PP_URLRESPONSEPROPERTY_REDIRECTURL);
        assert!(v.is_a_string());

        From::from(v)
    }
    pub fn redirect_url(&self) -> Option<Url> {
        let v = self.raw_redirect_url();
        if v == "" {
            None
        } else {
            Some(FromStr::from_str(v.as_ref()).unwrap())
        }
    }

    pub fn raw_redirect_method(&self) -> StringVar {
        let v = get_url_response()
            .property(self.unwrap(), ffi::PP_URLRESPONSEPROPERTY_REDIRECTMETHOD);
        assert!(v.is_a_string());

        From::from(v)
    }
    pub fn redirect_method(&self) -> Option<Method> {
        let v = get_url_response()
            .property(self.unwrap(), ffi::PP_URLRESPONSEPROPERTY_REDIRECTMETHOD);
        debug_assert!(v.is_a_string());

        let v = self.raw_redirect_method();
        if v == "" {
            None
        } else {
            Some(FromStr::from_str(v.as_ref()).unwrap())
        }
    }

    pub fn raw_status_code(&self) -> u16 {
        let v = get_url_response()
            .property(self.unwrap(), ffi::PP_URLRESPONSEPROPERTY_STATUSCODE);
        debug_assert!(v.is_an_i32());

        let v = unsafe { ffi::i32_from_var(v) };
        v as u16
    }
    pub fn status_code(&self) -> hyper::status::StatusCode {
        let v = self.raw_status_code();
        hyper::status::StatusCode::from_u16(v)
    }

    /// Returns a newline delimited string of `key: value` pairs.
    pub fn raw_headers_str<'a>(&'a self) -> &'a str {
        use std::str::from_utf8_unchecked;
        use std::slice::from_raw_parts;
        use std::mem::{transmute, uninitialized};

        let v = get_url_response()
            .property(self.unwrap(), ffi::PP_URLRESPONSEPROPERTY_HEADERS);
        assert!(v.is_a_string());

        let f = ppb::get_var().VarToUtf8.unwrap();

        unsafe {
            let mut len: u32 = uninitialized();
            let buf = f(v, &mut len as *mut u32);
            let len = len as usize;
            let slice = from_raw_parts(transmute(&buf), len);
            transmute(from_utf8_unchecked(slice))
        }
    }
    pub fn raw_headers_var(&self) -> StringVar {
        let v = get_url_response()
            .property(self.unwrap(), ffi::PP_URLRESPONSEPROPERTY_HEADERS);
        assert!(v.is_a_string());
        From::from(v)
    }

    pub fn headers(&self) -> Headers {
        let mut headers = Vec::with_capacity(5);
        let mut offset = 0;
        let raw_headers: &[u8] = self.raw_headers_str().as_ref();
        loop {
            headers.reserve(1);
            let current_len = headers.len();
            let current_cap = headers.capacity();
            unsafe { headers.set_len(current_cap) };
            let (bytes, parsed_len) = {
                let (b, p) =
                    httparse::parse_headers(&raw_headers[offset..],
                                            &mut headers[current_len..current_cap])
                    .unwrap()
                    .unwrap();
                (b, p.len())
            };
            unsafe { headers.set_len(current_len + parsed_len) };
            if bytes == 0 { break; }
            offset += bytes;
        }

        Headers::from_raw(&headers[..])
            .unwrap()
    }
}

#[derive(Clone, Hash, Eq, PartialEq, Debug)]
pub struct Loader {
    res: GenericResource,
    info: ResponseInfo,
}
impl Resource for Loader {
    fn unwrap(&self) -> ffi::PP_Resource { self.res.unwrap() }
    fn type_of(&self) -> Option<ResourceType> { Some(ResourceType::UrlLoader) }
}
impl From<ffi::PP_Resource> for Loader {
    fn from(loader: ffi::PP_Resource) -> Loader {
        let info = get_url_loader()
            .get_response_info(loader)
            .unwrap();

        Loader {
            res: From::from(loader),
            info: ResponseInfo(info),
        }
    }
}
impl Deref for Loader {
    type Target = ResponseInfo;
    fn deref(&self) -> &ResponseInfo { &self.info }
}

impl Loader {
    pub fn download_progress(&self) -> Option<(u64, Option<u64>)> {
        use std::mem;
        let mut bytes = unsafe { mem::uninitialized() };
        let mut total = unsafe { mem::uninitialized() };

        let f = get_url_loader().GetDownloadProgress.unwrap();
        if !f(self.unwrap(), &mut bytes, &mut total) != 0 {
            None
        } else {
            if total == -1 {
                Some((bytes as u64, None))
            } else {
                Some((bytes as u64, Some(total as u64)))
            }
        }
    }
    pub fn upload_progress(&self) -> Option<(u64, u64)> {
        use std::mem;
        let mut bytes = unsafe { mem::uninitialized() };
        let mut total = unsafe { mem::uninitialized() };

        let f = get_url_loader().GetUploadProgress.unwrap();
        if !f(self.unwrap(), &mut bytes, &mut total) != 0 {
            None
        } else {
            Some((bytes as u64, total as u64))
        }
    }

    pub fn finish_streaming_to_file(&self) { unimplemented!() }

    pub fn info(&self) -> ResponseInfo { self.info.clone() }

    pub fn async_open<F>(instance: Instance, info: UrlRequestInfo,
                         callback: super::CallbackArgs<F, Loader>) ->
        Code<Loader> where F: FnOnce(Code<Loader>)
    {
        let loader = try_code!(instance.create_url_loader());

        impl super::InPlaceInit for ffi::PP_Resource { }

        fn map(loader: ffi::PP_Resource, _status: Code) -> Loader {
            From::from(loader)
        }

        let mapper = super::StorageToArgsMapper(map);
        let cc = callback.to_ffi_callback(loader, mapper);

        try_code!(get_url_loader().open(loader, info.unwrap(), cc.cc()) => CC(cc))
    }
    pub fn sync_open(instance: Instance, info: UrlRequestInfo) -> Code<Loader> {
        let loader = try_code!(instance.create_url_loader());

        try_code!(get_url_loader().open(loader, info.unwrap(),
                                        BlockUntilComplete::new()));
        Code::Ok(From::from(loader))
    }
}
<commit_msg>Add a function for creating `RequestInfo` and a function for turning on redirect following.<commit_after>// Copyright (c) 2014 Richard Diamond & contributors.
//
// This file is part of the Rust PPApi project.
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

use super::{Callback, Resource, Instance, ToStringVar, ToVar, Code,
            StringVar, GenericResource, ResourceType, BlockUntilComplete,
            Var};
use super::ppb::{get_url_loader, get_url_loader_opt,
                 get_url_request_opt,
                 get_url_response};
use ppb::{self, URLRequestInfoIf, URLResponseInfoIf, URLLoaderIf};

use std::str::FromStr;
use std::ops::Deref;

use collections::enum_set::{CLike, EnumSet};

use hyper;
use hyper::header::Headers;
use httparse;

use super::ffi;
use super::ffi::bool_to_var;
use iurl::Url;

use fs::{SliceIo, FileIo, FileView};

#[derive(Hash, Eq, PartialEq, Debug)] pub struct UrlRequestInfo(ffi::PP_Resource);
#[derive(Hash, Eq, PartialEq, Debug)] pub struct ResponseInfo(ffi::PP_Resource);

impl_resource_for!(UrlRequestInfo, ResourceType::UrlRequestInfo);
impl_resource_for!(ResponseInfo, ResourceType::UrlResponseInfo);
impl_clone_drop_for!(UrlRequestInfo);
impl_clone_drop_for!(ResponseInfo);

pub type RequestProperties = EnumSet<RequestProperties_>;
#[derive(Eq, PartialEq, Clone, Hash, Copy, Debug)]
pub enum RequestProperties_ {
    AllowCrossOriginRequests,
    AllowCredentials,
    RecordUploadProgress,
    RecordDownloadProgress,
    FollowRedirects,
    StreamToFile,
}
impl CLike for RequestProperties_ {
    fn to_usize(&self) -> usize {
        match self {
            &RequestProperties_::AllowCrossOriginRequests => 0,
            &RequestProperties_::AllowCredentials => 1,
            &RequestProperties_::RecordUploadProgress => 2,
            &RequestProperties_::RecordDownloadProgress => 3,
            &RequestProperties_::FollowRedirects => 4,
            &RequestProperties_::StreamToFile => 5,
        }
    }
    fn from_usize(v: usize) -> RequestProperties_ {
        match v {
            0 => RequestProperties_::AllowCrossOriginRequests,
            1 => RequestProperties_::AllowCredentials,
            2 => RequestProperties_::RecordUploadProgress,
            3 => RequestProperties_::RecordDownloadProgress,
            4 => RequestProperties_::FollowRedirects,
            5 => RequestProperties_::StreamToFile,
            _ => unreachable!(),
        }
    }
}
impl Into<ffi::PP_URLRequestProperty> for RequestProperties_ {
    fn into(self) -> ffi::PP_URLRequestProperty {
        match self {
            RequestProperties_::AllowCrossOriginRequests =>
                ffi::PP_URLREQUESTPROPERTY_ALLOWCROSSORIGINREQUESTS,
            RequestProperties_::AllowCredentials =>
                ffi::PP_URLREQUESTPROPERTY_ALLOWCREDENTIALS,
            RequestProperties_::RecordUploadProgress =>
                ffi::PP_URLREQUESTPROPERTY_RECORDUPLOADPROGRESS,
            RequestProperties_::RecordDownloadProgress =>
                ffi::PP_URLREQUESTPROPERTY_RECORDDOWNLOADPROGRESS,
            RequestProperties_::FollowRedirects =>
                ffi::PP_URLREQUESTPROPERTY_FOLLOWREDIRECTS,
            RequestProperties_::StreamToFile =>
                ffi::PP_URLREQUESTPROPERTY_STREAMTOFILE,
        }
    }
}

#[derive(Clone, Debug)]
pub enum Body {
    File(SliceIo<FileIo>, Option<super::Time>),
    Blob(Vec<u8>),
}

pub type Method = hyper::method::Method;

trait CreateUrl {
    fn create_url_loader(&self) -> Code<ffi::PP_Resource>;
    fn create_url_request_info(&self) -> Code<UrlRequestInfo>;
}
impl CreateUrl for Instance {
    fn create_url_loader(&self) -> Code<ffi::PP_Resource> {
        get_url_loader_opt()
            .map(|i| {
                i.create(self.unwrap())
                    .map(|info| Code::Ok(info) )
                    .unwrap_or(Code::BadInstance)
            })
            .unwrap_or(Code::NoInterface)
    }
    fn create_url_request_info(&self) -> Code<UrlRequestInfo> {
        get_url_request_opt()
            .map(|i| {
                i.create(self.unwrap())
                    .map(|info| Code::Ok(UrlRequestInfo::new(info)) )
                    .unwrap_or(Code::BadInstance)
            })
            .unwrap_or(Code::NoInterface)
    }
}

#[derive(Clone, Debug)]
pub struct RequestInfo {
    pub url: Url,

    pub method: Method,

    pub prefetch_buffer: Option<(i32, i32)>,
    properties: RequestProperties,
    set_props:  RequestProperties,

    pub bodies: Vec<Body>,

    pub headers: Headers,
}
impl RequestInfo {
    pub fn new(url: Url, method: Method,
               body: Option<Body>, headers: Option<Headers>) -> RequestInfo {
        RequestInfo {
            url: url,
            method: method,

            prefetch_buffer: None,
            properties: EnumSet::new(),
            set_props: EnumSet::new(),

            bodies: body
                .map(|b| vec!(b) )
                .unwrap_or_default(),

            headers: headers.unwrap_or_else(|| Headers::new() ),
        }
    }
    fn clear_bit(bitfield: RequestProperties, prop: RequestProperties_) -> RequestProperties {
        let mut new = EnumSet::new();
        for p in bitfield
            .iter()
            .filter(|&p| p != prop ) {
                new.insert(p);
            }
        new
    }
    pub fn set_prop(&mut self, prop: RequestProperties_, bit: bool) {
        self.set_props.insert(prop);
        self.set_prop_value(prop, bit);
    }
    // unsets the 'this property is set' bit. Doesn't clear the actual property value bit.
    pub fn unset_prop(&mut self, prop: RequestProperties_) -> bool {
        let was_set = self.set_props.contains(&prop);
        self.set_props = RequestInfo::clear_bit(self.set_props, prop);
        was_set
    }

    // set the property value, but don't set the 'this property is set' bit.
    pub fn set_prop_value(&mut self, prop: RequestProperties_, bit: bool) -> bool {
        let mut new = RequestInfo::clear_bit(self.properties, prop);
        if bit {
            new.insert(prop);
        }

        let was_set = self.properties.contains(&prop);
        self.properties = new;
        was_set
    }

    pub fn follow_redirects(mut self) -> self {
        self.set_prop_value(RequestProperties_::FollowRedirects, true);
        self
    }

}
impl Into<Code<UrlRequestInfo>> for (Instance, RequestInfo) {
    fn into(self) -> Code<UrlRequestInfo> {
        let (instance, this) = self;

        let request = get_url_request_opt();
        if request.is_none() { return Code::NoInterface; }
        let request = request.unwrap();

        let res = try_code!(instance.create_url_request_info());

        let set_true = this.set_props.intersection(this.properties);
        let set_false = this.set_props - set_true;

        let true_var = unsafe { bool_to_var(1) };
        let false_var = unsafe { bool_to_var(0) };

        for prop in set_true.iter() {
            let set = request.property(res.unwrap(), prop.into(), true_var);
            if !set { return Code::Failed; }
        }
        for prop in set_false.iter() {
            let set = request.property(res.unwrap(), prop.into(), false_var);
            if !set { return Code::Failed; }
        }
        let RequestInfo {
            bodies, headers, url, method, ..
        } = this;
        for body in bodies.into_iter() {
            let success = match body {
                Body::File(ref slice, time) => {
                    request.append_file_to_body(res.unwrap(),
                                                slice.unwrap(),
                                                Some(slice.view_start() as i64),
                                                slice.view_len().map(|v| v as i64 ),
                                                time)
                }
                Body::Blob(ref blob) => {
                    request.append_blob_to_body(res.unwrap(),
                                                blob)
                }
            };
            if !success { return Code::Failed; }
        }
        let headers_str = headers.to_string();
        let headers_str = headers_str.to_string_var();
        let success = request.property(res.unwrap(),
                                       ffi::PP_URLREQUESTPROPERTY_HEADERS,
                                       headers_str.to_var());

        let url = url.to_string().to_string_var();
        let success = success && request.property(res.unwrap(),
                                                  ffi::PP_URLREQUESTPROPERTY_URL,
                                                  url.to_var());

        let method = method.to_string().to_string_var();
        let success = success && request.property(res.unwrap(),
                                                  ffi::PP_URLREQUESTPROPERTY_METHOD,
                                                  method.to_var());
        if !success { return Code::Failed; }
        Code::Ok(res)
    }
}

impl ResponseInfo {
    pub fn raw_url(&self) -> StringVar {
        let v = get_url_response()
            .property(self.unwrap(), ffi::PP_URLRESPONSEPROPERTY_URL);
        assert!(v.is_a_string());

        From::from(v)
    }
    pub fn url(&self) -> Url {
        let v = self.raw_url();
        FromStr::from_str(v.as_ref())
            .unwrap()
    }

    pub fn raw_redirect_url(&self) -> StringVar {
        let v = get_url_response()
            .property(self.unwrap(), ffi::PP_URLRESPONSEPROPERTY_REDIRECTURL);
        assert!(v.is_a_string());

        From::from(v)
    }
    pub fn redirect_url(&self) -> Option<Url> {
        let v = self.raw_redirect_url();
        if v == "" {
            None
        } else {
            Some(FromStr::from_str(v.as_ref()).unwrap())
        }
    }

    pub fn raw_redirect_method(&self) -> StringVar {
        let v = get_url_response()
            .property(self.unwrap(), ffi::PP_URLRESPONSEPROPERTY_REDIRECTMETHOD);
        assert!(v.is_a_string());

        From::from(v)
    }
    pub fn redirect_method(&self) -> Option<Method> {
        let v = get_url_response()
            .property(self.unwrap(), ffi::PP_URLRESPONSEPROPERTY_REDIRECTMETHOD);
        debug_assert!(v.is_a_string());

        let v = self.raw_redirect_method();
        if v == "" {
            None
        } else {
            Some(FromStr::from_str(v.as_ref()).unwrap())
        }
    }

    pub fn raw_status_code(&self) -> u16 {
        let v = get_url_response()
            .property(self.unwrap(), ffi::PP_URLRESPONSEPROPERTY_STATUSCODE);
        debug_assert!(v.is_an_i32());

        let v = unsafe { ffi::i32_from_var(v) };
        v as u16
    }
    pub fn status_code(&self) -> hyper::status::StatusCode {
        let v = self.raw_status_code();
        hyper::status::StatusCode::from_u16(v)
    }

    /// Returns a newline delimited string of `key: value` pairs.
    pub fn raw_headers_str<'a>(&'a self) -> &'a str {
        use std::str::from_utf8_unchecked;
        use std::slice::from_raw_parts;
        use std::mem::{transmute, uninitialized};

        let v = get_url_response()
            .property(self.unwrap(), ffi::PP_URLRESPONSEPROPERTY_HEADERS);
        assert!(v.is_a_string());

        let f = ppb::get_var().VarToUtf8.unwrap();

        unsafe {
            let mut len: u32 = uninitialized();
            let buf = f(v, &mut len as *mut u32);
            let len = len as usize;
            let slice = from_raw_parts(transmute(&buf), len);
            transmute(from_utf8_unchecked(slice))
        }
    }
    pub fn raw_headers_var(&self) -> StringVar {
        let v = get_url_response()
            .property(self.unwrap(), ffi::PP_URLRESPONSEPROPERTY_HEADERS);
        assert!(v.is_a_string());
        From::from(v)
    }

    pub fn headers(&self) -> Headers {
        let mut headers = Vec::with_capacity(5);
        let mut offset = 0;
        let raw_headers: &[u8] = self.raw_headers_str().as_ref();
        loop {
            headers.reserve(1);
            let current_len = headers.len();
            let current_cap = headers.capacity();
            unsafe { headers.set_len(current_cap) };
            let (bytes, parsed_len) = {
                let (b, p) =
                    httparse::parse_headers(&raw_headers[offset..],
                                            &mut headers[current_len..current_cap])
                    .unwrap()
                    .unwrap();
                (b, p.len())
            };
            unsafe { headers.set_len(current_len + parsed_len) };
            if bytes == 0 { break; }
            offset += bytes;
        }

        Headers::from_raw(&headers[..])
            .unwrap()
    }
}

#[derive(Clone, Hash, Eq, PartialEq, Debug)]
pub struct Loader {
    res: GenericResource,
    info: ResponseInfo,
}
impl Resource for Loader {
    fn unwrap(&self) -> ffi::PP_Resource { self.res.unwrap() }
    fn type_of(&self) -> Option<ResourceType> { Some(ResourceType::UrlLoader) }
}
impl From<ffi::PP_Resource> for Loader {
    fn from(loader: ffi::PP_Resource) -> Loader {
        let info = get_url_loader()
            .get_response_info(loader)
            .unwrap();

        Loader {
            res: From::from(loader),
            info: ResponseInfo(info),
        }
    }
}
impl Deref for Loader {
    type Target = ResponseInfo;
    fn deref(&self) -> &ResponseInfo { &self.info }
}

impl Loader {
    pub fn download_progress(&self) -> Option<(u64, Option<u64>)> {
        use std::mem;
        let mut bytes = unsafe { mem::uninitialized() };
        let mut total = unsafe { mem::uninitialized() };

        let f = get_url_loader().GetDownloadProgress.unwrap();
        if !f(self.unwrap(), &mut bytes, &mut total) != 0 {
            None
        } else {
            if total == -1 {
                Some((bytes as u64, None))
            } else {
                Some((bytes as u64, Some(total as u64)))
            }
        }
    }
    pub fn upload_progress(&self) -> Option<(u64, u64)> {
        use std::mem;
        let mut bytes = unsafe { mem::uninitialized() };
        let mut total = unsafe { mem::uninitialized() };

        let f = get_url_loader().GetUploadProgress.unwrap();
        if !f(self.unwrap(), &mut bytes, &mut total) != 0 {
            None
        } else {
            Some((bytes as u64, total as u64))
        }
    }

    pub fn finish_streaming_to_file(&self) { unimplemented!() }

    pub fn info(&self) -> ResponseInfo { self.info.clone() }

    pub fn async_open<F>(instance: Instance, info: UrlRequestInfo,
                         callback: super::CallbackArgs<F, Loader>) ->
        Code<Loader> where F: FnOnce(Code<Loader>)
    {
        let loader = try_code!(instance.create_url_loader());

        impl super::InPlaceInit for ffi::PP_Resource { }

        fn map(loader: ffi::PP_Resource, _status: Code) -> Loader {
            From::from(loader)
        }

        let mapper = super::StorageToArgsMapper(map);
        let cc = callback.to_ffi_callback(loader, mapper);

        try_code!(get_url_loader().open(loader, info.unwrap(), cc.cc()) => CC(cc))
    }
    pub fn sync_open(instance: Instance, info: UrlRequestInfo) -> Code<Loader> {
        let loader = try_code!(instance.create_url_loader());

        try_code!(get_url_loader().open(loader, info.unwrap(),
                                        BlockUntilComplete::new()));
        Code::Ok(From::from(loader))
    }
}
<|endoftext|>


<commit_before>use std::mem;
use std::sync;

pub struct Lazy<T> {
    pub lock: sync::Once,
    pub ptr: *const T,
}

impl<T> Lazy<T> {
    pub fn get<F>(&'static mut self, init: F) -> &'static T
        where F : FnOnce() -> T
    {
        self.lock.call_once(|| {
            unsafe {
                self.ptr = mem::transmute(Box::new(init()));
            }
        });
        unsafe { &*self.ptr }
    }
}

pub const ONCE_INIT: sync::Once = sync::ONCE_INIT;
<commit_msg>Add test for Lazy<T>.<commit_after>use std::mem;
use std::sync;

pub struct Lazy<T> {
    pub lock: sync::Once,
    pub ptr: *const T,
}

impl<T> Lazy<T> {
    pub fn get<F>(&'static mut self, init: F) -> &'static T
        where F : FnOnce() -> T
    {
        self.lock.call_once(|| {
            unsafe {
                self.ptr = mem::transmute(Box::new(init()));
            }
        });
        unsafe { &*self.ptr }
    }
}

pub const ONCE_INIT: sync::Once = sync::ONCE_INIT;

#[cfg(test)]
mod test {
    use super::{Lazy, ONCE_INIT};
    use std::thread;
    use std::sync::{Arc, Barrier};
    use std::sync::atomic::{AtomicIsize, Ordering, ATOMIC_ISIZE_INIT};

    #[test]
    fn many_threads_calling_get() {
        const N_THREADS: usize = 32;
        const N_ITERS_IN_THREAD: usize = 32;
        const N_ITERS: usize = 16;

        static mut LAZY: Lazy<String> = Lazy {
            lock: ONCE_INIT,
            ptr: 0 as *const String,
        };
        static CALL_COUNT: AtomicIsize = ATOMIC_ISIZE_INIT;

        let value = "Hello, world!".to_owned();

        for _ in 0..N_ITERS {
            // Reset mutable state.
            unsafe {
                LAZY = Lazy {
                    lock: ONCE_INIT,
                    ptr: 0 as *const String,
                }
            }
            CALL_COUNT.store(0, Ordering::SeqCst);

            // Create a bunch of threads, all calling .get() at the same time.
            let mut threads = vec![];
            let barrier = Arc::new(Barrier::new(N_THREADS));

            for _ in 0..N_THREADS {
                let cloned_value_thread = value.clone();
                let cloned_barrier = barrier.clone();
                threads.push(thread::spawn(move || {
                    // Ensure all threads start at once to maximise contention.
                    cloned_barrier.wait();
                    for _ in 0..N_ITERS_IN_THREAD {
                        assert_eq!(&cloned_value_thread, unsafe {
                            LAZY.get(|| {
                                CALL_COUNT.fetch_add(1, Ordering::SeqCst);
                                cloned_value_thread.clone()
                            })
                        });
                    }
                }));
            }

            for thread in threads {
                thread.join().unwrap();
            }

            assert_eq!(CALL_COUNT.load(Ordering::SeqCst), 1);
        }
    }
}
<|endoftext|>


<commit_before>//! Bare MIPS32r2, little endian, softfloat
//!
//! Can be used for MIPS M4K core (e.g. on PIC32MX devices)

use crate::spec::abi::Abi;
use crate::spec::{LinkerFlavor, LldFlavor, RelocModel};
use crate::spec::{PanicStrategy, Target, TargetOptions};

pub fn target() -> Target {
    Target {
        llvm_target: "mipsel-unknown-none".to_string(),
        target_endian: "little".to_string(),
        pointer_width: 32,
        target_c_int_width: "32".to_string(),
        data_layout: "e-m:m-p:32:32-i8:8:32-i16:16:32-i64:64-n32-S64".to_string(),
        arch: "mips".to_string(),
        target_os: "none".to_string(),
        target_env: String::new(),
        target_vendor: String::new(),
        linker_flavor: LinkerFlavor::Lld(LldFlavor::Ld),

        options: TargetOptions {
            cpu: "mips32r2".to_string(),
            features: "+mips32r2,+soft-float,+noabicalls".to_string(),
            max_atomic_width: Some(32),
            executables: true,
            linker: Some("rust-lld".to_owned()),
            panic_strategy: PanicStrategy::Abort,
            relocation_model: RelocModel::Static,
            unsupported_abis: vec![
                Abi::Stdcall,
                Abi::Fastcall,
                Abi::Vectorcall,
                Abi::Thiscall,
                Abi::Win64,
                Abi::SysV64,
            ],
            emit_debug_gdb_scripts: false,
            ..Default::default()
        },
    }
}
<commit_msg>indicate calling convention<commit_after>//! Bare MIPS32r2, little endian, softfloat, O32 calling convention
//!
//! Can be used for MIPS M4K core (e.g. on PIC32MX devices)

use crate::spec::abi::Abi;
use crate::spec::{LinkerFlavor, LldFlavor, RelocModel};
use crate::spec::{PanicStrategy, Target, TargetOptions};

pub fn target() -> Target {
    Target {
        llvm_target: "mipsel-unknown-none".to_string(),
        target_endian: "little".to_string(),
        pointer_width: 32,
        target_c_int_width: "32".to_string(),
        data_layout: "e-m:m-p:32:32-i8:8:32-i16:16:32-i64:64-n32-S64".to_string(),
        arch: "mips".to_string(),
        target_os: "none".to_string(),
        target_env: String::new(),
        target_vendor: String::new(),
        linker_flavor: LinkerFlavor::Lld(LldFlavor::Ld),

        options: TargetOptions {
            cpu: "mips32r2".to_string(),
            features: "+mips32r2,+soft-float,+noabicalls".to_string(),
            max_atomic_width: Some(32),
            executables: true,
            linker: Some("rust-lld".to_owned()),
            panic_strategy: PanicStrategy::Abort,
            relocation_model: RelocModel::Static,
            unsupported_abis: vec![
                Abi::Stdcall,
                Abi::Fastcall,
                Abi::Vectorcall,
                Abi::Thiscall,
                Abi::Win64,
                Abi::SysV64,
            ],
            emit_debug_gdb_scripts: false,
            ..Default::default()
        },
    }
}
<|endoftext|>


<commit_before>use crate::i18n::LocalText;
use crate::*;

use colored::Colorize;
use regex::Regex;
use std::io::prelude::*;
use std::{ffi::OsString, io};
use subprocess::{Exec, ExitStatus, Redirection};

// FTL: help-subcommand-make
/// Build specified target(s)
pub fn run(target: Vec<String>) -> Result<()> {
    setup::is_setup()?;
    show_header("make-header");
    let mut makeflags: Vec<OsString> = Vec::new();
    let cpus = &num_cpus::get().to_string();
    makeflags.push(OsString::from(format!("--jobs={}", cpus)));
    let mut makefiles: Vec<OsString> = Vec::new();
    let rules = status::get_rules()?;
    makefiles.push(OsString::from("-f"));
    makefiles.push(OsString::from(format!(
        "{}{}",
        CONFIGURE_DATADIR, "/rules/casile.mk"
    )));
    for rule in rules {
        makefiles.push(OsString::from("-f"));
        let p = rule.into_os_string();
        makefiles.push(p);
    }
    makefiles.push(OsString::from("-f"));
    makefiles.push(OsString::from(format!(
        "{}{}",
        CONFIGURE_DATADIR, "/rules/rules.mk"
    )));
    let mut targets: Vec<_> = target.into_iter().collect();
    if targets.is_empty() {
        targets.push(String::from("default"));
    }
    if status::is_gha()? {
        targets.push(String::from("_gha"));
    }
    if status::is_glc()? {
        targets.push(String::from("_glc"));
    }
    if (status::is_gha()? || status::is_glc()?)
        && targets.first().unwrap() != "debug"
        && targets.first().unwrap() != ".gitignore"
    {
        targets.push(String::from("install-dist"));
    }
    let mut process = Exec::cmd("make")
        .args(&makeflags)
        .args(&makefiles)
        .args(&targets);
    let gitname = status::get_gitname()?;
    let git_version = status::get_git_version();
    process = process
        .env("CASILE_CLI", "true")
        .env("CASILE_JOBS", cpus)
        .env("CASILEDIR", CONFIGURE_DATADIR)
        .env("CONTAINERIZED", status::is_container().to_string())
        .env("LANGUAGE", lang_to_language(CONF.get_string("language")?))
        .env("PROJECT", &gitname)
        .env("PROJECTDIR", CONF.get_string("path")?)
        .env("PROJECTVERSION", git_version);
    if CONF.get_bool("debug")? {
        process = process.env("DEBUG", "true");
    };
    if CONF.get_bool("quiet")? {
        process = process.env("QUIET", "true");
    };
    if CONF.get_bool("verbose")? || targets.contains(&"debug".into()) {
        process = process.env("VERBOSE", "true");
    };
    let repo = get_repo()?;
    let workdir = repo.workdir().unwrap();
    process = process.cwd(workdir);
    let process = process.stderr(Redirection::Merge).stdout(Redirection::Pipe);
    let mut popen = process.popen()?;
    let buf = io::BufReader::new(popen.stdout.as_mut().unwrap());
    let mut backlog: Vec<String> = Vec::new();
    let seps = Regex::new(r"").unwrap();
    let mut ret: u32 = 0;
    for line in buf.lines() {
        let text: &str = &line.unwrap();
        let fields: Vec<&str> = seps.splitn(text, 4).collect();
        match fields[0] {
            "CASILE" => match fields[1] {
                "PRE" => report_start(fields[2]),
                "STDOUT" => {
                    if status::is_gha()? {
                        println!("{}", fields[3]);
                    } else if CONF.get_bool("verbose")? {
                        report_line(fields[3]);
                    } else {
                        backlog.push(String::from(fields[3]));
                    }
                }
                "STDERR" => {
                    if CONF.get_bool("verbose")? {
                        report_line(fields[3]);
                    } else {
                        backlog.push(String::from(fields[3]));
                    }
                }
                "POST" => match fields[2] {
                    "0" => {
                        report_end(fields[3]);
                    }
                    val => {
                        report_fail(fields[3]);
                        ret = val.parse().unwrap_or(1);
                    }
                },
                _ => {
                    let errmsg = LocalText::new("make-error-unknown-code").fmt();
                    panic!("{}", errmsg)
                }
            },
            _ => backlog.push(String::from(fields[0])),
        }
    }
    let status = popen.wait();
    match status {
        Ok(ExitStatus::Exited(int)) => {
            let code = int + ret;
            match code {
                0 => {
                    if CONF.get_bool("debug")?
                        || targets.contains(&"debug".into())
                        || targets.contains(&"-p".into())
                    {
                        dump_backlog(&backlog)
                    };
                    Ok(())
                }
                1 => {
                    dump_backlog(&backlog);
                    Err(Box::new(io::Error::new(
                        io::ErrorKind::InvalidInput,
                        LocalText::new("make-error-unfinished").fmt(),
                    )))
                }
                2 => {
                    dump_backlog(&backlog);
                    Err(Box::new(io::Error::new(
                        io::ErrorKind::InvalidInput,
                        LocalText::new("make-error-build").fmt(),
                    )))
                }
                3 => {
                    if !CONF.get_bool("verbose")? {
                        dump_backlog(&backlog);
                    }
                    Err(Box::new(io::Error::new(
                        io::ErrorKind::InvalidInput,
                        LocalText::new("make-error-target").fmt(),
                    )))
                }
                137 => {
                    if !CONF.get_bool("verbose")? {
                        dump_backlog(&backlog);
                    }
                    Err(Box::new(io::Error::new(
                        io::ErrorKind::InvalidInput,
                        LocalText::new("make-error-oom").fmt(),
                    )))
                }
                _ => {
                    dump_backlog(&backlog);
                    Err(Box::new(io::Error::new(
                        io::ErrorKind::InvalidInput,
                        LocalText::new("make-error-unknown").fmt(),
                    )))
                }
            }
        }
        _ => Err(Box::new(io::Error::new(
            io::ErrorKind::InvalidInput,
            LocalText::new("make-error").fmt(),
        ))),
    }
}

fn dump_backlog(backlog: &[String]) {
    let start = LocalText::new("make-backlog-start").fmt();
    eprintln!("{} {}", "┖┄".cyan(), start);
    for line in backlog.iter() {
        eprintln!("{}", line);
    }
    let end = LocalText::new("make-backlog-end").fmt();
    eprintln!("{} {}", "┎┄".cyan(), end);
}

fn report_line(line: &str) {
    eprintln!("{} {}", "┠╎".cyan(), line.dimmed());
}

fn report_start(target: &str) {
    let text = LocalText::new("make-report-start")
        .arg("target", target.white().bold())
        .fmt();
    eprintln!("{} {}", "┠┄".cyan(), text.yellow());
}

fn report_end(target: &str) {
    let text = LocalText::new("make-report-end")
        .arg("target", target.white().bold())
        .fmt();
    eprintln!("{} {}", "┠┄".cyan(), text.green());
}

fn report_fail(target: &str) {
    let text = LocalText::new("make-report-fail")
        .arg("target", target.white().bold())
        .fmt();
    eprintln!("{} {}", "┠┄".cyan(), text.red());
}
<commit_msg>fix(cli): Avoid panic on invalid UTF-8 (sometimes thrown by kindlegen)<commit_after>use crate::i18n::LocalText;
use crate::*;

use colored::Colorize;
use regex::Regex;
use std::io::prelude::*;
use std::{ffi::OsString, io};
use subprocess::{Exec, ExitStatus, Redirection};

// FTL: help-subcommand-make
/// Build specified target(s)
pub fn run(target: Vec<String>) -> Result<()> {
    setup::is_setup()?;
    show_header("make-header");
    let mut makeflags: Vec<OsString> = Vec::new();
    let cpus = &num_cpus::get().to_string();
    makeflags.push(OsString::from(format!("--jobs={}", cpus)));
    let mut makefiles: Vec<OsString> = Vec::new();
    let rules = status::get_rules()?;
    makefiles.push(OsString::from("-f"));
    makefiles.push(OsString::from(format!(
        "{}{}",
        CONFIGURE_DATADIR, "/rules/casile.mk"
    )));
    for rule in rules {
        makefiles.push(OsString::from("-f"));
        let p = rule.into_os_string();
        makefiles.push(p);
    }
    makefiles.push(OsString::from("-f"));
    makefiles.push(OsString::from(format!(
        "{}{}",
        CONFIGURE_DATADIR, "/rules/rules.mk"
    )));
    let mut targets: Vec<_> = target.into_iter().collect();
    if targets.is_empty() {
        targets.push(String::from("default"));
    }
    if status::is_gha()? {
        targets.push(String::from("_gha"));
    }
    if status::is_glc()? {
        targets.push(String::from("_glc"));
    }
    if (status::is_gha()? || status::is_glc()?)
        && targets.first().unwrap() != "debug"
        && targets.first().unwrap() != ".gitignore"
    {
        targets.push(String::from("install-dist"));
    }
    let mut process = Exec::cmd("make")
        .args(&makeflags)
        .args(&makefiles)
        .args(&targets);
    let gitname = status::get_gitname()?;
    let git_version = status::get_git_version();
    process = process
        .env("CASILE_CLI", "true")
        .env("CASILE_JOBS", cpus)
        .env("CASILEDIR", CONFIGURE_DATADIR)
        .env("CONTAINERIZED", status::is_container().to_string())
        .env("LANGUAGE", lang_to_language(CONF.get_string("language")?))
        .env("PROJECT", &gitname)
        .env("PROJECTDIR", CONF.get_string("path")?)
        .env("PROJECTVERSION", git_version);
    if CONF.get_bool("debug")? {
        process = process.env("DEBUG", "true");
    };
    if CONF.get_bool("quiet")? {
        process = process.env("QUIET", "true");
    };
    if CONF.get_bool("verbose")? || targets.contains(&"debug".into()) {
        process = process.env("VERBOSE", "true");
    };
    let repo = get_repo()?;
    let workdir = repo.workdir().unwrap();
    process = process.cwd(workdir);
    let process = process.stderr(Redirection::Merge).stdout(Redirection::Pipe);
    let mut popen = process.popen()?;
    let buf = io::BufReader::new(popen.stdout.as_mut().unwrap());
    let mut backlog: Vec<String> = Vec::new();
    let seps = Regex::new(r"").unwrap();
    let mut ret: u32 = 0;
    for line in buf.lines() {
        let text: &str = &line.unwrap_or(String::from("INVALID UTF-8 FROM CHILD PROCESS STREAM"));
        let fields: Vec<&str> = seps.splitn(text, 4).collect();
        match fields[0] {
            "CASILE" => match fields[1] {
                "PRE" => report_start(fields[2]),
                "STDOUT" => {
                    if status::is_gha()? {
                        println!("{}", fields[3]);
                    } else if CONF.get_bool("verbose")? {
                        report_line(fields[3]);
                    } else {
                        backlog.push(String::from(fields[3]));
                    }
                }
                "STDERR" => {
                    if CONF.get_bool("verbose")? {
                        report_line(fields[3]);
                    } else {
                        backlog.push(String::from(fields[3]));
                    }
                }
                "POST" => match fields[2] {
                    "0" => {
                        report_end(fields[3]);
                    }
                    val => {
                        report_fail(fields[3]);
                        ret = val.parse().unwrap_or(1);
                    }
                },
                _ => {
                    let errmsg = LocalText::new("make-error-unknown-code").fmt();
                    panic!("{}", errmsg)
                }
            },
            _ => backlog.push(String::from(fields[0])),
        }
    }
    let status = popen.wait();
    match status {
        Ok(ExitStatus::Exited(int)) => {
            let code = int + ret;
            match code {
                0 => {
                    if CONF.get_bool("debug")?
                        || targets.contains(&"debug".into())
                        || targets.contains(&"-p".into())
                    {
                        dump_backlog(&backlog)
                    };
                    Ok(())
                }
                1 => {
                    dump_backlog(&backlog);
                    Err(Box::new(io::Error::new(
                        io::ErrorKind::InvalidInput,
                        LocalText::new("make-error-unfinished").fmt(),
                    )))
                }
                2 => {
                    dump_backlog(&backlog);
                    Err(Box::new(io::Error::new(
                        io::ErrorKind::InvalidInput,
                        LocalText::new("make-error-build").fmt(),
                    )))
                }
                3 => {
                    if !CONF.get_bool("verbose")? {
                        dump_backlog(&backlog);
                    }
                    Err(Box::new(io::Error::new(
                        io::ErrorKind::InvalidInput,
                        LocalText::new("make-error-target").fmt(),
                    )))
                }
                137 => {
                    if !CONF.get_bool("verbose")? {
                        dump_backlog(&backlog);
                    }
                    Err(Box::new(io::Error::new(
                        io::ErrorKind::InvalidInput,
                        LocalText::new("make-error-oom").fmt(),
                    )))
                }
                _ => {
                    dump_backlog(&backlog);
                    Err(Box::new(io::Error::new(
                        io::ErrorKind::InvalidInput,
                        LocalText::new("make-error-unknown").fmt(),
                    )))
                }
            }
        }
        _ => Err(Box::new(io::Error::new(
            io::ErrorKind::InvalidInput,
            LocalText::new("make-error").fmt(),
        ))),
    }
}

fn dump_backlog(backlog: &[String]) {
    let start = LocalText::new("make-backlog-start").fmt();
    eprintln!("{} {}", "┖┄".cyan(), start);
    for line in backlog.iter() {
        eprintln!("{}", line);
    }
    let end = LocalText::new("make-backlog-end").fmt();
    eprintln!("{} {}", "┎┄".cyan(), end);
}

fn report_line(line: &str) {
    eprintln!("{} {}", "┠╎".cyan(), line.dimmed());
}

fn report_start(target: &str) {
    let text = LocalText::new("make-report-start")
        .arg("target", target.white().bold())
        .fmt();
    eprintln!("{} {}", "┠┄".cyan(), text.yellow());
}

fn report_end(target: &str) {
    let text = LocalText::new("make-report-end")
        .arg("target", target.white().bold())
        .fmt();
    eprintln!("{} {}", "┠┄".cyan(), text.green());
}

fn report_fail(target: &str) {
    let text = LocalText::new("make-report-fail")
        .arg("target", target.white().bold())
        .fmt();
    eprintln!("{} {}", "┠┄".cyan(), text.red());
}
<|endoftext|>


<commit_before><commit_msg>Impl ser::to_write<commit_after><|endoftext|>


<commit_before><commit_msg>docs pass for service<commit_after><|endoftext|>