<commit_before>use std::cell::RefCell;
use std::slice::bytes::copy_memory;

use sdl2::rect::Rect;
use sdl2::keycode::KeyCode;
use sdl2::render::{RenderDrawer, Texture};

use gb_emu::mmu::Memory;
use gb_emu::emulator::Emulator;
use gb_emu::graphics;
use gb_emu::joypad;

use common::{PlayerData, SpriteData, Direction};

use client;
use interface::{self, extract, hacks, InterfaceData, InterfaceState};
use chat::ChatBox;
use font::Font;

enum GameState {
    Emulator,
    ChatBox,
    Menu,
}

pub struct Game<'a> {
    pub emulator: Box<Emulator>,
    pub emu_texture: Texture<'a>,
    pub default_sprite: Vec<u8>,
    pub font: Font<'a>,

    pub game_state: GameState,
    pub interface_data: RefCell<InterfaceData>,
    pub chat_box: ChatBox,
    pub player_data: PlayerData,
    pub fast_mode: bool,
}

impl<'a> Game<'a> {
    pub fn new(emulator: Box<Emulator>, emu_texture: Texture<'a>, default_sprite: Vec<u8>,
        font: Font<'a>) -> Game<'a>
    {
        Game {
            emulator: emulator,
            emu_texture: emu_texture,
            default_sprite: default_sprite,
            font: font,

            game_state: GameState::Emulator,
            interface_data: RefCell::new(InterfaceData::new()),
            chat_box: ChatBox::new(),
            player_data: PlayerData::new(),
            fast_mode: false,
        }
    }

    pub fn update(&mut self) {
        if self.interface_data.borrow().state == InterfaceState::Normal {
            let interface_data = &mut self.interface_data;
            let mut emulator = &mut self.emulator;
            let emu_texture = &mut self.emu_texture;
            let default_sprite = &*self.default_sprite;
            let player_data = &mut self.player_data;

            emulator.frame(
                |cpu, mem| {
                    let interface_data = &mut *interface_data.borrow_mut();
                    hacks::sprite_check(cpu, mem, interface_data);
                    hacks::display_text(cpu, mem, interface_data);
                    hacks::sprite_update_tracker(cpu, mem, interface_data);
                },

                |_, mem| {
                    let interface_data = &*interface_data.borrow();
                    if interface_data.sprites_enabled() {
                        draw_other_players(interface_data, player_data, mem, default_sprite);
                    }

                    // Copy the screen to the emulator texture
                    let _ = emu_texture.with_lock(None, |mut pixels, _| {
                        copy_memory(&mut *pixels, &mem.gpu.framebuffer);
                    });

                    *player_data = extract::player_data(mem);
                }
            );
        }

    }

    pub fn render(&self, drawer: &mut RenderDrawer) {
        drawer.copy(&self.emu_texture, None, Some(Rect::new(0, 0, client::EMU_WIDTH,
            client::EMU_HEIGHT)));

        self.chat_box.draw(drawer, &self.font, Rect::new(client::EMU_WIDTH, 0,
            client::CHAT_WIDTH, client::EMU_HEIGHT));
    }

    pub fn key_down(&mut self, keycode: KeyCode) {
        match self.game_state {
            GameState::Emulator => {
                self.write_to_joypad(keycode, joypad::State::Pressed);
                if keycode == KeyCode::Space { self.fast_mode = true; }
            },

            GameState::ChatBox => self.write_to_chatbox(keycode),

            GameState::Menu => unimplemented!(),
        }
    }

    pub fn key_up(&mut self, keycode: KeyCode) {
        match self.game_state {
            GameState::Emulator => {
                self.write_to_joypad(keycode, joypad::State::Released);
                if keycode == KeyCode::Space { self.fast_mode = false; }
                else if keycode == KeyCode::T { self.game_state = GameState::ChatBox; }
            },

            GameState::ChatBox => {
                if keycode == KeyCode::Return { self.game_state = GameState::Emulator; }
            },

            GameState::Menu => unimplemented!(),
        }
    }

    fn write_to_joypad(&mut self, keycode: KeyCode, state: joypad::State) {
        let joypad = &mut self.emulator.mem.joypad;
        // TODO: Add custom keybindings
        match keycode {
            KeyCode::Up => joypad.up = state,
            KeyCode::Down => joypad.down = state,
            KeyCode::Left => joypad.left = state,
            KeyCode::Right => joypad.right = state,

            KeyCode::Z => joypad.a = state,
            KeyCode::X => joypad.b = state,
            KeyCode::Return => joypad.start = state,
            KeyCode::RShift => joypad.select = state,

            _ => {},
        }
    }

    fn write_to_chatbox(&mut self, keycode: KeyCode) {
        let letter = match keycode {
            KeyCode::Return => { self.chat_box.message_ready = true; return },
            KeyCode::Backspace => { self.chat_box.message_buffer.pop(); return },
            KeyCode::Space => ' ',
            KeyCode::A => 'a',
            KeyCode::B => 'b',
            KeyCode::C => 'c',
            KeyCode::D => 'd',
            KeyCode::E => 'e',
            KeyCode::F => 'f',
            KeyCode::G => 'g',
            KeyCode::H => 'h',
            KeyCode::I => 'i',
            KeyCode::J => 'j',
            KeyCode::K => 'k',
            KeyCode::L => 'l',
            KeyCode::M => 'm',
            KeyCode::N => 'n',
            KeyCode::O => 'o',
            KeyCode::P => 'p',
            KeyCode::Q => 'q',
            KeyCode::R => 'r',
            KeyCode::S => 's',
            KeyCode::T => 't',
            KeyCode::U => 'u',
            KeyCode::V => 'v',
            KeyCode::W => 'w',
            KeyCode::X => 'x',
            KeyCode::Y => 'y',
            KeyCode::Z => 'z',
            _ => return,
        };

        self.chat_box.message_buffer.push(letter);
    }
}

fn draw_other_players(interface_data: &InterfaceData, self_data: &PlayerData, mem: &mut Memory,
    sprite: &[u8])
{
    for player in interface_data.players.values() {
        if player.is_visible_to(self_data) {
            let (x, y) = get_player_draw_position(self_data, player);
            let (index, flags) = get_sprite_index_and_flags(player);
            let sprite_data = SpriteData {
                x: x as isize,
                y: y as isize,
                index: index as usize,
                flags: flags,
            };
            interface::render_sprite(mem, sprite, &sprite_data);
        }
    }
}

fn get_player_draw_position(self_player: &PlayerData, other_player: &PlayerData) -> (i32, i32) {
    let base_x = (graphics::WIDTH as i32) / 2 - 16;
    let base_y = (graphics::HEIGHT as i32) / 2 - 12;

    let (self_x, self_y) = get_player_position(self_player);
    let (other_x, other_y) = get_player_position(other_player);

    (other_x - self_x + base_x, other_y - self_y + base_y)
}

fn get_player_position(player: &PlayerData) -> (i32, i32) {
    let x = player.movement_data.map_x as i32 * 16;
    let y = player.movement_data.map_y as i32 * 16;

    // Determine the offset of the player between tiles:
    // When a player begins walking, the walk counter is set to 8. For each step the walk counter
    // decreases by one, and the player is moved by two pixels, until the walk counter is 0. When
    // we reach this point, the players map coordinate updated.
    let ticks = player.movement_data.walk_counter;
    let offset = if ticks == 0 { 0 } else { (8 - ticks) * 2 } as i32;

    match player.movement_data.direction {
        Direction::Down  => (x, y + offset),
        Direction::Up    => (x, y - offset),
        Direction::Left  => (x - offset, y),
        Direction::Right => (x + offset, y),
    }
}

//fn get_sprite_index_and_flags(direction: u8, walk_counter: u8) -> (isize, u8) {
fn get_sprite_index_and_flags(player: &PlayerData) -> (isize, u8) {
    let (mut index, mut flags) = match player.movement_data.direction {
        Direction::Down  => (0, 0x00),
        Direction::Up    => (1, 0x00),
        Direction::Left  => (2, 0x00),
        Direction::Right => (2, 0x20),
    };

    flags |= 0x80;

    index += match player.movement_data.walk_counter / 4 {
        0 => 0,
        1 => 3,

        _ => 0, // Usually unreachable
    };

    (index, flags)
}
<commit_msg>Remove comment<commit_after>use std::cell::RefCell;
use std::slice::bytes::copy_memory;

use sdl2::rect::Rect;
use sdl2::keycode::KeyCode;
use sdl2::render::{RenderDrawer, Texture};

use gb_emu::mmu::Memory;
use gb_emu::emulator::Emulator;
use gb_emu::graphics;
use gb_emu::joypad;

use common::{PlayerData, SpriteData, Direction};

use client;
use interface::{self, extract, hacks, InterfaceData, InterfaceState};
use chat::ChatBox;
use font::Font;

enum GameState {
    Emulator,
    ChatBox,
    Menu,
}

pub struct Game<'a> {
    pub emulator: Box<Emulator>,
    pub emu_texture: Texture<'a>,
    pub default_sprite: Vec<u8>,
    pub font: Font<'a>,

    pub game_state: GameState,
    pub interface_data: RefCell<InterfaceData>,
    pub chat_box: ChatBox,
    pub player_data: PlayerData,
    pub fast_mode: bool,
}

impl<'a> Game<'a> {
    pub fn new(emulator: Box<Emulator>, emu_texture: Texture<'a>, default_sprite: Vec<u8>,
        font: Font<'a>) -> Game<'a>
    {
        Game {
            emulator: emulator,
            emu_texture: emu_texture,
            default_sprite: default_sprite,
            font: font,

            game_state: GameState::Emulator,
            interface_data: RefCell::new(InterfaceData::new()),
            chat_box: ChatBox::new(),
            player_data: PlayerData::new(),
            fast_mode: false,
        }
    }

    pub fn update(&mut self) {
        if self.interface_data.borrow().state == InterfaceState::Normal {
            let interface_data = &mut self.interface_data;
            let mut emulator = &mut self.emulator;
            let emu_texture = &mut self.emu_texture;
            let default_sprite = &*self.default_sprite;
            let player_data = &mut self.player_data;

            emulator.frame(
                |cpu, mem| {
                    let interface_data = &mut *interface_data.borrow_mut();
                    hacks::sprite_check(cpu, mem, interface_data);
                    hacks::display_text(cpu, mem, interface_data);
                    hacks::sprite_update_tracker(cpu, mem, interface_data);
                },

                |_, mem| {
                    let interface_data = &*interface_data.borrow();
                    if interface_data.sprites_enabled() {
                        draw_other_players(interface_data, player_data, mem, default_sprite);
                    }

                    // Copy the screen to the emulator texture
                    let _ = emu_texture.with_lock(None, |mut pixels, _| {
                        copy_memory(&mut *pixels, &mem.gpu.framebuffer);
                    });

                    *player_data = extract::player_data(mem);
                }
            );
        }

    }

    pub fn render(&self, drawer: &mut RenderDrawer) {
        drawer.copy(&self.emu_texture, None, Some(Rect::new(0, 0, client::EMU_WIDTH,
            client::EMU_HEIGHT)));

        self.chat_box.draw(drawer, &self.font, Rect::new(client::EMU_WIDTH, 0,
            client::CHAT_WIDTH, client::EMU_HEIGHT));
    }

    pub fn key_down(&mut self, keycode: KeyCode) {
        match self.game_state {
            GameState::Emulator => {
                self.write_to_joypad(keycode, joypad::State::Pressed);
                if keycode == KeyCode::Space { self.fast_mode = true; }
            },

            GameState::ChatBox => self.write_to_chatbox(keycode),

            GameState::Menu => unimplemented!(),
        }
    }

    pub fn key_up(&mut self, keycode: KeyCode) {
        match self.game_state {
            GameState::Emulator => {
                self.write_to_joypad(keycode, joypad::State::Released);
                if keycode == KeyCode::Space { self.fast_mode = false; }
                else if keycode == KeyCode::T { self.game_state = GameState::ChatBox; }
            },

            GameState::ChatBox => {
                if keycode == KeyCode::Return { self.game_state = GameState::Emulator; }
            },

            GameState::Menu => unimplemented!(),
        }
    }

    fn write_to_joypad(&mut self, keycode: KeyCode, state: joypad::State) {
        let joypad = &mut self.emulator.mem.joypad;
        // TODO: Add custom keybindings
        match keycode {
            KeyCode::Up => joypad.up = state,
            KeyCode::Down => joypad.down = state,
            KeyCode::Left => joypad.left = state,
            KeyCode::Right => joypad.right = state,

            KeyCode::Z => joypad.a = state,
            KeyCode::X => joypad.b = state,
            KeyCode::Return => joypad.start = state,
            KeyCode::RShift => joypad.select = state,

            _ => {},
        }
    }

    fn write_to_chatbox(&mut self, keycode: KeyCode) {
        let letter = match keycode {
            KeyCode::Return => { self.chat_box.message_ready = true; return },
            KeyCode::Backspace => { self.chat_box.message_buffer.pop(); return },
            KeyCode::Space => ' ',
            KeyCode::A => 'a',
            KeyCode::B => 'b',
            KeyCode::C => 'c',
            KeyCode::D => 'd',
            KeyCode::E => 'e',
            KeyCode::F => 'f',
            KeyCode::G => 'g',
            KeyCode::H => 'h',
            KeyCode::I => 'i',
            KeyCode::J => 'j',
            KeyCode::K => 'k',
            KeyCode::L => 'l',
            KeyCode::M => 'm',
            KeyCode::N => 'n',
            KeyCode::O => 'o',
            KeyCode::P => 'p',
            KeyCode::Q => 'q',
            KeyCode::R => 'r',
            KeyCode::S => 's',
            KeyCode::T => 't',
            KeyCode::U => 'u',
            KeyCode::V => 'v',
            KeyCode::W => 'w',
            KeyCode::X => 'x',
            KeyCode::Y => 'y',
            KeyCode::Z => 'z',
            _ => return,
        };

        self.chat_box.message_buffer.push(letter);
    }
}

fn draw_other_players(interface_data: &InterfaceData, self_data: &PlayerData, mem: &mut Memory,
    sprite: &[u8])
{
    for player in interface_data.players.values() {
        if player.is_visible_to(self_data) {
            let (x, y) = get_player_draw_position(self_data, player);
            let (index, flags) = get_sprite_index_and_flags(player);
            let sprite_data = SpriteData {
                x: x as isize,
                y: y as isize,
                index: index as usize,
                flags: flags,
            };
            interface::render_sprite(mem, sprite, &sprite_data);
        }
    }
}

fn get_player_draw_position(self_player: &PlayerData, other_player: &PlayerData) -> (i32, i32) {
    let base_x = (graphics::WIDTH as i32) / 2 - 16;
    let base_y = (graphics::HEIGHT as i32) / 2 - 12;

    let (self_x, self_y) = get_player_position(self_player);
    let (other_x, other_y) = get_player_position(other_player);

    (other_x - self_x + base_x, other_y - self_y + base_y)
}

fn get_player_position(player: &PlayerData) -> (i32, i32) {
    let x = player.movement_data.map_x as i32 * 16;
    let y = player.movement_data.map_y as i32 * 16;

    // Determine the offset of the player between tiles:
    // When a player begins walking, the walk counter is set to 8. For each step the walk counter
    // decreases by one, and the player is moved by two pixels, until the walk counter is 0. When
    // we reach this point, the players map coordinate updated.
    let ticks = player.movement_data.walk_counter;
    let offset = if ticks == 0 { 0 } else { (8 - ticks) * 2 } as i32;

    match player.movement_data.direction {
        Direction::Down  => (x, y + offset),
        Direction::Up    => (x, y - offset),
        Direction::Left  => (x - offset, y),
        Direction::Right => (x + offset, y),
    }
}

fn get_sprite_index_and_flags(player: &PlayerData) -> (isize, u8) {
    let (mut index, mut flags) = match player.movement_data.direction {
        Direction::Down  => (0, 0x00),
        Direction::Up    => (1, 0x00),
        Direction::Left  => (2, 0x00),
        Direction::Right => (2, 0x20),
    };

    flags |= 0x80;

    index += match player.movement_data.walk_counter / 4 {
        0 => 0,
        1 => 3,

        _ => 0, // Usually unreachable
    };

    (index, flags)
}
<|endoftext|>


<commit_before>//! Hazard pointer management.
//!
//! "Hazard" is the general name for an atomic pointer potentially "protecting" some object from
//! getting deleted. First when the hazard is changed to another state, the object can be deleted.
//!
//! Since hazards are only useful when they're shared between the global and local state, a "hazard
//! pair" refers to the collection of the two connected ends of the hazard (they both share a
//! pointer to the hazard on the heap). When such pair is created, its reading end is usually
//! stored in the global state, such that a thread can check that no hazard is matching a
//! particular object during garbage collection. The writer part controls what the state of the
//! hazard is and is usually passed around locally.
//!
//! The asymmetry of a hazard pair is strictly speaking not necessary, but it allows to enforce
//! rules (e.g. only the reader/global part may deallocate the hazard box).

use std::sync::atomic::{self, AtomicUsize};
use std::{ops, mem, thread};

use local;

/// The state of a hazard.
///
/// Note that this `enum` excludes the blocked state, because it is semantically different from the
/// other states.
#[derive(PartialEq)]
pub enum State {
    /// The hazard does not currently protect any object.
    Free,
    /// The hazard is dead and may be deallocated when necessary.
    Dead,
    /// The hazard is protecting an object.
    ///
    /// "Protecting" means that the pointer it holds is not deleted while the hazard is in this
    /// state.
    Protect(*const u8),
}

/// A hazard.
///
/// This type holds an atomic pointer with the state of the hazard. It represents the same as
/// `State` but is encoded such that it is atomically accessible.
///
/// Furthermore, there is an additional state: Blocked. If the hazard is in this state, reading it
/// will block until it no longer is. This is useful for blocking garbage collection while a value
/// is being read (avoiding the ABA problem).
#[derive(Debug)]
pub struct Hazard {
    /// The inner atomic value.
    ///
    /// This takes following forms:
    ///
    /// - 0: blocked.
    /// - 1: free.
    /// - 2: dead
    /// - otherwise: protecting the address represented by the `usize`.
    ptr: AtomicUsize,
}

impl Hazard {
    /// Create a new hazard in blocked state.
    pub fn blocked() -> Hazard {
        Hazard {
            ptr: AtomicUsize::new(0),
        }
    }

    /// Block the hazard.
    pub fn block(&self) {
        self.ptr.store(0, atomic::Ordering::Release);
    }

    /// Set the hazard to a new state.
    ///
    /// Whether or not it is blocked has no effect on this. To get it back to the blocked state,
    /// use `self.block()`.
    pub fn set(&self, new: State) {
        // Simply encode and store.
        self.ptr.store(match new {
            State::Free => 1,
            State::Dead => 2,
            State::Protect(ptr) => ptr as usize,
        }, atomic::Ordering::Release);
    }

    /// Get the state of the hazard.
    ///
    /// It will spin until the hazard is no longer in a blocked state.
    pub fn get(&self) -> State {
        // Spin until not blocked.
        loop {
            return match self.ptr.load(atomic::Ordering::Acquire) {
                // 0 means that the hazard is blocked by another thread, and we must loop until it
                // assumes another state.
                0 => continue,
                1 => State::Free,
                2 => State::Dead,
                ptr => State::Protect(ptr as *const u8)
            };
        }
    }
}

/// Create a new hazard reader-writer pair.
///
/// This creates a new hazard pair in blocked state.
pub fn create() -> (Writer, Reader) {
    // Allocate the hazard on the heap.
    let ptr: &'static Hazard = unsafe { &*Box::into_raw(Box::new(Hazard::blocked())) };

    // Construct the values.
    (Writer {
        ptr: ptr,
    }, Reader {
        ptr: ptr,
    })
}

/// An hazard reader.
///
/// This wraps a hazard and provides only ability to read and deallocate it. It is created through
/// the `create()` function.
///
/// The destructor will, for the sake of safety, panic. To deallocate, use `self.destroy()`
/// instead.
pub struct Reader {
    /// The pointer to the heap-allocated hazard.
    ptr: &'static Hazard,
}

impl Reader {
    /// Get the state of the hazard.
    pub fn get(&self) -> State {
        self.ptr.get()
    }

    /// Destroy the hazard.
    ///
    /// # Safety
    ///
    /// This is unsafe as it relies on the writer part being dead and not used anymore. There is
    /// currently no way to express this invariant through the type system, so we must rely on the
    /// caller to ensure that.
    ///
    /// # Panics
    ///
    /// In debug mode, this will panic if the state of the hazard is not "dead".
    pub unsafe fn destroy(self) {
        debug_assert!(self.get() == State::Dead, "Prematurely freeing an active hazard.");

        // Load the pointer and deallocate it.
        Box::from_raw(self.ptr as *const Hazard as *mut Hazard);
        // Ensure that the RAII destructor doesn't kick in and crashes the program.
        mem::forget(self);
    }
}

impl Drop for Reader {
    fn drop(&mut self) {
        panic!("Hazard readers ought to be destroyed manually through the `destroy` method.");
    }
}

/// An hazard reader.
///
/// This wraps a hazard and provides only ability to read and deallocate it. It is created through
/// the `create()` function.
///
/// The destructor relocate the hazard to the thread-local cache.
#[derive(Debug)]
pub struct Writer {
    /// The pointer to the heap-allocated hazard.
    ptr: &'static Hazard,
}

impl Writer {
    /// Set the state of this hazard to "dead".
    ///
    /// This will ensure that the hazard won't end up in the thread-local cache, by (eventually)
    /// deleting it globally.
    ///
    /// Generally, this is not recommended, as it means that your hazard cannot be reused.
    pub fn kill(self) {
        // Set the state to dead.
        self.set(State::Dead);
        // Avoid the RAII destructor.
        mem::forget(self);
    }
}

impl ops::Deref for Writer {
    type Target = Hazard;

    fn deref(&self) -> &Hazard {
        self.ptr
    }
}

impl Drop for Writer {
    fn drop(&mut self) {
        // Implementation note: Freeing to local state in the destructor does lead to issues with
        // panicking, which this conditional is supposed to solve. The alternative is to outright
        // set the hazard to state "dead", disregarding whether the thread is panicking or not.
        // This is fairly nice for some purposes, but it makes it necessary to store an `Option` in
        // `Guard`, as one must avoid the hazard from being set to state "dead" after being
        // relocated to the local state. As such, this approach (where the destructor automatically
        // puts the hazard back into the local cache) is nicer. For more information on its
        // alternative, see commit b7047c263cbd614b7c828d68b29d7928be543623.
        if thread::panicking() {
            // If the thread is unwinding, there is no point in putting it back in the thread-local
            // cache. In fact, it might cause problems, if the unwinding tries to garbage collect
            // and the hazard is in blocked state. For this reason, we simply set the state to
            // "dead" and move on.
            self.ptr.set(State::Dead);
        } else {
            // Free the hazard to the thread-local cache. We have to clone the hazard to get around the
            // fact that `drop` takes `&mut self`.
            local::free_hazard(Writer {
                ptr: self.ptr,
            });
        }
    }
}
<commit_msg>Add a debug assertion against faulty hazards.<commit_after>//! Hazard pointer management.
//!
//! "Hazard" is the general name for an atomic pointer potentially "protecting" some object from
//! getting deleted. First when the hazard is changed to another state, the object can be deleted.
//!
//! Since hazards are only useful when they're shared between the global and local state, a "hazard
//! pair" refers to the collection of the two connected ends of the hazard (they both share a
//! pointer to the hazard on the heap). When such pair is created, its reading end is usually
//! stored in the global state, such that a thread can check that no hazard is matching a
//! particular object during garbage collection. The writer part controls what the state of the
//! hazard is and is usually passed around locally.
//!
//! The asymmetry of a hazard pair is strictly speaking not necessary, but it allows to enforce
//! rules (e.g. only the reader/global part may deallocate the hazard box).

use std::sync::atomic::{self, AtomicUsize};
use std::{ops, mem, thread};

use local;

/// The state of a hazard.
///
/// Note that this `enum` excludes the blocked state, because it is semantically different from the
/// other states.
#[derive(PartialEq)]
pub enum State {
    /// The hazard does not currently protect any object.
    Free,
    /// The hazard is dead and may be deallocated when necessary.
    Dead,
    /// The hazard is protecting an object.
    ///
    /// "Protecting" means that the pointer it holds is not deleted while the hazard is in this
    /// state.
    Protect(*const u8),
}

/// A hazard.
///
/// This type holds an atomic pointer with the state of the hazard. It represents the same as
/// `State` but is encoded such that it is atomically accessible.
///
/// Furthermore, there is an additional state: Blocked. If the hazard is in this state, reading it
/// will block until it no longer is. This is useful for blocking garbage collection while a value
/// is being read (avoiding the ABA problem).
#[derive(Debug)]
pub struct Hazard {
    /// The inner atomic value.
    ///
    /// This takes following forms:
    ///
    /// - 0: blocked.
    /// - 1: free.
    /// - 2: dead
    /// - otherwise: protecting the address represented by the `usize`.
    ptr: AtomicUsize,
}

impl Hazard {
    /// Create a new hazard in blocked state.
    pub fn blocked() -> Hazard {
        Hazard {
            ptr: AtomicUsize::new(0),
        }
    }

    /// Block the hazard.
    pub fn block(&self) {
        self.ptr.store(0, atomic::Ordering::Release);
    }

    /// Set the hazard to a new state.
    ///
    /// Whether or not it is blocked has no effect on this. To get it back to the blocked state,
    /// use `self.block()`.
    pub fn set(&self, new: State) {
        // Simply encode and store.
        self.ptr.store(match new {
            State::Free => 1,
            State::Dead => 2,
            State::Protect(ptr) => {
                debug_assert!(ptr as usize > 2, "Protecting an invalid pointer, colliding with a \
                              trap value of the hazard. This likely happens because you have stored \
                              or corrupted the pointer in a container. Ensure that you only store \
                              valid values in hazards.");
                ptr as usize
            }
        }, atomic::Ordering::Release);
    }

    /// Get the state of the hazard.
    ///
    /// It will spin until the hazard is no longer in a blocked state.
    pub fn get(&self) -> State {
        // Spin until not blocked.
        loop {
            return match self.ptr.load(atomic::Ordering::Acquire) {
                // 0 means that the hazard is blocked by another thread, and we must loop until it
                // assumes another state.
                0 => continue,
                1 => State::Free,
                2 => State::Dead,
                ptr => State::Protect(ptr as *const u8)
            };
        }
    }
}

/// Create a new hazard reader-writer pair.
///
/// This creates a new hazard pair in blocked state.
pub fn create() -> (Writer, Reader) {
    // Allocate the hazard on the heap.
    let ptr: &'static Hazard = unsafe { &*Box::into_raw(Box::new(Hazard::blocked())) };

    // Construct the values.
    (Writer {
        ptr: ptr,
    }, Reader {
        ptr: ptr,
    })
}

/// An hazard reader.
///
/// This wraps a hazard and provides only ability to read and deallocate it. It is created through
/// the `create()` function.
///
/// The destructor will, for the sake of safety, panic. To deallocate, use `self.destroy()`
/// instead.
pub struct Reader {
    /// The pointer to the heap-allocated hazard.
    ptr: &'static Hazard,
}

impl Reader {
    /// Get the state of the hazard.
    pub fn get(&self) -> State {
        self.ptr.get()
    }

    /// Destroy the hazard.
    ///
    /// # Safety
    ///
    /// This is unsafe as it relies on the writer part being dead and not used anymore. There is
    /// currently no way to express this invariant through the type system, so we must rely on the
    /// caller to ensure that.
    ///
    /// # Panics
    ///
    /// In debug mode, this will panic if the state of the hazard is not "dead".
    pub unsafe fn destroy(self) {
        debug_assert!(self.get() == State::Dead, "Prematurely freeing an active hazard.");

        // Load the pointer and deallocate it.
        Box::from_raw(self.ptr as *const Hazard as *mut Hazard);
        // Ensure that the RAII destructor doesn't kick in and crashes the program.
        mem::forget(self);
    }
}

impl Drop for Reader {
    fn drop(&mut self) {
        panic!("Hazard readers ought to be destroyed manually through the `destroy` method.");
    }
}

/// An hazard reader.
///
/// This wraps a hazard and provides only ability to read and deallocate it. It is created through
/// the `create()` function.
///
/// The destructor relocate the hazard to the thread-local cache.
#[derive(Debug)]
pub struct Writer {
    /// The pointer to the heap-allocated hazard.
    ptr: &'static Hazard,
}

impl Writer {
    /// Set the state of this hazard to "dead".
    ///
    /// This will ensure that the hazard won't end up in the thread-local cache, by (eventually)
    /// deleting it globally.
    ///
    /// Generally, this is not recommended, as it means that your hazard cannot be reused.
    pub fn kill(self) {
        // Set the state to dead.
        self.set(State::Dead);
        // Avoid the RAII destructor.
        mem::forget(self);
    }
}

impl ops::Deref for Writer {
    type Target = Hazard;

    fn deref(&self) -> &Hazard {
        self.ptr
    }
}

impl Drop for Writer {
    fn drop(&mut self) {
        // Implementation note: Freeing to local state in the destructor does lead to issues with
        // panicking, which this conditional is supposed to solve. The alternative is to outright
        // set the hazard to state "dead", disregarding whether the thread is panicking or not.
        // This is fairly nice for some purposes, but it makes it necessary to store an `Option` in
        // `Guard`, as one must avoid the hazard from being set to state "dead" after being
        // relocated to the local state. As such, this approach (where the destructor automatically
        // puts the hazard back into the local cache) is nicer. For more information on its
        // alternative, see commit b7047c263cbd614b7c828d68b29d7928be543623.
        if thread::panicking() {
            // If the thread is unwinding, there is no point in putting it back in the thread-local
            // cache. In fact, it might cause problems, if the unwinding tries to garbage collect
            // and the hazard is in blocked state. For this reason, we simply set the state to
            // "dead" and move on.
            self.ptr.set(State::Dead);
        } else {
            // Free the hazard to the thread-local cache. We have to clone the hazard to get around the
            // fact that `drop` takes `&mut self`.
            local::free_hazard(Writer {
                ptr: self.ptr,
            });
        }
    }
}
<|endoftext|>


<commit_before>// pest. The Elegant Parser
// Copyright (c) 2018 Dragoș Tiselice
//
// Licensed under the Apache License, Version 2.0
// <LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0> or the MIT
// license <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. All files in the project carrying such notice may not be copied,
// modified, or distributed except according to those terms.
#![cfg_attr(feature = "const_prec_climber", feature(const_fn))]
#![cfg_attr(not(feature = "std"), no_std)]

//! # pest. The Elegant Parser
//!
//! pest is a general purpose parser written in Rust with a focus on accessibility, correctness,
//! and performance. It uses parsing expression grammars (or [PEG]) as input, which are similar in
//! spirit to regular expressions, but which offer the enhanced expressivity needed to parse
//! complex languages.
//!
//! [PEG]: https://en.wikipedia.org/wiki/Parsing_expression_grammar
//!
//! ## Getting started
//!
//! The recommended way to start parsing with pest is to read the official [book].
//!
//! Other helpful resources:
//!
//! * API reference on [docs.rs]
//! * play with grammars and share them on our [fiddle]
//! * leave feedback, ask questions, or greet us on [Gitter]
//!
//! [book]: https://pest-parser.github.io/book
//! [docs.rs]: https://docs.rs/pest
//! [fiddle]: https://pest-parser.github.io/#editor
//! [Gitter]: https://gitter.im/dragostis/pest
//!
//! ## Usage
//!
//! The core of pest is the trait [`Parser`], which provides an interface to the parsing
//! functionality.
//!
//! The accompanying crate `pest_derive` can automatically generate a [`Parser`] from a PEG
//! grammar. Using `pest_derive` is highly encouraged, but it is also possible to implement
//! [`Parser`] manually if required.
//!
//! ## `.pest` files
//!
//! Grammar definitions reside in custom `.pest` files located in the crate `src` directory.
//! Parsers are automatically generated from these files using `#[derive(Parser)]` and a special
//! `#[grammar = "..."]` attribute on a dummy struct.
//!
//! ```ignore
//! #[derive(Parser)]
//! #[grammar = "path/to/my_grammar.pest"] // relative to src
//! struct MyParser;
//! ```
//!
//! The syntax of `.pest` files is documented in the [`pest_derive` crate].
//!
//! ## Inline grammars
//!
//! Grammars can also be inlined by using the `#[grammar_inline = "..."]` attribute.
//!
//! [`Parser`]: trait.Parser.html
//! [`pest_derive` crate]: https://docs.rs/pest_derive/

#![doc(html_root_url = "https://docs.rs/pest")]

extern crate alloc;
#[cfg(feature = "std")]
extern crate core;
extern crate ucd_trie;

#[cfg(feature = "pretty-print")]
extern crate serde;
#[cfg(feature = "pretty-print")]
extern crate serde_json;

use core::fmt::Debug;
use core::hash::Hash;
pub use parser::Parser;
pub use parser_state::{state, Atomicity, Lookahead, MatchDir, ParseResult, ParserState};
pub use position::Position;
pub use span::{Lines, Span};
pub use token::Token;

pub mod error;
pub mod iterators;
mod macros;
mod parser;
mod parser_state;
mod position;
pub mod prec_climber;
mod span;
mod stack;
mod token;
#[doc(hidden)]
pub mod unicode;

/// A trait which parser rules must implement.
///
/// This trait is set up so that any struct that implements all of its required traits will
/// automatically implement this trait as well.
///
/// This is essentially a [trait alias](https://github.com/rust-lang/rfcs/pull/1733). When trait
/// aliases are implemented, this may be replaced by one.
pub trait RuleType: Copy + Debug + Eq + Hash + Ord {}

impl<T: Copy + Debug + Eq + Hash + Ord> RuleType for T {}
<commit_msg>Unconditionally use no_std and include std only if feature is enabled.<commit_after>// pest. The Elegant Parser
// Copyright (c) 2018 Dragoș Tiselice
//
// Licensed under the Apache License, Version 2.0
// <LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0> or the MIT
// license <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. All files in the project carrying such notice may not be copied,
// modified, or distributed except according to those terms.
#![no_std]
#![cfg_attr(feature = "const_prec_climber", feature(const_fn))]

//! # pest. The Elegant Parser
//!
//! pest is a general purpose parser written in Rust with a focus on accessibility, correctness,
//! and performance. It uses parsing expression grammars (or [PEG]) as input, which are similar in
//! spirit to regular expressions, but which offer the enhanced expressivity needed to parse
//! complex languages.
//!
//! [PEG]: https://en.wikipedia.org/wiki/Parsing_expression_grammar
//!
//! ## Getting started
//!
//! The recommended way to start parsing with pest is to read the official [book].
//!
//! Other helpful resources:
//!
//! * API reference on [docs.rs]
//! * play with grammars and share them on our [fiddle]
//! * leave feedback, ask questions, or greet us on [Gitter]
//!
//! [book]: https://pest-parser.github.io/book
//! [docs.rs]: https://docs.rs/pest
//! [fiddle]: https://pest-parser.github.io/#editor
//! [Gitter]: https://gitter.im/dragostis/pest
//!
//! ## Usage
//!
//! The core of pest is the trait [`Parser`], which provides an interface to the parsing
//! functionality.
//!
//! The accompanying crate `pest_derive` can automatically generate a [`Parser`] from a PEG
//! grammar. Using `pest_derive` is highly encouraged, but it is also possible to implement
//! [`Parser`] manually if required.
//!
//! ## `.pest` files
//!
//! Grammar definitions reside in custom `.pest` files located in the crate `src` directory.
//! Parsers are automatically generated from these files using `#[derive(Parser)]` and a special
//! `#[grammar = "..."]` attribute on a dummy struct.
//!
//! ```ignore
//! #[derive(Parser)]
//! #[grammar = "path/to/my_grammar.pest"] // relative to src
//! struct MyParser;
//! ```
//!
//! The syntax of `.pest` files is documented in the [`pest_derive` crate].
//!
//! ## Inline grammars
//!
//! Grammars can also be inlined by using the `#[grammar_inline = "..."]` attribute.
//!
//! [`Parser`]: trait.Parser.html
//! [`pest_derive` crate]: https://docs.rs/pest_derive/

#![doc(html_root_url = "https://docs.rs/pest")]

extern crate alloc;
#[cfg(feature = "std")]
extern crate std;
extern crate ucd_trie;

#[cfg(feature = "pretty-print")]
extern crate serde;
#[cfg(feature = "pretty-print")]
extern crate serde_json;

use core::fmt::Debug;
use core::hash::Hash;
pub use parser::Parser;
pub use parser_state::{state, Atomicity, Lookahead, MatchDir, ParseResult, ParserState};
pub use position::Position;
pub use span::{Lines, Span};
pub use token::Token;

pub mod error;
pub mod iterators;
mod macros;
mod parser;
mod parser_state;
mod position;
pub mod prec_climber;
mod span;
mod stack;
mod token;
#[doc(hidden)]
pub mod unicode;

/// A trait which parser rules must implement.
///
/// This trait is set up so that any struct that implements all of its required traits will
/// automatically implement this trait as well.
///
/// This is essentially a [trait alias](https://github.com/rust-lang/rfcs/pull/1733). When trait
/// aliases are implemented, this may be replaced by one.
pub trait RuleType: Copy + Debug + Eq + Hash + Ord {}

impl<T: Copy + Debug + Eq + Hash + Ord> RuleType for T {}
<|endoftext|>


<commit_before>#![feature(set_stdio)]

extern crate crossbeam;
extern crate env_logger;
#[macro_use] extern crate log;
extern crate rl_sys;
extern crate nix;
extern crate term;

extern crate flang_common as common;
extern crate flang_errors as errors;
extern crate parser;

use std::cell::RefCell;
use std::collections::HashMap;
use std::ffi::CString;
use std::io::{self, Write};
use std::sync::mpsc::channel;
use std::panic::catch_unwind;
use std::rc::Rc;
use std::str;
use std::sync::{Arc, Mutex};
use std::thread;

use nix::libc;
use rl_sys::readline;

mod directive;
use InputType::*;
use ErrorKind::*;

pub use common::driver::{DriverEnv, ErrorDestination};
use errors::{DiagnosticBuilder, Handler};
use errors::emitter::{ColorConfig, Emitter, EmitterWriter};
use parser::ast::StmtKind::*;
use parser::codemap::CodeMap;
use parser::lexer::{Reader, StringReader};
use parser::parser::{filemap_to_parser, parse_tts_from_source_str, ParseSess, Parser, PResult};
use parser::tokenstream::TokenTree;

static WELCOME_MSG: &'static str =
  "Welcome to Flang version 0.1. (Type :help for assistance.)\n";
static DEFAULT_PROMPT: &'static str = "\x1b[33mflang> \x1b[0m";
static CONTINUE_PROMPT: &'static str = "\x1b[33m> \x1b[0m";
static REPL_DUMMY_FILENAME: &'static str = "console.fl";

pub enum InputType<'a> {
  Directive(&'a str, Vec<&'a str>),
  ExternalComamnd(&'a str),
  ExecuteSource(&'a str),
  Empty,
}

pub enum ReplAction {
  Done,
  Error,
  Continue,
  Quit
}

unsafe impl Send for ReplAction {}

pub struct Repl {
  unexecuted_src: SourceFile,
  executed_src: SourceFile,
  parsess: ParseSess,
  env: DriverEnv, // stream err,
}

impl Repl {
  pub fn new(env: DriverEnv) -> Repl {
    Repl {
      unexecuted_src: SourceFile::new(),
      executed_src: SourceFile::new(),
      parsess: ParseSess::new(),
      env: env,
    }
  }

  fn println(&self, msg: &str) {
    self.env.errdst.borrow_mut().write(msg.as_bytes()).ok();
  }

  pub fn run(&mut self) {
    self.println(WELCOME_MSG);

    let mut prompt: String = DEFAULT_PROMPT.to_string();

    loop {
      match readline::readline(&prompt) {
        Ok(Some(line)) => {
          match self.handle_line(&line) {
            ReplAction::Done | ReplAction::Error => {
              prompt = DEFAULT_PROMPT.to_string();
            }
            ReplAction::Continue => prompt = CONTINUE_PROMPT.to_string(),
            ReplAction::Quit => break
          }
        }
        Ok(None) => break, // eof
        Err(msg) => {
          self.println(&format!("ERROR: {}", msg));
        }
      }
    }
  }

  fn handle_line(&mut self, line: &str) -> ReplAction {
    match self.parse_input(line) {
      Directive(d, args) => self.exec_directive(d, args),
      ExecuteSource(line) => self.exec_line(line),
      ExternalComamnd(command) => self.exec_external_program(command),
      Empty => ReplAction::Done // just enter without any type
    }
  }

  fn parse_input<'a>(&self, line: &'a str) -> InputType<'a> {
    let mut tokens = line.split_whitespace();
    let first_token = tokens.next();

    match first_token {
      Some(first) => match first {
        x if x.starts_with(':') => {
          Directive(&first[1..], tokens.collect::<Vec<_>>())
        }
        x if x.starts_with('!') => ExternalComamnd(&line[1..]),
        _ => ExecuteSource(line)
      },
      None => Empty
    }
  }

  pub fn exec_directive(&self, directive: &str, args: Vec<&str>) -> ReplAction {
    match directive {
      "dump" => {
        self.println(self.executed_src.as_str());
        ReplAction::Done
      }
      "quit" => ReplAction::Quit,
      _ => ReplAction::Done
    }
  }

  fn new_emitter(errdst: ErrorDestination, cm: Rc<CodeMap>)
      -> (Box<Emitter>, Rc<RefCell<Vec<ErrorKind>>>) {
    let ew = match errdst {
      ErrorDestination::Stderr => {
        EmitterWriter::stderr(ColorConfig::Auto, Some(cm))
      }
      ErrorDestination::Raw(ref buf) => {
        let delegator = WriteDelegator {write: buf.clone()};
        EmitterWriter::new(Box::new(delegator), Some(cm))
      }
    };

    let (emitter, errkinds) = EmitterDelegator::new(ew);
    (Box::new(emitter), errkinds)
  }

  fn need_more_liens(errs: &Rc<RefCell<Vec<ErrorKind>>>) -> bool {
    errs.borrow().len() == 1 && match errs.borrow()[0] {
      UnclosedDelimiter => true,
      _ => false
    }
  }

  fn is_error(errs: &Rc<RefCell<Vec<ErrorKind>>>) -> bool {
    if errs.borrow().len() == 0 {
      return false;
    }

    if errs.borrow().len() > 2 {
      return true;
    }

    // only if errors.len() == 1
    match errs.borrow()[0] {
      UnclosedDelimiter => false,
      _ => true
    }
  }

  pub fn exec_line(&mut self, line: &str) -> ReplAction {

    // Temporarily have stack size set to 16MB to deal with nom-using crates failing
    const STACK_SIZE: usize = 16 * 1024 * 1024; // 16MB

    struct Sink(Arc<Mutex<Vec<u8>>>);
    impl Write for Sink {
        fn write(&mut self, data: &[u8]) -> io::Result<usize> {
            Write::write(&mut *self.0.lock().unwrap(), data)
        }
        fn flush(&mut self) -> io::Result<()> {
            Ok(())
        }
    }

    let data = Arc::new(Mutex::new(Vec::new()));
    let err = Sink(data.clone());

    //let (tx, rx) = channel();
    let line = line.to_owned();
    let mut unexecuted_src = self.unexecuted_src.as_str().to_owned();
    let errdst = self.env.errdst.borrow().clone();

    let task = thread::Builder::new().name("parsing".to_owned());
    let handle = task.spawn(move || {
      io::set_panic(Some(Box::new(err)));

      unexecuted_src.push_str(&line);
      unexecuted_src.push_str("\n");

      let cm = Rc::new(CodeMap::new());
      let (emitter, errs) = Repl::new_emitter(errdst, cm.clone());
      let handler = Handler::with_emitter(true, false, emitter);
      let parsess = ParseSess::with_span_handler(handler, cm.clone());
      let mut parser = parse_flang(&parsess, &unexecuted_src);

      if Repl::need_more_liens(&errs) {
        println!("need more lines");
        return (ReplAction::Continue, unexecuted_src, "".to_owned());
      } else if Repl::is_error(&errs) {
        return (ReplAction::Error, "".to_owned(), "".to_owned());
      }

      match parser.parse_item() {
        Ok(Some(item)) => {
          println!("item");
        }
        Ok(None) => println!("no item"),
        Err(ref mut e) => {
          println!("not item");
          e.cancel();
        }
      };

      match parser.parse_full_stmt() {
        Ok(Some(stmt)) => {
          match stmt.node {
            Local(l) => println!("local"),
            Item(i) => println!("item"),
            Expr(e) => println!("expr"),
            Semi(s) => println!("semi"),
          }
        }
        Ok(None) => println!("no stmt"),
        Err(e) => println!("not stmt"),
      };

      (ReplAction::Done, "".to_owned(), unexecuted_src.to_owned())
    }).unwrap();

    let (action, unexecuted_src, executed_src) = match handle.join() {
      Ok(result) => result,

      Err(value) => {
        // if it is a real error or bug, it will print out the stacktrace
        if !value.is::<errors::FatalError>() {
          writeln!(io::stderr(), "{}",
            str::from_utf8(&data.lock().unwrap()).unwrap()).unwrap();
        }
        (ReplAction::Error, "".to_owned(), "".to_owned())
      }
    };

    self.unexecuted_src.update(&unexecuted_src);
    self.executed_src.update(&executed_src);

    // append source
    // generate mir
    // check ast error
    // add source which is valid
    // skip if the source doesn't need to run
    // generate llvm ir and module
    // reorganize llvm module

    action
  }

  pub fn exec_external_program(&self, command: &str) -> ReplAction {
    let c_to_print = CString::new(command).unwrap();
    unsafe { libc::system(c_to_print.as_ptr()); }
    ReplAction::Done
  }
}

fn parse_flang<'a>(parsess: &'a ParseSess, line: &str) -> Parser<'a> {
  let filemap = parsess
    .codemap()
    .new_filemap_and_lines(REPL_DUMMY_FILENAME, None, line);
  filemap_to_parser(&parsess, filemap)
}

pub struct WriteDelegator {
  write: Rc<RefCell<Box<io::Write + Send>>>
}

unsafe impl Send for WriteDelegator {}

impl io::Write for WriteDelegator {
  fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
    self.write.borrow_mut().write(buf)
  }

  fn flush(&mut self) -> io::Result<()> {
    self.write.borrow_mut().flush()
  }
}

pub struct SourceFile {
  source: String
}

impl SourceFile {
  pub fn new() -> SourceFile {
    SourceFile { source : String::new() }
  }

  pub fn add_line(&mut self, line: &str) {
    self.source.push_str(line);
    self.source.push_str("\n");
  }

  pub fn as_str(&self) -> &str {
    &self.source
  }

  pub fn as_bytes(&self) -> &[u8] {
    self.source.as_bytes()
  }

  pub fn clear(&mut self) {
    self.source.clear();
  }

  pub fn update(&mut self, lines: &str) {
    self.clear();
    self.source.push_str(lines);
  }
}

unsafe impl Sync for SourceFile {}

enum ErrorKind {
  UnclosedDelimiter,
  Unknown
}

struct EmitterDelegator {
  em: EmitterWriter,
  errors: Rc<RefCell<Vec<ErrorKind>>>
}

impl EmitterDelegator {
  pub fn new(em : EmitterWriter)
      -> (EmitterDelegator, Rc<RefCell<Vec<ErrorKind>>>) {
    let errors = Rc::new(RefCell::new(Vec::new()));
    let delegator = EmitterDelegator { em : em, errors: errors.clone() };
    (delegator, errors)
  }
}

impl Emitter for EmitterDelegator {
  fn emit(&mut self, db: &DiagnosticBuilder) {

    let errkind = error_kind(db.message());

    match errkind {
      UnclosedDelimiter => {},
      _ => self.em.emit(db)
    };

    self.errors.borrow_mut().push(errkind);
  }
}

fn error_kind(msg: &str) -> ErrorKind {
  if is_unclosed_delemiter(msg) {
    ErrorKind::UnclosedDelimiter
  } else {
    Unknown
  }
}

fn is_unclosed_delemiter(msg: &str) -> bool {
  msg == "this file contains an un-closed delimiter"
}<commit_msg>Clean up repl and remove test codes.<commit_after>#![feature(set_stdio)]

extern crate crossbeam;
extern crate env_logger;
#[macro_use] extern crate log;
extern crate rl_sys;
extern crate nix;
extern crate term;

extern crate flang_common as common;
extern crate flang_errors as errors;
extern crate parser;

use std::cell::RefCell;
use std::collections::HashMap;
use std::ffi::CString;
use std::io::{self, Write};
use std::sync::mpsc::channel;
use std::panic::catch_unwind;
use std::rc::Rc;
use std::str;
use std::sync::{Arc, Mutex};
use std::thread;

use nix::libc;
use rl_sys::readline;

mod directive;
use InputType::*;
use ErrorKind::*;

pub use common::driver::{DriverEnv, ErrorDestination};
use errors::{DiagnosticBuilder, Handler};
use errors::emitter::{ColorConfig, Emitter, EmitterWriter};
use parser::ast::Stmt;
use parser::ast::StmtKind::*;
use parser::codemap::CodeMap;
use parser::lexer::{Reader, StringReader};
use parser::parser::{filemap_to_parser, parse_tts_from_source_str, ParseSess, Parser, PResult};
use parser::tokenstream::TokenTree;

static WELCOME_MSG: &'static str =
  "Welcome to Flang version 0.1. (Type :help for assistance.)\n";
static DEFAULT_PROMPT: &'static str = "\x1b[33mflang> \x1b[0m";
static CONTINUE_PROMPT: &'static str = "\x1b[33m> \x1b[0m";
static REPL_DUMMY_FILENAME: &'static str = "console.fl";

pub enum InputType<'a> {
  Directive(&'a str, Vec<&'a str>),
  ExternalComamnd(&'a str),
  ExecuteSource(&'a str),
  Empty,
}

pub enum ReplAction {
  Done,
  Error,
  Continue,
  Quit
}

unsafe impl Send for ReplAction {}

pub struct Repl {
  unexecuted_src: SourceFile,
  executed_src: SourceFile,
  parsess: ParseSess,
  env: DriverEnv, // stream err,
}

impl Repl {
  pub fn new(env: DriverEnv) -> Repl {
    Repl {
      unexecuted_src: SourceFile::new(),
      executed_src: SourceFile::new(),
      parsess: ParseSess::new(),
      env: env,
    }
  }

  fn println(&self, msg: &str) {
    self.env.errdst.borrow_mut().write(msg.as_bytes()).ok();
  }

  pub fn run(&mut self) {
    self.println(WELCOME_MSG);

    let mut prompt: String = DEFAULT_PROMPT.to_string();

    loop {
      match readline::readline(&prompt) {
        Ok(Some(line)) => {
          match self.handle_line(&line) {
            ReplAction::Done | ReplAction::Error => {
              prompt = DEFAULT_PROMPT.to_string();
            }
            ReplAction::Continue => prompt = CONTINUE_PROMPT.to_string(),
            ReplAction::Quit => break
          }
        }
        Ok(None) => break, // eof
        Err(msg) => {
          self.println(&format!("ERROR: {}", msg));
        }
      }
    }
  }

  fn handle_line(&mut self, line: &str) -> ReplAction {
    match self.parse_input(line) {
      Directive(d, args) => self.exec_directive(d, args),
      ExecuteSource(line) => self.exec_line(line),
      ExternalComamnd(command) => self.exec_external_program(command),
      Empty => ReplAction::Done // just enter without any type
    }
  }

  fn parse_input<'a>(&self, line: &'a str) -> InputType<'a> {
    let mut tokens = line.split_whitespace();
    let first_token = tokens.next();

    match first_token {
      Some(first) => match first {
        x if x.starts_with(':') => {
          Directive(&first[1..], tokens.collect::<Vec<_>>())
        }
        x if x.starts_with('!') => ExternalComamnd(&line[1..]),
        _ => ExecuteSource(line)
      },
      None => Empty
    }
  }

  pub fn exec_directive(&self, directive: &str, args: Vec<&str>) -> ReplAction {
    match directive {
      "dump" => {
        self.println(self.executed_src.as_str());
        ReplAction::Done
      }
      "quit" => ReplAction::Quit,
      _ => ReplAction::Done
    }
  }

  fn new_emitter(errdst: ErrorDestination, cm: Rc<CodeMap>)
      -> (Box<Emitter>, Rc<RefCell<Vec<ErrorKind>>>) {
    let ew = match errdst {
      ErrorDestination::Stderr => {
        EmitterWriter::stderr(ColorConfig::Auto, Some(cm))
      }
      ErrorDestination::Raw(ref buf) => {
        let delegator = WriteDelegator {write: buf.clone()};
        EmitterWriter::new(Box::new(delegator), Some(cm))
      }
    };

    let (emitter, errkinds) = EmitterDelegator::new(ew);
    (Box::new(emitter), errkinds)
  }

  fn need_more_liens(errs: &Rc<RefCell<Vec<ErrorKind>>>) -> bool {
    errs.borrow().len() == 1 && match errs.borrow()[0] {
      UnclosedDelimiter => true,
      _ => false
    }
  }

  fn is_error(errs: &Rc<RefCell<Vec<ErrorKind>>>) -> bool {
    if errs.borrow().len() == 0 {
      return false;
    }

    if errs.borrow().len() > 2 {
      return true;
    }

    // only if errors.len() == 1
    match errs.borrow()[0] {
      UnclosedDelimiter => false,
      _ => true
    }
  }

  pub fn exec_line(&mut self, line: &str) -> ReplAction {

    // Temporarily have stack size set to 16MB to deal with nom-using crates failing
    const STACK_SIZE: usize = 16 * 1024 * 1024; // 16MB

    struct Sink(Arc<Mutex<Vec<u8>>>);
    impl Write for Sink {
        fn write(&mut self, data: &[u8]) -> io::Result<usize> {
            Write::write(&mut *self.0.lock().unwrap(), data)
        }
        fn flush(&mut self) -> io::Result<()> {
            Ok(())
        }
    }

    let data = Arc::new(Mutex::new(Vec::new()));
    let err = Sink(data.clone());

    //let (tx, rx) = channel();
    let line = line.to_owned();
    let mut unexecuted_src = self.unexecuted_src.as_str().to_owned();
    let errdst = self.env.errdst.borrow().clone();

    let task = thread::Builder::new().name("parsing".to_owned());
    let handle = task.spawn(move || {
      io::set_panic(Some(Box::new(err)));

      unexecuted_src.push_str(&line);
      unexecuted_src.push_str("\n");

      let cm = Rc::new(CodeMap::new());
      let (emitter, errs) = Repl::new_emitter(errdst, cm.clone());
      let handler = Handler::with_emitter(true, false, emitter);
      let parsess = ParseSess::with_span_handler(handler, cm.clone());
      let mut parser = parse_flang(&parsess, &unexecuted_src);

      if Repl::need_more_liens(&errs) {
        println!("need more lines");
        return (ReplAction::Continue, unexecuted_src, "".to_owned());
      } else if Repl::is_error(&errs) {
        return (ReplAction::Error, "".to_owned(), "".to_owned());
      }

      let item = match parser.parse_item() {
        Ok(item) => item,
        Err(ref mut e) => {
          e.cancel();
          None
        }
      };

      let stmt = if item.is_none() {
        match parser.parse_full_stmt() {
          Ok(stmt) => stmt,
          Err(_) => None,
        }
      } else {
        None
      };

      (ReplAction::Done, "".to_owned(), unexecuted_src.to_owned())
    }).unwrap();

    let (action, unexecuted_src, executed_src) = match handle.join() {
      Ok(result) => result,

      Err(value) => {
        // if it is a real error or bug, it will print out the stacktrace
        if !value.is::<errors::FatalError>() {
          writeln!(io::stderr(), "{}",
            str::from_utf8(&data.lock().unwrap()).unwrap()).unwrap();
        }
        (ReplAction::Error, "".to_owned(), "".to_owned())
      }
    };

    self.unexecuted_src.update(&unexecuted_src);
    self.executed_src.update(&executed_src);

    // append source
    // generate mir
    // check ast error
    // add source which is valid
    // skip if the source doesn't need to run
    // generate llvm ir and module
    // reorganize llvm module

    action
  }

  pub fn exec_external_program(&self, command: &str) -> ReplAction {
    let c_to_print = CString::new(command).unwrap();
    unsafe { libc::system(c_to_print.as_ptr()); }
    ReplAction::Done
  }
}

fn parse_flang<'a>(parsess: &'a ParseSess, line: &str) -> Parser<'a> {
  let filemap = parsess
    .codemap()
    .new_filemap_and_lines(REPL_DUMMY_FILENAME, None, line);
  filemap_to_parser(&parsess, filemap)
}

pub struct WriteDelegator {
  write: Rc<RefCell<Box<io::Write + Send>>>
}

unsafe impl Send for WriteDelegator {}

impl io::Write for WriteDelegator {
  fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
    self.write.borrow_mut().write(buf)
  }

  fn flush(&mut self) -> io::Result<()> {
    self.write.borrow_mut().flush()
  }
}

pub struct SourceFile {
  source: String
}

impl SourceFile {
  pub fn new() -> SourceFile {
    SourceFile { source : String::new() }
  }

  pub fn add_line(&mut self, line: &str) {
    self.source.push_str(line);
    self.source.push_str("\n");
  }

  pub fn as_str(&self) -> &str {
    &self.source
  }

  pub fn as_bytes(&self) -> &[u8] {
    self.source.as_bytes()
  }

  pub fn clear(&mut self) {
    self.source.clear();
  }

  pub fn update(&mut self, lines: &str) {
    self.clear();
    self.source.push_str(lines);
  }
}

unsafe impl Sync for SourceFile {}

enum ErrorKind {
  UnclosedDelimiter,
  Unknown
}

struct EmitterDelegator {
  em: EmitterWriter,
  errors: Rc<RefCell<Vec<ErrorKind>>>
}

impl EmitterDelegator {
  pub fn new(em : EmitterWriter)
      -> (EmitterDelegator, Rc<RefCell<Vec<ErrorKind>>>) {
    let errors = Rc::new(RefCell::new(Vec::new()));
    let delegator = EmitterDelegator { em : em, errors: errors.clone() };
    (delegator, errors)
  }
}

impl Emitter for EmitterDelegator {
  fn emit(&mut self, db: &DiagnosticBuilder) {

    let errkind = error_kind(db.message());

    match errkind {
      UnclosedDelimiter => {},
      _ => self.em.emit(db)
    };

    self.errors.borrow_mut().push(errkind);
  }
}

fn error_kind(msg: &str) -> ErrorKind {
  if is_unclosed_delemiter(msg) {
    ErrorKind::UnclosedDelimiter
  } else {
    Unknown
  }
}

fn is_unclosed_delemiter(msg: &str) -> bool {
  msg == "this file contains an un-closed delimiter"
}<|endoftext|>


<commit_before>use std::path::PathBuf;

use git2::{Index, Repository};
use toml::Value;

use libimagerror::into::IntoError;
use libimagerror::trace::{MapErrTrace, trace_error};
use libimagstore::hook::error::CustomData;
use libimagstore::hook::error::HookErrorKind as HEK;
use libimagstore::hook::result::HookResult;
use libimagutil::debug_result::*;

use vcs::git::action::StoreAction;
use vcs::git::result::Result;
use vcs::git::error::{MapErrInto, GitHookErrorKind as GHEK};

/// Runtime object for git hook implementations.
///
/// Contains some utility functionality to hold the repository and the configuration for the hooks.
pub struct Runtime {
    repository: Option<Repository>,
    config: Option<Value>,
}

impl Runtime {

    /// Build a `Runtime` object, pass the store path to build the `Repository` instance the
    /// `Runtime` has to contain.
    ///
    /// If the building of the `Repository` fails, this function `trace_error()`s the error and
    /// returns a `Runtime` object that does _not_ contain a `Repository`.
    pub fn new(storepath: &PathBuf) -> Runtime {
        Runtime {
            repository: Repository::open(storepath).map_err_trace().ok(),
            config: None,
        }
    }

    /// Set the configuration for the `Runtime`. Always returns `Ok(())`.
    pub fn set_config(&mut self, cfg: &Value) -> Result<()> {
        self.config = Some(cfg.clone());
        Ok(())
    }

    /// Check whether the `Runtime` has a `Repository`
    pub fn has_repository(&self) -> bool {
        self.repository.is_some()
    }

    /// Check whether the `Runtime` has a configuration
    pub fn has_config(&self) -> bool {
        self.config.is_some()
    }

    /// Get the the config value by reference or get an `Err()` which can be returned to the callee
    /// of the Hook.
    ///
    /// The `action` Argument is required in case of `Err()` so the error message can be build
    /// correctly.
    pub fn config_value_or_err(&self, action: &StoreAction) -> HookResult<&Value> {
        self.config
            .as_ref()
            .ok_or(GHEK::NoConfigError.into_error())
            .map_err_into(GHEK::ConfigError)
            .map_err(Box::new)
            .map_err(|e| HEK::HookExecutionError.into_error_with_cause(e))
            .map_err(|mut e| e.with_custom_data(CustomData::default().aborting(false)))
            .map_dbg_err(|_| {
                format!("[GIT {} HOOK]: Couldn't get Value object from config", action.uppercase())
            })
    }

    /// Get the `Repository` object from the `Runtime` or an `Err()` that can be returned to the
    /// callee of the Hook.
    ///
    /// The `action` Argument is required in case of `Err()` so the error message can be build
    /// correctly.
    pub fn repository(&self, action: &StoreAction) -> HookResult<&Repository> {
        use vcs::git::error::MapIntoHookError;

        debug!("[GIT {} HOOK]: Getting repository", action.uppercase());
        self.repository
            .as_ref()
            .ok_or(GHEK::MkRepo.into_error())
            .map_err_into(GHEK::RepositoryError)
            .map_into_hook_error()
            .map_dbg_err(|_| format!("[GIT {} HOOK]: Couldn't fetch Repository", action.uppercase()))
            .map_dbg(|_| format!("[GIT {} HOOK]: Repository object fetched", action.uppercase()))
    }

    /// Ensure that the branch that is put in the configuration file is checked out, if any.
    pub fn ensure_cfg_branch_is_checked_out(&self, action: &StoreAction) -> HookResult<()> {
        use vcs::git::config::ensure_branch;

        debug!("[GIT {} HOOK]: Ensuring branch checkout", action.uppercase());
        let head = try!(self
                        .repository(action)
                        .and_then(|r| {
                            debug!("[GIT {} HOOK]: Repository fetched, getting head", action.uppercase());
                            r.head()
                                .map_dbg_err_str("Couldn't fetch HEAD")
                                .map_dbg_err(|e| format!("\tbecause = {:?}", e))
                                .map_err_into(GHEK::HeadFetchError)
                                .map_err(|e| e.into())
                        }));
        debug!("[GIT {} HOOK]: HEAD fetched", action.uppercase());

        // TODO: Fail if not on branch? hmmh... I'm not sure
        if !head.is_branch() {
            debug!("[GIT {} HOOK]: HEAD is not a branch", action.uppercase());
            return Err(GHEK::NotOnBranch.into_error().into());
        }
        debug!("[GIT {} HOOK]: HEAD is a branch", action.uppercase());

        // Check out appropriate branch ... or fail
        match ensure_branch(self.config.as_ref()) {
            Ok(Some(s)) => {
                debug!("[GIT {} HOOK]: We have to ensure branch: {}", action.uppercase(), s);
                match head.name().map(|name| {
                    debug!("[GIT {} HOOK]: {} == {}", action.uppercase(), name, s);
                    name == s
                }) {
                    Some(b) => {
                        if b {
                            debug!("[GIT {} HOOK]: Branch already checked out.", action.uppercase());
                            Ok(())
                        } else {
                            debug!("[GIT {} HOOK]: Branch not checked out.", action.uppercase());
                            unimplemented!()
                        }
                    },

                    None => Err(GHEK::RepositoryBranchNameFetchingError.into_error())
                        .map_err_into(GHEK::RepositoryBranchError)
                        .map_err_into(GHEK::RepositoryError),
                }
            },
            Ok(None) => {
                debug!("[GIT {} HOOK]: No branch to checkout", action.uppercase());
                Ok(())
            },

            Err(e) => Err(e).map_err_into(GHEK::RepositoryError),
        }
        .map_err(Box::new)
        .map_err(|e| HEK::HookExecutionError.into_error_with_cause(e))
        .map_dbg_str("[GIT CREATE HOOK]: Branch checked out")
    }

    /// Check whether the WD is "dirty" - whether there is a diff to the repository
    /// This function returns false if there is no `Repository` object in the `Runtime`
    pub fn repo_is_dirty(&self, index: &Index) -> bool {
        match self.repository.as_ref() {
            Some(repo) => {
                repo.diff_index_to_workdir(Some(index), None)
                    .map_dbg_str("Fetched diff: Index <-> WD")
                    .map_dbg_err_str("Failed to fetch diff: Index <-> WD")
                    .map(|diff| diff.deltas().count() != 0)
                    .unwrap_or(false)
            },

            None => {
                debug!("No repository: Cannot fetch diff: Index <-> WD");
                false
            }
        }

    }

}

<commit_msg>Runtime::ensure_cfg_branch_is_checked_out() should check whether config enforces this<commit_after>use std::path::PathBuf;

use git2::{Index, Repository};
use toml::Value;

use libimagerror::into::IntoError;
use libimagerror::trace::{MapErrTrace, trace_error};
use libimagstore::hook::error::CustomData;
use libimagstore::hook::error::HookErrorKind as HEK;
use libimagstore::hook::result::HookResult;
use libimagutil::debug_result::*;

use vcs::git::action::StoreAction;
use vcs::git::result::Result;
use vcs::git::error::{MapErrInto, GitHookErrorKind as GHEK};

/// Runtime object for git hook implementations.
///
/// Contains some utility functionality to hold the repository and the configuration for the hooks.
pub struct Runtime {
    repository: Option<Repository>,
    config: Option<Value>,
}

impl Runtime {

    /// Build a `Runtime` object, pass the store path to build the `Repository` instance the
    /// `Runtime` has to contain.
    ///
    /// If the building of the `Repository` fails, this function `trace_error()`s the error and
    /// returns a `Runtime` object that does _not_ contain a `Repository`.
    pub fn new(storepath: &PathBuf) -> Runtime {
        Runtime {
            repository: Repository::open(storepath).map_err_trace().ok(),
            config: None,
        }
    }

    /// Set the configuration for the `Runtime`. Always returns `Ok(())`.
    pub fn set_config(&mut self, cfg: &Value) -> Result<()> {
        self.config = Some(cfg.clone());
        Ok(())
    }

    /// Check whether the `Runtime` has a `Repository`
    pub fn has_repository(&self) -> bool {
        self.repository.is_some()
    }

    /// Check whether the `Runtime` has a configuration
    pub fn has_config(&self) -> bool {
        self.config.is_some()
    }

    /// Get the the config value by reference or get an `Err()` which can be returned to the callee
    /// of the Hook.
    ///
    /// The `action` Argument is required in case of `Err()` so the error message can be build
    /// correctly.
    pub fn config_value_or_err(&self, action: &StoreAction) -> HookResult<&Value> {
        self.config
            .as_ref()
            .ok_or(GHEK::NoConfigError.into_error())
            .map_err_into(GHEK::ConfigError)
            .map_err(Box::new)
            .map_err(|e| HEK::HookExecutionError.into_error_with_cause(e))
            .map_err(|mut e| e.with_custom_data(CustomData::default().aborting(false)))
            .map_dbg_err(|_| {
                format!("[GIT {} HOOK]: Couldn't get Value object from config", action.uppercase())
            })
    }

    /// Get the `Repository` object from the `Runtime` or an `Err()` that can be returned to the
    /// callee of the Hook.
    ///
    /// The `action` Argument is required in case of `Err()` so the error message can be build
    /// correctly.
    pub fn repository(&self, action: &StoreAction) -> HookResult<&Repository> {
        use vcs::git::error::MapIntoHookError;

        debug!("[GIT {} HOOK]: Getting repository", action.uppercase());
        self.repository
            .as_ref()
            .ok_or(GHEK::MkRepo.into_error())
            .map_err_into(GHEK::RepositoryError)
            .map_into_hook_error()
            .map_dbg_err(|_| format!("[GIT {} HOOK]: Couldn't fetch Repository", action.uppercase()))
            .map_dbg(|_| format!("[GIT {} HOOK]: Repository object fetched", action.uppercase()))
    }

    /// Ensure that the branch that is put in the configuration file is checked out, if any.
    pub fn ensure_cfg_branch_is_checked_out(&self, action: &StoreAction) -> HookResult<()> {
        use vcs::git::config::ensure_branch;
        use vcs::git::config::do_checkout_ensure_branch;

        if !do_checkout_ensure_branch(self.config.as_ref()) {
            return Ok(())
        }

        debug!("[GIT {} HOOK]: Ensuring branch checkout", action.uppercase());
        let head = try!(self
                        .repository(action)
                        .and_then(|r| {
                            debug!("[GIT {} HOOK]: Repository fetched, getting head", action.uppercase());
                            r.head()
                                .map_dbg_err_str("Couldn't fetch HEAD")
                                .map_dbg_err(|e| format!("\tbecause = {:?}", e))
                                .map_err_into(GHEK::HeadFetchError)
                                .map_err(|e| e.into())
                        }));
        debug!("[GIT {} HOOK]: HEAD fetched", action.uppercase());

        // TODO: Fail if not on branch? hmmh... I'm not sure
        if !head.is_branch() {
            debug!("[GIT {} HOOK]: HEAD is not a branch", action.uppercase());
            return Err(GHEK::NotOnBranch.into_error().into());
        }
        debug!("[GIT {} HOOK]: HEAD is a branch", action.uppercase());

        // Check out appropriate branch ... or fail
        match ensure_branch(self.config.as_ref()) {
            Ok(Some(s)) => {
                debug!("[GIT {} HOOK]: We have to ensure branch: {}", action.uppercase(), s);
                match head.name().map(|name| {
                    debug!("[GIT {} HOOK]: {} == {}", action.uppercase(), name, s);
                    name == s
                }) {
                    Some(b) => {
                        if b {
                            debug!("[GIT {} HOOK]: Branch already checked out.", action.uppercase());
                            Ok(())
                        } else {
                            debug!("[GIT {} HOOK]: Branch not checked out.", action.uppercase());
                            unimplemented!()
                        }
                    },

                    None => Err(GHEK::RepositoryBranchNameFetchingError.into_error())
                        .map_err_into(GHEK::RepositoryBranchError)
                        .map_err_into(GHEK::RepositoryError),
                }
            },
            Ok(None) => {
                debug!("[GIT {} HOOK]: No branch to checkout", action.uppercase());
                Ok(())
            },

            Err(e) => Err(e).map_err_into(GHEK::RepositoryError),
        }
        .map_err(Box::new)
        .map_err(|e| HEK::HookExecutionError.into_error_with_cause(e))
        .map_dbg_str("[GIT CREATE HOOK]: Branch checked out")
    }

    /// Check whether the WD is "dirty" - whether there is a diff to the repository
    /// This function returns false if there is no `Repository` object in the `Runtime`
    pub fn repo_is_dirty(&self, index: &Index) -> bool {
        match self.repository.as_ref() {
            Some(repo) => {
                repo.diff_index_to_workdir(Some(index), None)
                    .map_dbg_str("Fetched diff: Index <-> WD")
                    .map_dbg_err_str("Failed to fetch diff: Index <-> WD")
                    .map(|diff| diff.deltas().count() != 0)
                    .unwrap_or(false)
            },

            None => {
                debug!("No repository: Cannot fetch diff: Index <-> WD");
                false
            }
        }

    }

}

<|endoftext|>


<commit_before>//! # A strongly restrictive color type.
use std::fmt;
use palette::pixel::Srgb;
use palette::{Rgb, Lch, Mix};

/// It is guaranteed that `T` can be and will always be only either `u8` or `f32`.
/// It can also be guaranteed that `Display` is not and will never be implemented
/// for `Color<f32>`.
/// It can also be guaranteed that arithmetics are not and will never be implemented
/// for `Color<u8>`.
///
/// ```rust,ignore
/// assert_fail_to_compile!(Color(0.0, 0.0, 0.0).to_string());
/// assert_fail_to_compile!(println!("{}", Color(0.0, 0.0, 0.0)));
/// assert_fail_to_compile!(Color(0, 0, 0) + Color(1, 1, 1));
/// ```
#[derive(Debug, PartialEq, Clone, Copy)]
pub struct Color<T>(pub T, pub T, pub T);

/// `Color888` can only do IO (convert to and from strings), and some simple equality tests.
/// Color is sRGB encoded.
pub type Color888 = Color<u8>;
/// `Colorf32` can only do arithmetics and convert to/from `Color888`.
/// Color is Linear RGB encoded.
pub type Colorf32 = Color<f32>;

impl fmt::Display for Color888 {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let Color(r, g, b) = *self;
        write!(f, "{:02x}{:02x}{:02x}", r, g, b)
    }
}

impl Eq for Color888 {}

impl From<Color888> for Colorf32 {
    /// Convert `Color888` (sRGB) to `Colorf32` (Linear RGB).
    fn from(color: Color888) -> Self {
        let Color(r, g, b) = color;
        let rgb = Rgb::<f32>::from(Srgb::new_u8(r, g, b));
        let Rgb { red: r, green: g, blue: b } = rgb;
        Color(r, g, b)
    }
}

impl Colorf32 {
    /// Convert `Colorf32` (Linear RGB) to `Color888` (sRGB).
    pub fn clamp_to_888(self) -> Color888 {
        /// This is used to "reduce" conversion error, so that
        /// `lossless_conversion` test can pass.
        const DELTA: f32 = 1e-6;

        let Color(r, g, b) = self;
        let srgb = Srgb::from(Rgb::new(r + DELTA, g + DELTA, b + DELTA));
        let Srgb { red: r, green: g, blue: b, .. } = srgb;
        let Color(r, g, b) = Color(r * 255.0, g * 255.0, b * 255.0).clamp_with(0.0, 255.0);
        Color(r as u8, g as u8, b as u8)
    }

    /// Clamp to [`min`, `max`].
    pub fn clamp_with(self, min: f32, max: f32) -> Self {
        let Color(r, g, b) = self;
        let r = r.max(min).min(max);
        let g = g.max(min).min(max);
        let b = b.max(min).min(max);
        Color(r, g, b)
    }

    /// Clamp to [0, 1].
    pub fn clamp(self) -> Self {
        self.clamp_with(0.0, 1.0)
    }

    /// Linear interpolate between colors.
    /// This is done in the CIE LCHab color space.
    pub fn mix(self, other: Self, factor: f32) -> Self {
        let Color(r1, g1, b1) = self;
        let Color(r2, g2, b2) = other;
        let lch1 = Lch::from(Rgb::new(r1, g1, b1));
        let lch2 = Lch::from(Rgb::new(r2, g2, b2));
        let lch = lch1.mix(&lch2, factor);
        let Rgb { red: r, green: g, blue: b } = Rgb::from(lch);
        Color(r, g, b)
    }

    /// Linear interpolate between colors.
    /// This is done in the Linear RGB color space.
    pub fn mix_fast(self, other: Self, factor: f32) -> Self {
        let Color(r1, g1, b1) = self;
        let Color(r2, g2, b2) = other;
        let r = (1.0 - factor) * r1 + factor * r2;
        let g = (1.0 - factor) * g1 + factor * g2;
        let b = (1.0 - factor) * b1 + factor * b2;
        Color(r, g, b)
    }
}

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

    /// When this test fails, you may want to increase the `DELTA` constant
    /// in `Colorf32::clamp_to_888()`.
    #[test]
    fn lossless_conversion() {
        for i in 0..256 {
            let a = i as u8;
            let color = Color(a, a, a);
            let color2 = Colorf32::from(color).clamp_to_888();
            assert_eq!(color, color2);
        }
    }

    #[test]
    fn clampped_down_conversion() {
        let max = Color(255, 255, 255);
        for i in 256..512 {
            let a = i as f32 / 255_f32;
            let color = Color(a, a, a).clamp_to_888();
            assert_eq!(color, max);
        }
    }

    #[test]
    fn clampped_up_conversion() {
        let min = Color(0, 0, 0);
        for i in -256..0 {
            let a = i as f32 / 255_f32;
            let color = Color(a, a, a).clamp_to_888();
            assert_eq!(color, min);
        }
    }

    #[test]
    fn display_color888() {
        assert_eq!(Color(0, 0, 0).to_string(), "000000");
        assert_eq!(Color(255, 255, 255).to_string(), "ffffff");
        assert_eq!(Color(1, 254, 10).to_string(), "01fe0a");
    }

    // TODO: automate the checking of this test.
    #[test]
    fn mixing() {
        let c1 = Color(1.0, 0.0, 0.0);
        let c2 = Color(0.0, 0.0, 1.0);
        for i in 0..256 {
            let a = (i as f32) / 255.0;
            let mix1 = c1.mix(c2, a).clamp_to_888();
            let mix2 = c1.mix_fast(c2, a).clamp_to_888();
            println!("{} {}", mix1, mix2);
        }
    }
}

<commit_msg>color: + set_lightness to change Lab L component<commit_after>//! # A strongly restrictive color type.
use std::fmt;
use palette::pixel::Srgb;
use palette::{Rgb, Lch, Lab, Mix};

/// It is guaranteed that `T` can be and will always be only either `u8` or `f32`.
/// It can also be guaranteed that `Display` is not and will never be implemented
/// for `Color<f32>`.
/// It can also be guaranteed that arithmetics are not and will never be implemented
/// for `Color<u8>`.
///
/// ```rust,ignore
/// assert_fail_to_compile!(Color(0.0, 0.0, 0.0).to_string());
/// assert_fail_to_compile!(println!("{}", Color(0.0, 0.0, 0.0)));
/// assert_fail_to_compile!(Color(0, 0, 0) + Color(1, 1, 1));
/// ```
#[derive(Debug, PartialEq, Clone, Copy)]
pub struct Color<T>(pub T, pub T, pub T);

/// `Color888` can only do IO (convert to and from strings), and some simple equality tests.
/// Color is sRGB encoded.
pub type Color888 = Color<u8>;
/// `Colorf32` can only do arithmetics and convert to/from `Color888`.
/// Color is Linear RGB encoded.
pub type Colorf32 = Color<f32>;

impl fmt::Display for Color888 {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let Color(r, g, b) = *self;
        write!(f, "{:02x}{:02x}{:02x}", r, g, b)
    }
}

impl Eq for Color888 {}

impl From<Color888> for Colorf32 {
    /// Convert `Color888` (sRGB) to `Colorf32` (Linear RGB).
    fn from(color: Color888) -> Self {
        let Color(r, g, b) = color;
        let rgb = Rgb::<f32>::from(Srgb::new_u8(r, g, b));
        let Rgb { red: r, green: g, blue: b } = rgb;
        Color(r, g, b)
    }
}

impl Colorf32 {
    /// Convert `Colorf32` (Linear RGB) to `Color888` (sRGB).
    pub fn clamp_to_888(self) -> Color888 {
        /// This is used to "reduce" conversion error, so that
        /// `lossless_conversion` test can pass.
        const DELTA: f32 = 1e-6;

        let Color(r, g, b) = self;
        let srgb = Srgb::from(Rgb::new(r + DELTA, g + DELTA, b + DELTA));
        let Srgb { red: r, green: g, blue: b, .. } = srgb;
        let Color(r, g, b) = Color(r * 255.0, g * 255.0, b * 255.0).clamp_with(0.0, 255.0);
        Color(r as u8, g as u8, b as u8)
    }

    /// Clamp to [`min`, `max`].
    pub fn clamp_with(self, min: f32, max: f32) -> Self {
        let Color(r, g, b) = self;
        let r = r.max(min).min(max);
        let g = g.max(min).min(max);
        let b = b.max(min).min(max);
        Color(r, g, b)
    }

    /// Clamp to [0, 1].
    pub fn clamp(self) -> Self {
        self.clamp_with(0.0, 1.0)
    }

    /// Linear interpolate between colors.
    /// This is done in the CIE LCHab color space.
    pub fn mix(self, other: Self, factor: f32) -> Self {
        let Color(r1, g1, b1) = self;
        let Color(r2, g2, b2) = other;
        let lch1 = Lch::from(Rgb::new(r1, g1, b1));
        let lch2 = Lch::from(Rgb::new(r2, g2, b2));
        let lch = lch1.mix(&lch2, factor);
        let Rgb { red: r, green: g, blue: b } = Rgb::from(lch);
        Color(r, g, b)
    }

    /// Linear interpolate between colors.
    /// This is done in the Linear RGB color space.
    pub fn mix_fast(self, other: Self, factor: f32) -> Self {
        let Color(r1, g1, b1) = self;
        let Color(r2, g2, b2) = other;
        let r = (1.0 - factor) * r1 + factor * r2;
        let g = (1.0 - factor) * g1 + factor * g2;
        let b = (1.0 - factor) * b1 + factor * b2;
        Color(r, g, b)
    }

    /// Modify the perceptual lightness of current color.
    /// A value of `0.0` gives darkest color and `1.0` gives whitest.
    ///
    /// This is done in the CIE Lab color space by setting the L component.
    pub fn set_lightness(self, lightness: f32) -> Self {
        let Color(r, g, b) = self;
        let lab = Lab { l: lightness, ..Rgb::new(r, g, b).into() };
        let Rgb { red: r, green: g, blue: b } = lab.into();
        Color(r, g, b)
    }
}

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

    /// When this test fails, you may want to increase the `DELTA` constant
    /// in `Colorf32::clamp_to_888()`.
    #[test]
    fn lossless_conversion() {
        for i in 0..256 {
            let a = i as u8;
            let color = Color(a, a, a);
            let color2 = Colorf32::from(color).clamp_to_888();
            assert_eq!(color, color2);
        }
    }

    #[test]
    fn clampped_down_conversion() {
        let max = Color(255, 255, 255);
        for i in 256..512 {
            let a = i as f32 / 255_f32;
            let color = Color(a, a, a).clamp_to_888();
            assert_eq!(color, max);
        }
    }

    #[test]
    fn clampped_up_conversion() {
        let min = Color(0, 0, 0);
        for i in -256..0 {
            let a = i as f32 / 255_f32;
            let color = Color(a, a, a).clamp_to_888();
            assert_eq!(color, min);
        }
    }

    #[test]
    fn display_color888() {
        assert_eq!(Color(0, 0, 0).to_string(), "000000");
        assert_eq!(Color(255, 255, 255).to_string(), "ffffff");
        assert_eq!(Color(1, 254, 10).to_string(), "01fe0a");
    }

    // TODO: automate the checking of this test.
    #[test]
    fn mixing() {
        let c1 = Color(1.0, 0.0, 0.0);
        let c2 = Color(0.0, 0.0, 1.0);
        for i in 0..256 {
            let a = (i as f32) / 255.0;
            let mix1 = c1.mix(c2, a).clamp_to_888();
            let mix2 = c1.mix_fast(c2, a).clamp_to_888();
            println!("{} {}", mix1, mix2);
        }
    }

    #[test]
    fn lightness() {
        let c = Color(0.5, 0.5, 0.5);
        let black = c.set_lightness(0.0).clamp_to_888().to_string();
        let white = c.set_lightness(1.0).clamp_to_888().to_string();
        assert_eq!(black, "000000");
        assert_eq!(white, "ffffff");
    }
}

<|endoftext|>


<commit_before>use std::ops::{BitOr, BitXor};
use super::{chtype, A_ALTCHARSET, A_BOLD, A_BLINK, A_CHARTEXT, A_DIM, A_LEFTLINE, A_INVIS};
use super::{A_ITALIC, A_OVERLINE, A_REVERSE, A_RIGHTLINE, A_STRIKEOUT, A_UNDERLINE};
use super::{COLOR_PAIR};
use super::colorpair::ColorPair;

#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub enum Attribute {
   AlternativeCharSet,
   Bold,
   Blink,
   CharText,
   Dim,
   Leftline,
   Invisible,
   Italic,
   Normal,
   Overline,
   Reverse,
   Rightline,
   Strikeout,
   Underline
}

#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub struct Attributes {
   raw: chtype,
   color_pair: ColorPair
}

macro_rules! attribute_setter {
    ($name:ident, $attr:ident) => {
        pub fn $name(&mut self, enabled: bool) {
            if enabled {
                self.raw = self.raw | $attr;
            } else {
                self.raw = self.raw ^ $attr;
            }
        }
    };
}

impl Attributes {
    pub fn new() -> Attributes {
        Attributes {
            raw: 0,
            color_pair: ColorPair(0)
        }
    }

    pub fn is_alternative_char_set(&self) -> bool { (self.raw & A_ALTCHARSET) > 0 }
    attribute_setter!(set_alternative_char_set, A_ALTCHARSET);

    pub fn is_bold(&self) -> bool { (self.raw & A_BOLD) > 0 }
    attribute_setter!(set_bold, A_BOLD);

    pub fn is_blink(&self) -> bool { (self.raw & A_BLINK) > 0 }
    attribute_setter!(set_blink, A_BLINK);

    pub fn is_char_text(&self) -> bool { (self.raw & A_CHARTEXT) > 0 }
    attribute_setter!(set_char_text, A_CHARTEXT);

    pub fn is_dim(&self) -> bool { (self.raw & A_DIM) > 0 }
    attribute_setter!(set_dim, A_DIM);

    pub fn is_leftline(&self) -> bool { (self.raw & A_LEFTLINE) > 0 }
    attribute_setter!(set_leftline, A_LEFTLINE);

    pub fn is_invisible(&self) -> bool { (self.raw & A_INVIS) > 0 }
    attribute_setter!(set_invisible, A_INVIS);

    pub fn is_italic(&self) -> bool { (self.raw & A_ITALIC) > 0 }
    attribute_setter!(set_italic, A_ITALIC);

    pub fn is_normal(&self) -> bool { self.raw == 0 }
    pub fn set_normal(&mut self) { self.raw = 0 }

    pub fn is_overline(&self) -> bool { (self.raw & A_OVERLINE) > 0 }
    attribute_setter!(set_overline, A_OVERLINE);

    pub fn is_reverse(&self) -> bool { (self.raw & A_REVERSE) > 0 }
    attribute_setter!(set_reverse, A_REVERSE);

    pub fn is_rightline(&self) -> bool { (self.raw & A_RIGHTLINE) > 0 }
    attribute_setter!(set_rightline, A_RIGHTLINE);

    pub fn is_strikeout(&self) -> bool { (self.raw & A_STRIKEOUT) > 0 }
    attribute_setter!(set_strikeout, A_STRIKEOUT);

    pub fn is_underline(&self) -> bool { (self.raw & A_UNDERLINE) > 0 }
    attribute_setter!(set_underline, A_UNDERLINE);

    pub fn color_pair(&self) -> ColorPair { self.color_pair }
    pub fn set_color_pair(&mut self, color_pair: ColorPair) {
        let color_chtype: chtype = color_pair.into();
        self.raw = self.raw | color_chtype;
        self.color_pair = color_pair;
    }
}

/// Implement the | operator for adding an Attribute to Attributes
///
/// # Example
///
/// ```
/// use pancurses::{Attribute, Attributes};
///
/// let mut attributes = Attributes::new();
/// assert!(!attributes.is_bold());
/// attributes = attributes | Attribute::Bold;
/// assert!(attributes.is_bold());
/// ```
impl BitOr<Attribute> for Attributes {
    type Output = Attributes;

    fn bitor(mut self, rhs: Attribute) -> Attributes {
        match rhs {
            Attribute::AlternativeCharSet => self.set_alternative_char_set(true),
            Attribute::Bold => self.set_bold(true),
            Attribute::Blink => self.set_blink(true),
            Attribute::CharText => self.set_char_text(true),
            Attribute::Dim => self.set_dim(true),
            Attribute::Leftline => self.set_leftline(true),
            Attribute::Invisible => self.set_invisible(true),
            Attribute::Italic => self.set_italic(true),
            Attribute::Normal => self.set_normal(),
            Attribute::Overline => self.set_overline(true),
            Attribute::Reverse => self.set_reverse(true),
            Attribute::Rightline => self.set_rightline(true),
            Attribute::Strikeout => self.set_strikeout(true),
            Attribute::Underline => self.set_underline(true)
        }
        self
    }
}

/// Implement the ^ operator for disabling an Attribute from Attributes
///
/// # Example
///
/// ```
/// use pancurses::{Attribute, Attributes};
///
/// let mut attributes = Attributes::from(Attribute::Bold);
/// assert!(attributes.is_bold());
/// attributes = attributes ^ Attribute::Bold;
/// assert!(!attributes.is_bold());
/// ```
impl BitXor<Attribute> for Attributes {
    type Output = Attributes;

    fn bitxor(mut self, rhs: Attribute) -> Attributes {
        match rhs {
            Attribute::AlternativeCharSet => self.set_alternative_char_set(false),
            Attribute::Bold => self.set_bold(false),
            Attribute::Blink => self.set_blink(false),
            Attribute::CharText => self.set_char_text(false),
            Attribute::Dim => self.set_dim(false),
            Attribute::Leftline => self.set_leftline(false),
            Attribute::Invisible => self.set_invisible(false),
            Attribute::Italic => self.set_italic(false),
            Attribute::Normal => (),
            Attribute::Overline => self.set_overline(false),
            Attribute::Reverse => self.set_reverse(false),
            Attribute::Rightline => self.set_rightline(false),
            Attribute::Strikeout => self.set_strikeout(false),
            Attribute::Underline => self.set_underline(false)
        }
        self
    }
}

/// Implement the | operator for adding Attributes to Attributes
///
/// # Example
///
/// ```
/// use pancurses::{Attribute, Attributes};
///
/// let mut attributes = Attributes::new() | Attribute::Bold;
/// let other = Attributes::new() | Attribute::Reverse;
/// attributes = attributes | other;
/// assert!(attributes.is_bold());
/// assert!(attributes.is_reverse());
/// assert!(!attributes.is_italic());
/// ```
impl BitOr for Attributes {
    type Output = Attributes;

    fn bitor(self, rhs: Attributes) -> Attributes {
        Attributes{
            raw: self.raw | rhs.raw,
            color_pair: ColorPair(self.color_pair.0 | rhs.color_pair.0)
        }
    }
}

/// Implement the ^ operator for removing Attributes from Attributes
///
/// # Example
///
/// ```
/// use pancurses::{Attribute, Attributes};
///
/// let mut attributes = Attributes::new() | Attribute::Blink | Attribute::Bold;
/// let other = Attributes::new() | Attribute::Reverse | Attribute::Bold;
/// attributes = attributes ^ other;
/// assert!(!attributes.is_bold());
/// assert!(attributes.is_reverse());
/// assert!(attributes.is_blink());
/// ```
impl BitXor for Attributes {
    type Output = Attributes;

    fn bitxor(self, rhs: Attributes) -> Attributes {
        Attributes{
            raw: self.raw ^ rhs.raw,
            color_pair: ColorPair(self.color_pair.0 ^ rhs.color_pair.0)
        }
    }
}

/// Implement the | operator for combining two 'Attribute's into Attributes
///
/// # Example
///
/// ```
/// use pancurses::{Attribute, Attributes};
///
/// let attributes = Attribute::Blink | Attribute::Reverse;
/// assert!(!attributes.is_bold());
/// assert!(attributes.is_blink());
/// assert!(attributes.is_reverse());
/// ```
impl BitOr for Attribute {
    type Output = Attributes;

    fn bitor(self, rhs: Attribute) -> Attributes {
        Attributes::new() | self | rhs
    }
}

impl From<Attribute> for Attributes {
    fn from(attribute: Attribute) -> Attributes {
        Attributes::new() | attribute
    }
}

impl From<Attribute> for chtype {
    fn from(attribute: Attribute) -> chtype {
        chtype::from(Attributes::from(attribute))
    }
}

impl From<Attributes> for chtype {
    fn from(attributes: Attributes) -> chtype {
        attributes.raw
    }
}
<commit_msg>Removed unused 'use'<commit_after>use std::ops::{BitOr, BitXor};
use super::{chtype, A_ALTCHARSET, A_BOLD, A_BLINK, A_CHARTEXT, A_DIM, A_LEFTLINE, A_INVIS};
use super::{A_ITALIC, A_OVERLINE, A_REVERSE, A_RIGHTLINE, A_STRIKEOUT, A_UNDERLINE};
use super::colorpair::ColorPair;

#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub enum Attribute {
   AlternativeCharSet,
   Bold,
   Blink,
   CharText,
   Dim,
   Leftline,
   Invisible,
   Italic,
   Normal,
   Overline,
   Reverse,
   Rightline,
   Strikeout,
   Underline
}

#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub struct Attributes {
   raw: chtype,
   color_pair: ColorPair
}

macro_rules! attribute_setter {
    ($name:ident, $attr:ident) => {
        pub fn $name(&mut self, enabled: bool) {
            if enabled {
                self.raw = self.raw | $attr;
            } else {
                self.raw = self.raw ^ $attr;
            }
        }
    };
}

impl Attributes {
    pub fn new() -> Attributes {
        Attributes {
            raw: 0,
            color_pair: ColorPair(0)
        }
    }

    pub fn is_alternative_char_set(&self) -> bool { (self.raw & A_ALTCHARSET) > 0 }
    attribute_setter!(set_alternative_char_set, A_ALTCHARSET);

    pub fn is_bold(&self) -> bool { (self.raw & A_BOLD) > 0 }
    attribute_setter!(set_bold, A_BOLD);

    pub fn is_blink(&self) -> bool { (self.raw & A_BLINK) > 0 }
    attribute_setter!(set_blink, A_BLINK);

    pub fn is_char_text(&self) -> bool { (self.raw & A_CHARTEXT) > 0 }
    attribute_setter!(set_char_text, A_CHARTEXT);

    pub fn is_dim(&self) -> bool { (self.raw & A_DIM) > 0 }
    attribute_setter!(set_dim, A_DIM);

    pub fn is_leftline(&self) -> bool { (self.raw & A_LEFTLINE) > 0 }
    attribute_setter!(set_leftline, A_LEFTLINE);

    pub fn is_invisible(&self) -> bool { (self.raw & A_INVIS) > 0 }
    attribute_setter!(set_invisible, A_INVIS);

    pub fn is_italic(&self) -> bool { (self.raw & A_ITALIC) > 0 }
    attribute_setter!(set_italic, A_ITALIC);

    pub fn is_normal(&self) -> bool { self.raw == 0 }
    pub fn set_normal(&mut self) { self.raw = 0 }

    pub fn is_overline(&self) -> bool { (self.raw & A_OVERLINE) > 0 }
    attribute_setter!(set_overline, A_OVERLINE);

    pub fn is_reverse(&self) -> bool { (self.raw & A_REVERSE) > 0 }
    attribute_setter!(set_reverse, A_REVERSE);

    pub fn is_rightline(&self) -> bool { (self.raw & A_RIGHTLINE) > 0 }
    attribute_setter!(set_rightline, A_RIGHTLINE);

    pub fn is_strikeout(&self) -> bool { (self.raw & A_STRIKEOUT) > 0 }
    attribute_setter!(set_strikeout, A_STRIKEOUT);

    pub fn is_underline(&self) -> bool { (self.raw & A_UNDERLINE) > 0 }
    attribute_setter!(set_underline, A_UNDERLINE);

    pub fn color_pair(&self) -> ColorPair { self.color_pair }
    pub fn set_color_pair(&mut self, color_pair: ColorPair) {
        let color_chtype: chtype = color_pair.into();
        self.raw = self.raw | color_chtype;
        self.color_pair = color_pair;
    }
}

/// Implement the | operator for adding an Attribute to Attributes
///
/// # Example
///
/// ```
/// use pancurses::{Attribute, Attributes};
///
/// let mut attributes = Attributes::new();
/// assert!(!attributes.is_bold());
/// attributes = attributes | Attribute::Bold;
/// assert!(attributes.is_bold());
/// ```
impl BitOr<Attribute> for Attributes {
    type Output = Attributes;

    fn bitor(mut self, rhs: Attribute) -> Attributes {
        match rhs {
            Attribute::AlternativeCharSet => self.set_alternative_char_set(true),
            Attribute::Bold => self.set_bold(true),
            Attribute::Blink => self.set_blink(true),
            Attribute::CharText => self.set_char_text(true),
            Attribute::Dim => self.set_dim(true),
            Attribute::Leftline => self.set_leftline(true),
            Attribute::Invisible => self.set_invisible(true),
            Attribute::Italic => self.set_italic(true),
            Attribute::Normal => self.set_normal(),
            Attribute::Overline => self.set_overline(true),
            Attribute::Reverse => self.set_reverse(true),
            Attribute::Rightline => self.set_rightline(true),
            Attribute::Strikeout => self.set_strikeout(true),
            Attribute::Underline => self.set_underline(true)
        }
        self
    }
}

/// Implement the ^ operator for disabling an Attribute from Attributes
///
/// # Example
///
/// ```
/// use pancurses::{Attribute, Attributes};
///
/// let mut attributes = Attributes::from(Attribute::Bold);
/// assert!(attributes.is_bold());
/// attributes = attributes ^ Attribute::Bold;
/// assert!(!attributes.is_bold());
/// ```
impl BitXor<Attribute> for Attributes {
    type Output = Attributes;

    fn bitxor(mut self, rhs: Attribute) -> Attributes {
        match rhs {
            Attribute::AlternativeCharSet => self.set_alternative_char_set(false),
            Attribute::Bold => self.set_bold(false),
            Attribute::Blink => self.set_blink(false),
            Attribute::CharText => self.set_char_text(false),
            Attribute::Dim => self.set_dim(false),
            Attribute::Leftline => self.set_leftline(false),
            Attribute::Invisible => self.set_invisible(false),
            Attribute::Italic => self.set_italic(false),
            Attribute::Normal => (),
            Attribute::Overline => self.set_overline(false),
            Attribute::Reverse => self.set_reverse(false),
            Attribute::Rightline => self.set_rightline(false),
            Attribute::Strikeout => self.set_strikeout(false),
            Attribute::Underline => self.set_underline(false)
        }
        self
    }
}

/// Implement the | operator for adding Attributes to Attributes
///
/// # Example
///
/// ```
/// use pancurses::{Attribute, Attributes};
///
/// let mut attributes = Attributes::new() | Attribute::Bold;
/// let other = Attributes::new() | Attribute::Reverse;
/// attributes = attributes | other;
/// assert!(attributes.is_bold());
/// assert!(attributes.is_reverse());
/// assert!(!attributes.is_italic());
/// ```
impl BitOr for Attributes {
    type Output = Attributes;

    fn bitor(self, rhs: Attributes) -> Attributes {
        Attributes{
            raw: self.raw | rhs.raw,
            color_pair: ColorPair(self.color_pair.0 | rhs.color_pair.0)
        }
    }
}

/// Implement the ^ operator for removing Attributes from Attributes
///
/// # Example
///
/// ```
/// use pancurses::{Attribute, Attributes};
///
/// let mut attributes = Attributes::new() | Attribute::Blink | Attribute::Bold;
/// let other = Attributes::new() | Attribute::Reverse | Attribute::Bold;
/// attributes = attributes ^ other;
/// assert!(!attributes.is_bold());
/// assert!(attributes.is_reverse());
/// assert!(attributes.is_blink());
/// ```
impl BitXor for Attributes {
    type Output = Attributes;

    fn bitxor(self, rhs: Attributes) -> Attributes {
        Attributes{
            raw: self.raw ^ rhs.raw,
            color_pair: ColorPair(self.color_pair.0 ^ rhs.color_pair.0)
        }
    }
}

/// Implement the | operator for combining two 'Attribute's into Attributes
///
/// # Example
///
/// ```
/// use pancurses::{Attribute, Attributes};
///
/// let attributes = Attribute::Blink | Attribute::Reverse;
/// assert!(!attributes.is_bold());
/// assert!(attributes.is_blink());
/// assert!(attributes.is_reverse());
/// ```
impl BitOr for Attribute {
    type Output = Attributes;

    fn bitor(self, rhs: Attribute) -> Attributes {
        Attributes::new() | self | rhs
    }
}

impl From<Attribute> for Attributes {
    fn from(attribute: Attribute) -> Attributes {
        Attributes::new() | attribute
    }
}

impl From<Attribute> for chtype {
    fn from(attribute: Attribute) -> chtype {
        chtype::from(Attributes::from(attribute))
    }
}

impl From<Attributes> for chtype {
    fn from(attributes: Attributes) -> chtype {
        attributes.raw
    }
}
<|endoftext|>