<commit_before>//! Unix specific definitions
use std;
use std::io::{Chars, Read, Write};
use std::sync;
use std::sync::atomic;
use libc;
use nix;
use nix::sys::signal;
use nix::sys::termios;

use consts::{self, KeyPress};
use ::Result;
use ::error;
use super::{RawReader, Term};

pub type Mode = termios::Termios;
const STDIN_FILENO: libc::c_int = libc::STDIN_FILENO;
const STDOUT_FILENO: libc::c_int = libc::STDOUT_FILENO;

/// Unsupported Terminals that don't support RAW mode
static UNSUPPORTED_TERM: [&'static str; 3] = ["dumb", "cons25", "emacs"];

#[cfg(any(target_os = "macos", target_os = "freebsd"))]
const TIOCGWINSZ: libc::c_ulong = 0x40087468;

#[cfg(any(all(target_os = "linux", target_env = "gnu"), target_os = "android"))]
const TIOCGWINSZ: libc::c_ulong = 0x5413;

#[cfg(all(target_os = "linux", target_env = "musl"))]
const TIOCGWINSZ: libc::c_int = 0x5413;

/// Try to get the number of columns in the current terminal,
/// or assume 80 if it fails.
fn get_columns() -> usize {
    let (cols, _) = get_win_size();
    cols
}

/// Try to get the number of rows in the current terminal,
/// or assume 24 if it fails.
fn get_rows() -> usize {
    let (_, rows) = get_win_size();
    rows
}

fn get_win_size() -> (usize, usize) {
    use std::mem::zeroed;
    use libc::c_ushort;

    unsafe {
        #[repr(C)]
        struct winsize {
            ws_row: c_ushort,
            ws_col: c_ushort,
            ws_xpixel: c_ushort,
            ws_ypixel: c_ushort,
        }

        let mut size: winsize = zeroed();
        match libc::ioctl(STDOUT_FILENO, TIOCGWINSZ, &mut size) {
            0 => (size.ws_col as usize, size.ws_row as usize), // TODO getCursorPosition
            _ => (80, 24),
        }
    }
}

/// Check TERM environment variable to see if current term is in our
/// unsupported list
fn is_unsupported_term() -> bool {
    use std::ascii::AsciiExt;
    match std::env::var("TERM") {
        Ok(term) => {
            let mut unsupported = false;
            for iter in &UNSUPPORTED_TERM {
                unsupported = (*iter).eq_ignore_ascii_case(&term)
            }
            unsupported
        }
        Err(_) => false,
    }
}

/// Return whether or not STDIN, STDOUT or STDERR is a TTY
fn is_a_tty(fd: libc::c_int) -> bool {
    unsafe { libc::isatty(fd) != 0 }
}

/// Enable RAW mode for the terminal.
pub fn enable_raw_mode() -> Result<Mode> {
    use nix::errno::Errno::ENOTTY;
    use nix::sys::termios::{BRKINT, CS8, ECHO, ICANON, ICRNL, IEXTEN, INPCK, ISIG, ISTRIP, IXON,
                            /* OPOST, */ VMIN, VTIME};
    if !is_a_tty(STDIN_FILENO) {
        try!(Err(nix::Error::from_errno(ENOTTY)));
    }
    let original_mode = try!(termios::tcgetattr(STDIN_FILENO));
    let mut raw = original_mode;
    // disable BREAK interrupt, CR to NL conversion on input,
    // input parity check, strip high bit (bit 8), output flow control
    raw.c_iflag = raw.c_iflag & !(BRKINT | ICRNL | INPCK | ISTRIP | IXON);
    // we don't want raw output, it turns newlines into straight linefeeds
    // raw.c_oflag = raw.c_oflag & !(OPOST); // disable all output processing
    raw.c_cflag = raw.c_cflag | (CS8); // character-size mark (8 bits)
    // disable echoing, canonical mode, extended input processing and signals
    raw.c_lflag = raw.c_lflag & !(ECHO | ICANON | IEXTEN | ISIG);
    raw.c_cc[VMIN] = 1; // One character-at-a-time input
    raw.c_cc[VTIME] = 0; // with blocking read
    try!(termios::tcsetattr(STDIN_FILENO, termios::TCSAFLUSH, &raw));
    Ok(original_mode)
}

/// Disable RAW mode for the terminal.
pub fn disable_raw_mode(original_mode: Mode) -> Result<()> {
    try!(termios::tcsetattr(STDIN_FILENO, termios::TCSAFLUSH, &original_mode));
    Ok(())
}

fn clear_screen(w: &mut Write) -> Result<()> {
    try!(w.write_all(b"\x1b[H\x1b[2J"));
    try!(w.flush());
    Ok(())
}

/// Console input reader
pub struct PosixRawReader {
    chars: Chars<std::io::Stdin>,
}

impl PosixRawReader {
    pub fn new(stdin: std::io::Stdin) -> Result<PosixRawReader> {
        Ok(PosixRawReader { chars: stdin.chars() })
    }

    fn escape_sequence(&mut self) -> Result<KeyPress> {
        // Read the next two bytes representing the escape sequence.
        let seq1 = try!(self.next_char());
        if seq1 == '[' {
            // ESC [ sequences.
            let seq2 = try!(self.next_char());
            if seq2.is_digit(10) {
                // Extended escape, read additional byte.
                let seq3 = try!(self.next_char());
                if seq3 == '~' {
                    match seq2 {
                        // '1' => Ok(KeyPress::Home),
                        '3' => Ok(KeyPress::Delete),
                        // '4' => Ok(KeyPress::End),
                        '5' => Ok(KeyPress::PageUp),
                        '6' => Ok(KeyPress::PageDown),
                        '7' => Ok(KeyPress::Home),
                        '8' => Ok(KeyPress::End),
                        _ => Ok(KeyPress::UnknownEscSeq),
                    }
                } else {
                    Ok(KeyPress::UnknownEscSeq)
                }
            } else {
                match seq2 {
                    'A' => Ok(KeyPress::Up), // ANSI
                    'B' => Ok(KeyPress::Down),
                    'C' => Ok(KeyPress::Right),
                    'D' => Ok(KeyPress::Left),
                    'F' => Ok(KeyPress::End),
                    'H' => Ok(KeyPress::Home),
                    _ => Ok(KeyPress::UnknownEscSeq),
                }
            }
        } else if seq1 == 'O' {
            // ESC O sequences.
            let seq2 = try!(self.next_char());
            match seq2 {
                'A' => Ok(KeyPress::Up),
                'B' => Ok(KeyPress::Down),
                'C' => Ok(KeyPress::Right),
                'D' => Ok(KeyPress::Left),
                'F' => Ok(KeyPress::End),
                'H' => Ok(KeyPress::Home),
                _ => Ok(KeyPress::UnknownEscSeq),
            }
        } else {
            // TODO ESC-N (n): search history forward not interactively
            // TODO ESC-P (p): search history backward not interactively
            // TODO ESC-R (r): Undo all changes made to this line.
            match seq1 {
                '\x08' => Ok(KeyPress::Meta('\x08')), // Backspace
                '<' => Ok(KeyPress::Meta('<')),
                '>' => Ok(KeyPress::Meta('>')),
                'b' | 'B' => Ok(KeyPress::Meta('B')),
                'c' | 'C' => Ok(KeyPress::Meta('C')),
                'd' | 'D' => Ok(KeyPress::Meta('D')),
                'f' | 'F' => Ok(KeyPress::Meta('F')),
                'l' | 'L' => Ok(KeyPress::Meta('L')),
                't' | 'T' => Ok(KeyPress::Meta('T')),
                'u' | 'U' => Ok(KeyPress::Meta('U')),
                'y' | 'Y' => Ok(KeyPress::Meta('Y')),
                '\x7f' => Ok(KeyPress::Meta('\x7f')), // Delete
                _ => {
                    // writeln!(io::stderr(), "key: {:?}, seq1: {:?}", KeyPress::Esc, seq1).unwrap();
                    Ok(KeyPress::UnknownEscSeq)
                }
            }
        }
    }
}

impl RawReader for PosixRawReader {
    // As there is no read timeout to properly handle single ESC key,
    // we make possible to deactivate escape sequence processing.
    fn next_key(&mut self, esc_seq: bool) -> Result<KeyPress> {
        // FIXME Stdin is buffered, so polling after reading an ESC char is useless (next available chars are already buffered)...
        let c = try!(self.next_char());

        let mut key = consts::char_to_key_press(c);
        if key == KeyPress::Esc && esc_seq {
            // escape sequence
            key = try!(self.escape_sequence());
        }
        Ok(key)
    }

    fn next_char(&mut self) -> Result<char> {
        match self.chars.next() {
            Some(c) => Ok(try!(c)),
            None => Err(error::ReadlineError::Eof),
        }
    }
}

static SIGWINCH_ONCE: sync::Once = sync::ONCE_INIT;
static SIGWINCH: atomic::AtomicBool = atomic::ATOMIC_BOOL_INIT;

fn install_sigwinch_handler() {
    SIGWINCH_ONCE.call_once(|| unsafe {
        let sigwinch = signal::SigAction::new(signal::SigHandler::Handler(sigwinch_handler),
                                              signal::SaFlags::empty(),
                                              signal::SigSet::empty());
        let _ = signal::sigaction(signal::SIGWINCH, &sigwinch);
    });
}

extern "C" fn sigwinch_handler(_: libc::c_int) {
    SIGWINCH.store(true, atomic::Ordering::SeqCst);
}

pub type Terminal = PosixTerminal;

#[derive(Clone,Debug)]
pub struct PosixTerminal {
    unsupported: bool,
    stdin_isatty: bool,
}

impl PosixTerminal {
    /// Create a RAW reader
    pub fn create_reader(&self) -> Result<PosixRawReader> {
        PosixRawReader::new(std::io::stdin())
    }
}

impl Term for PosixTerminal {
    fn new() -> PosixTerminal {
        let term = PosixTerminal {
            unsupported: is_unsupported_term(),
            stdin_isatty: is_a_tty(STDIN_FILENO),
        };
        if !term.unsupported && term.stdin_isatty && is_a_tty(STDOUT_FILENO) {
            install_sigwinch_handler();
        }
        term
    }

    // Init checks:

    /// Check if current terminal can provide a rich line-editing user interface.
    fn is_unsupported(&self) -> bool {
        self.unsupported
    }

    /// check if stdin is connected to a terminal.
    fn is_stdin_tty(&self) -> bool {
        self.stdin_isatty
    }

    // Interactive loop:

    /// Get the number of columns in the current terminal.
    fn get_columns(&self) -> usize {
        get_columns()
    }

    /// Get the number of rows in the current terminal.
    fn get_rows(&self) -> usize {
        get_rows()
    }

    /// Check if a SIGWINCH signal has been received
    fn sigwinch(&self) -> bool {
        SIGWINCH.compare_and_swap(true, false, atomic::Ordering::SeqCst)
    }

    /// Clear the screen. Used to handle ctrl+l
    fn clear_screen(&mut self, w: &mut Write) -> Result<()> {
        clear_screen(w)
    }
}
<commit_msg>Replace TCSAFLUSH by TCSADRAIN<commit_after>//! Unix specific definitions
use std;
use std::io::{Chars, Read, Write};
use std::sync;
use std::sync::atomic;
use libc;
use nix;
use nix::sys::signal;
use nix::sys::termios;

use consts::{self, KeyPress};
use ::Result;
use ::error;
use super::{RawReader, Term};

pub type Mode = termios::Termios;
const STDIN_FILENO: libc::c_int = libc::STDIN_FILENO;
const STDOUT_FILENO: libc::c_int = libc::STDOUT_FILENO;

/// Unsupported Terminals that don't support RAW mode
static UNSUPPORTED_TERM: [&'static str; 3] = ["dumb", "cons25", "emacs"];

#[cfg(any(target_os = "macos", target_os = "freebsd"))]
const TIOCGWINSZ: libc::c_ulong = 0x40087468;

#[cfg(any(all(target_os = "linux", target_env = "gnu"), target_os = "android"))]
const TIOCGWINSZ: libc::c_ulong = 0x5413;

#[cfg(all(target_os = "linux", target_env = "musl"))]
const TIOCGWINSZ: libc::c_int = 0x5413;

/// Try to get the number of columns in the current terminal,
/// or assume 80 if it fails.
fn get_columns() -> usize {
    let (cols, _) = get_win_size();
    cols
}

/// Try to get the number of rows in the current terminal,
/// or assume 24 if it fails.
fn get_rows() -> usize {
    let (_, rows) = get_win_size();
    rows
}

fn get_win_size() -> (usize, usize) {
    use std::mem::zeroed;
    use libc::c_ushort;

    unsafe {
        #[repr(C)]
        struct winsize {
            ws_row: c_ushort,
            ws_col: c_ushort,
            ws_xpixel: c_ushort,
            ws_ypixel: c_ushort,
        }

        let mut size: winsize = zeroed();
        match libc::ioctl(STDOUT_FILENO, TIOCGWINSZ, &mut size) {
            0 => (size.ws_col as usize, size.ws_row as usize), // TODO getCursorPosition
            _ => (80, 24),
        }
    }
}

/// Check TERM environment variable to see if current term is in our
/// unsupported list
fn is_unsupported_term() -> bool {
    use std::ascii::AsciiExt;
    match std::env::var("TERM") {
        Ok(term) => {
            let mut unsupported = false;
            for iter in &UNSUPPORTED_TERM {
                unsupported = (*iter).eq_ignore_ascii_case(&term)
            }
            unsupported
        }
        Err(_) => false,
    }
}

/// Return whether or not STDIN, STDOUT or STDERR is a TTY
fn is_a_tty(fd: libc::c_int) -> bool {
    unsafe { libc::isatty(fd) != 0 }
}

/// Enable RAW mode for the terminal.
pub fn enable_raw_mode() -> Result<Mode> {
    use nix::errno::Errno::ENOTTY;
    use nix::sys::termios::{BRKINT, CS8, ECHO, ICANON, ICRNL, IEXTEN, INPCK, ISIG, ISTRIP, IXON,
                            /* OPOST, */ VMIN, VTIME};
    if !is_a_tty(STDIN_FILENO) {
        try!(Err(nix::Error::from_errno(ENOTTY)));
    }
    let original_mode = try!(termios::tcgetattr(STDIN_FILENO));
    let mut raw = original_mode;
    // disable BREAK interrupt, CR to NL conversion on input,
    // input parity check, strip high bit (bit 8), output flow control
    raw.c_iflag = raw.c_iflag & !(BRKINT | ICRNL | INPCK | ISTRIP | IXON);
    // we don't want raw output, it turns newlines into straight linefeeds
    // raw.c_oflag = raw.c_oflag & !(OPOST); // disable all output processing
    raw.c_cflag = raw.c_cflag | (CS8); // character-size mark (8 bits)
    // disable echoing, canonical mode, extended input processing and signals
    raw.c_lflag = raw.c_lflag & !(ECHO | ICANON | IEXTEN | ISIG);
    raw.c_cc[VMIN] = 1; // One character-at-a-time input
    raw.c_cc[VTIME] = 0; // with blocking read
    try!(termios::tcsetattr(STDIN_FILENO, termios::TCSADRAIN, &raw));
    Ok(original_mode)
}

/// Disable RAW mode for the terminal.
pub fn disable_raw_mode(original_mode: Mode) -> Result<()> {
    try!(termios::tcsetattr(STDIN_FILENO, termios::TCSADRAIN, &original_mode));
    Ok(())
}

fn clear_screen(w: &mut Write) -> Result<()> {
    try!(w.write_all(b"\x1b[H\x1b[2J"));
    try!(w.flush());
    Ok(())
}

/// Console input reader
pub struct PosixRawReader {
    chars: Chars<std::io::Stdin>,
}

impl PosixRawReader {
    pub fn new(stdin: std::io::Stdin) -> Result<PosixRawReader> {
        Ok(PosixRawReader { chars: stdin.chars() })
    }

    fn escape_sequence(&mut self) -> Result<KeyPress> {
        // Read the next two bytes representing the escape sequence.
        let seq1 = try!(self.next_char());
        if seq1 == '[' {
            // ESC [ sequences.
            let seq2 = try!(self.next_char());
            if seq2.is_digit(10) {
                // Extended escape, read additional byte.
                let seq3 = try!(self.next_char());
                if seq3 == '~' {
                    match seq2 {
                        // '1' => Ok(KeyPress::Home),
                        '3' => Ok(KeyPress::Delete),
                        // '4' => Ok(KeyPress::End),
                        '5' => Ok(KeyPress::PageUp),
                        '6' => Ok(KeyPress::PageDown),
                        '7' => Ok(KeyPress::Home),
                        '8' => Ok(KeyPress::End),
                        _ => Ok(KeyPress::UnknownEscSeq),
                    }
                } else {
                    Ok(KeyPress::UnknownEscSeq)
                }
            } else {
                match seq2 {
                    'A' => Ok(KeyPress::Up), // ANSI
                    'B' => Ok(KeyPress::Down),
                    'C' => Ok(KeyPress::Right),
                    'D' => Ok(KeyPress::Left),
                    'F' => Ok(KeyPress::End),
                    'H' => Ok(KeyPress::Home),
                    _ => Ok(KeyPress::UnknownEscSeq),
                }
            }
        } else if seq1 == 'O' {
            // ESC O sequences.
            let seq2 = try!(self.next_char());
            match seq2 {
                'A' => Ok(KeyPress::Up),
                'B' => Ok(KeyPress::Down),
                'C' => Ok(KeyPress::Right),
                'D' => Ok(KeyPress::Left),
                'F' => Ok(KeyPress::End),
                'H' => Ok(KeyPress::Home),
                _ => Ok(KeyPress::UnknownEscSeq),
            }
        } else {
            // TODO ESC-N (n): search history forward not interactively
            // TODO ESC-P (p): search history backward not interactively
            // TODO ESC-R (r): Undo all changes made to this line.
            match seq1 {
                '\x08' => Ok(KeyPress::Meta('\x08')), // Backspace
                '<' => Ok(KeyPress::Meta('<')),
                '>' => Ok(KeyPress::Meta('>')),
                'b' | 'B' => Ok(KeyPress::Meta('B')),
                'c' | 'C' => Ok(KeyPress::Meta('C')),
                'd' | 'D' => Ok(KeyPress::Meta('D')),
                'f' | 'F' => Ok(KeyPress::Meta('F')),
                'l' | 'L' => Ok(KeyPress::Meta('L')),
                't' | 'T' => Ok(KeyPress::Meta('T')),
                'u' | 'U' => Ok(KeyPress::Meta('U')),
                'y' | 'Y' => Ok(KeyPress::Meta('Y')),
                '\x7f' => Ok(KeyPress::Meta('\x7f')), // Delete
                _ => {
                    // writeln!(io::stderr(), "key: {:?}, seq1: {:?}", KeyPress::Esc, seq1).unwrap();
                    Ok(KeyPress::UnknownEscSeq)
                }
            }
        }
    }
}

impl RawReader for PosixRawReader {
    // As there is no read timeout to properly handle single ESC key,
    // we make possible to deactivate escape sequence processing.
    fn next_key(&mut self, esc_seq: bool) -> Result<KeyPress> {
        // FIXME Stdin is buffered, so polling after reading an ESC char is useless (next available chars are already buffered)...
        let c = try!(self.next_char());

        let mut key = consts::char_to_key_press(c);
        if key == KeyPress::Esc && esc_seq {
            // escape sequence
            key = try!(self.escape_sequence());
        }
        Ok(key)
    }

    fn next_char(&mut self) -> Result<char> {
        match self.chars.next() {
            Some(c) => Ok(try!(c)),
            None => Err(error::ReadlineError::Eof),
        }
    }
}

static SIGWINCH_ONCE: sync::Once = sync::ONCE_INIT;
static SIGWINCH: atomic::AtomicBool = atomic::ATOMIC_BOOL_INIT;

fn install_sigwinch_handler() {
    SIGWINCH_ONCE.call_once(|| unsafe {
        let sigwinch = signal::SigAction::new(signal::SigHandler::Handler(sigwinch_handler),
                                              signal::SaFlags::empty(),
                                              signal::SigSet::empty());
        let _ = signal::sigaction(signal::SIGWINCH, &sigwinch);
    });
}

extern "C" fn sigwinch_handler(_: libc::c_int) {
    SIGWINCH.store(true, atomic::Ordering::SeqCst);
}

pub type Terminal = PosixTerminal;

#[derive(Clone,Debug)]
pub struct PosixTerminal {
    unsupported: bool,
    stdin_isatty: bool,
}

impl PosixTerminal {
    /// Create a RAW reader
    pub fn create_reader(&self) -> Result<PosixRawReader> {
        PosixRawReader::new(std::io::stdin())
    }
}

impl Term for PosixTerminal {
    fn new() -> PosixTerminal {
        let term = PosixTerminal {
            unsupported: is_unsupported_term(),
            stdin_isatty: is_a_tty(STDIN_FILENO),
        };
        if !term.unsupported && term.stdin_isatty && is_a_tty(STDOUT_FILENO) {
            install_sigwinch_handler();
        }
        term
    }

    // Init checks:

    /// Check if current terminal can provide a rich line-editing user interface.
    fn is_unsupported(&self) -> bool {
        self.unsupported
    }

    /// check if stdin is connected to a terminal.
    fn is_stdin_tty(&self) -> bool {
        self.stdin_isatty
    }

    // Interactive loop:

    /// Get the number of columns in the current terminal.
    fn get_columns(&self) -> usize {
        get_columns()
    }

    /// Get the number of rows in the current terminal.
    fn get_rows(&self) -> usize {
        get_rows()
    }

    /// Check if a SIGWINCH signal has been received
    fn sigwinch(&self) -> bool {
        SIGWINCH.compare_and_swap(true, false, atomic::Ordering::SeqCst)
    }

    /// Clear the screen. Used to handle ctrl+l
    fn clear_screen(&mut self, w: &mut Write) -> Result<()> {
        clear_screen(w)
    }
}
<|endoftext|>


<commit_before>use std::io;
use std::os::unix::io::{AsRawFd, FromRawFd};
use std::fs::File;

use libc::{c_int, pid_t};
use libc::{getpgrp};
use nix::sys::ioctl::ioctl;
use nix::unistd::{isatty, dup, getpid};

mod ffi {
    use libc::{c_ulong};

    pub static TIOCSPGRP: c_ulong = 0x5410;
    pub static TIOCGPGRP: c_ulong = 0x540F;
}

#[derive(Debug)]
pub struct TtyGuard {
    tty: Option<File>,
    my_pgrp: pid_t,
}

impl TtyGuard {
    pub fn take(&mut self) -> Result<(), io::Error> {
        let &mut TtyGuard { ref tty, my_pgrp, .. } = self;
        tty.as_ref().map_or(Ok(()), |f| {
            try!(unsafe { give_tty_to(f.as_raw_fd(), my_pgrp) });
            Ok(())
        })
    }
    pub fn give(&mut self, pid: pid_t) -> Result<(), io::Error> {
        let &mut TtyGuard { ref tty, .. } = self;
        tty.as_ref().map_or(Ok(()), |f| {
            try!(unsafe { give_tty_to(f.as_raw_fd(), pid) });
            Ok(())
        })
    }
    /// Check terminal and take it if it's not owned
    pub fn check(&mut self) -> Result<(), io::Error> {
        let &mut TtyGuard { ref tty, my_pgrp, .. } = self;
        tty.as_ref().map_or(Ok(()), |f| {
            if try!(unsafe { get_group(f.as_raw_fd()) }) == 0 {
                try!(unsafe { give_tty_to(f.as_raw_fd(), my_pgrp) });
            }
            Ok(())
        })
    }
    pub fn capture_tty() -> Result<TtyGuard, io::Error> {
        let my_pgrp = unsafe { getpgrp() };
        if true { //my_pgrp == getpid() {
            // only group leader, so can manage a tty
            for i in 0..3 {
                if isatty(i).unwrap_or(false) {
                    // after we determined which FD is a TTY there is no way
                    // to ensure that the same fd will be at the same number
                    // So we duplicate it:
                    let mut guard = TtyGuard {
                        tty: Some(unsafe { File::from_raw_fd(try!(dup(i))) }),
                        my_pgrp: unsafe { getpgrp() },
                    };
                    try!(guard.take());
                    return Ok(guard)
                }
            }
        }
        Ok(TtyGuard {
            tty: None,
            my_pgrp: my_pgrp,
        })
    }
}

impl Drop for TtyGuard {
    fn drop(&mut self) {
        self.take().ok();
    }
}

// dealing with file descriptors is always unsafe
unsafe fn get_group(fd: c_int)
    -> Result<pid_t, io::Error>
{
    let mut pgrp = 0;
    let res = ioctl(fd, ffi::TIOCGPGRP, &mut pgrp);
    if res == 0 {
        Ok(pgrp)
    } else {
        Err(io::Error::last_os_error())
    }
}

// dealing with file descriptors is always unsafe
unsafe fn give_tty_to(fd: c_int, pgrp: pid_t) -> Result<(), io::Error> {
    let res = ioctl(fd, ffi::TIOCSPGRP, &pgrp);
    if res == 0 {
        Ok(())
    } else {
        Err(io::Error::last_os_error())
    }
}
<commit_msg>Fix temporary commented out code<commit_after>use std::io;
use std::os::unix::io::{AsRawFd, FromRawFd};
use std::fs::File;

use libc::{c_int, pid_t};
use libc::{getpgrp};
use nix::sys::ioctl::ioctl;
use nix::unistd::{isatty, dup};

mod ffi {
    use libc::{c_ulong};

    pub static TIOCSPGRP: c_ulong = 0x5410;
    pub static TIOCGPGRP: c_ulong = 0x540F;
}

#[derive(Debug)]
pub struct TtyGuard {
    tty: Option<File>,
    my_pgrp: pid_t,
}

impl TtyGuard {
    pub fn take(&mut self) -> Result<(), io::Error> {
        let &mut TtyGuard { ref tty, my_pgrp, .. } = self;
        tty.as_ref().map_or(Ok(()), |f| {
            try!(unsafe { give_tty_to(f.as_raw_fd(), my_pgrp) });
            Ok(())
        })
    }
    pub fn give(&mut self, pid: pid_t) -> Result<(), io::Error> {
        let &mut TtyGuard { ref tty, .. } = self;
        tty.as_ref().map_or(Ok(()), |f| {
            try!(unsafe { give_tty_to(f.as_raw_fd(), pid) });
            Ok(())
        })
    }
    /// Check terminal and take it if it's not owned
    pub fn check(&mut self) -> Result<(), io::Error> {
        let &mut TtyGuard { ref tty, my_pgrp, .. } = self;
        tty.as_ref().map_or(Ok(()), |f| {
            if try!(unsafe { get_group(f.as_raw_fd()) }) == 0 {
                try!(unsafe { give_tty_to(f.as_raw_fd(), my_pgrp) });
            }
            Ok(())
        })
    }
    pub fn capture_tty() -> Result<TtyGuard, io::Error> {
        let my_pgrp = unsafe { getpgrp() };
        if my_pgrp != 0 {
            // my_pgrp can be zero if group owner is outside of the PID ns
            for i in 0..3 {
                if isatty(i).unwrap_or(false) {
                    // after we determined which FD is a TTY there is no way
                    // to ensure that the same fd will be at the same number
                    // So we duplicate it:
                    let mut guard = TtyGuard {
                        tty: Some(unsafe { File::from_raw_fd(try!(dup(i))) }),
                        my_pgrp: unsafe { getpgrp() },
                    };
                    try!(guard.take());
                    return Ok(guard)
                }
            }
        }
        Ok(TtyGuard {
            tty: None,
            my_pgrp: my_pgrp,
        })
    }
}

impl Drop for TtyGuard {
    fn drop(&mut self) {
        self.take().ok();
    }
}

// dealing with file descriptors is always unsafe
unsafe fn get_group(fd: c_int)
    -> Result<pid_t, io::Error>
{
    let mut pgrp = 0;
    let res = ioctl(fd, ffi::TIOCGPGRP, &mut pgrp);
    if res == 0 {
        Ok(pgrp)
    } else {
        Err(io::Error::last_os_error())
    }
}

// dealing with file descriptors is always unsafe
unsafe fn give_tty_to(fd: c_int, pgrp: pid_t) -> Result<(), io::Error> {
    let res = ioctl(fd, ffi::TIOCSPGRP, &pgrp);
    if res == 0 {
        Ok(())
    } else {
        Err(io::Error::last_os_error())
    }
}
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2017-2018 Ryan Huang
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published
 * by the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 */

mod board;
mod board_parts;
mod sys;
mod vec2;

use imgui::{ImGuiCond, ImStr, ImVec2, StyleVar, Ui};

use self::board::board;
pub use self::sys::run;
use self::vec2::Vec2;
use model::{Color, ColorMap, Model, Player};
use update::Event;

pub fn draw(ui: &Ui, size: (f32, f32), model: &Model) -> Option<Event> {
    let mut event = None;

    ui.main_menu_bar(|| {
        ui.menu(im_str!("Game")).build(|| {
            ui.menu_item(im_str!("New game")).enabled(false).build();

            use self::Player::*;
            if ui.menu_item(im_str!("Human vs. Human")).build() {
                insert_if_empty(&mut event, Event::NewGame(ColorMap::new(Human, Human)));
            }
            if ui.menu_item(im_str!("Human vs. Computer")).build() {
                insert_if_empty(&mut event, Event::NewGame(ColorMap::new(Human, Computer)));
            }
            if ui.menu_item(im_str!("Computer vs. Human")).build() {
                insert_if_empty(&mut event, Event::NewGame(ColorMap::new(Computer, Human)));
            }
            if ui.menu_item(im_str!("Computer vs. Computer")).build() {
                insert_if_empty(
                    &mut event,
                    Event::NewGame(ColorMap::new(Computer, Computer)),
                );
            }
            ui.separator();
            if ui.menu_item(im_str!("Quit")).build() {
                insert_if_empty(&mut event, Event::Quit);
            }
        });

        let mut depth = i32::from(model.ai_search_depth);
        ui.menu(im_str!("Computer")).build(|| {
            ui.slider_int(im_str!("Search depth"), &mut depth, 1, 7)
                .build();
            if ui.is_item_hovered() {
                ui.tooltip_text("How many moves ahead the computer will search.\nFewer moves is faster and easier, while more moves is slower and more difficult.");
            }
        });
        if depth as u8 != model.ai_search_depth {
            insert_if_empty(&mut event, Event::SetAIDepth(depth as u8));
        }
    });

    ui.with_style_var(StyleVar::WindowRounding(0.0), || {
        draw_window(ui, size, model, &mut event);
    });

    event
}

fn draw_window(ui: &Ui, size: (f32, f32), model: &Model, event: &mut Option<Event>) {
    ui.window(im_str!("Coerceo"))
        .size(size, ImGuiCond::Always)
        .position((0.0, 27.0), ImGuiCond::Once)
        .title_bar(false)
        .resizable(false)
        .movable(false)
        .build(|| {
            ui.text("Welcome to Coerceo!");
            ui.text(format!(
                "{:?} vs. {:?}",
                model.players.white, model.players.black
            ));

            if let Some(click) = board(ui, model, Vec2::new(size.0 - 16.0, size.1 - 230.0)) {
                insert_if_empty(event, click);
            }

            let format_piece_count = |count| match count {
                1 => String::from("1 piece"),
                _ => format!("{} pieces", count),
            };

            let format_hex_count = |count| match count {
                1 => String::from("1 captured hex"),
                _ => format!("{} captured hexes", count),
            };

            let display_vitals = || {
                ui.text(format!(
                    "{:?} has {} and {}.",
                    Color::White,
                    format_piece_count(model.board.pieces(Color::White)),
                    format_hex_count(model.board.hexes(Color::White)),
                ));
                ui.text(format!(
                    "{:?} has {} and {}.",
                    Color::Black,
                    format_piece_count(model.board.pieces(Color::Black)),
                    format_hex_count(model.board.hexes(Color::Black)),
                ));
            };

            let button_size = Vec2::new(120.0, 28.0);
            use model::Outcome::*;
            match model.outcome {
                Win(color) => {
                    ui.text(format!("{:?} wins!", color));
                    display_vitals();
                    if model.can_undo() && ui.button(im_str!("Undo"), button_size) {
                        insert_if_empty(event, Event::Undo);
                    }
                }
                InProgress => {
                    if model.players.white == model.players.black {
                        ui.text(format!("It's {:?}'s turn.", model.board.turn,));
                    } else {
                        ui.text(match model.current_player() {
                            Player::Computer => "Waiting for the computer...",
                            Player::Human => "It's your turn.",
                        });
                    }

                    display_vitals();

                    horz_button_layout(
                        ui,
                        vec![
                            (model.can_undo(), im_str!("Undo"), Event::Undo),
                            (model.can_redo(), im_str!("Redo"), Event::Redo),
                        ],
                        &button_size,
                        event,
                    );
                    let is_human_player = model.current_player() == Player::Human;
                    horz_button_layout(
                        ui,
                        vec![
                            (is_human_player, im_str!("Resign"), Event::Resign),
                            (
                                model.board.can_exchange() && is_human_player,
                                if model.exchanging {
                                    im_str!("Stop Exchanging")
                                } else {
                                    im_str!("Exchange")
                                },
                                Event::Exchange,
                            ),
                        ],
                        &button_size,
                        event,
                    );
                }
                // Draw cases
                _ => {
                    let message = match model.outcome {
                        DrawStalemate => "It's a draw by stalemate!",
                        DrawThreefoldRepetition => "It's a draw by threefold repetition!",
                        DrawInsufficientMaterial => "It's a draw by insufficient material!",
                        _ => unreachable!(),
                    };
                    ui.text(message);
                    display_vitals();
                    if model.can_undo() && ui.button(im_str!("Undo"), button_size) {
                        insert_if_empty(event, Event::Undo);
                    }
                }
            }
        });
}

fn horz_button_layout(
    ui: &Ui,
    buttons: Vec<(bool, &ImStr, Event)>,
    size: &Vec2,
    event: &mut Option<Event>,
) {
    if !buttons.iter().any(|&(show, _, _)| show) {
        return;
    }

    let size: ImVec2 = (*size).into();

    for (show, label, action) in buttons {
        if show {
            if ui.button(label, size) {
                insert_if_empty(event, action);
            }
        } else {
            ui.dummy(size);
        }
        ui.same_line(0.0);
    }
    ui.new_line();
}

fn insert_if_empty<T>(a: &mut Option<T>, b: T) {
    if a.is_none() {
        *a = Some(b);
    }
}
<commit_msg>Always set window position<commit_after>/*
 * Copyright (C) 2017-2018 Ryan Huang
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published
 * by the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 */

mod board;
mod board_parts;
mod sys;
mod vec2;

use imgui::{ImGuiCond, ImStr, ImVec2, StyleVar, Ui};

use self::board::board;
pub use self::sys::run;
use self::vec2::Vec2;
use model::{Color, ColorMap, Model, Player};
use update::Event;

pub fn draw(ui: &Ui, size: (f32, f32), model: &Model) -> Option<Event> {
    let mut event = None;

    ui.main_menu_bar(|| {
        ui.menu(im_str!("Game")).build(|| {
            ui.menu_item(im_str!("New game")).enabled(false).build();

            use self::Player::*;
            if ui.menu_item(im_str!("Human vs. Human")).build() {
                insert_if_empty(&mut event, Event::NewGame(ColorMap::new(Human, Human)));
            }
            if ui.menu_item(im_str!("Human vs. Computer")).build() {
                insert_if_empty(&mut event, Event::NewGame(ColorMap::new(Human, Computer)));
            }
            if ui.menu_item(im_str!("Computer vs. Human")).build() {
                insert_if_empty(&mut event, Event::NewGame(ColorMap::new(Computer, Human)));
            }
            if ui.menu_item(im_str!("Computer vs. Computer")).build() {
                insert_if_empty(
                    &mut event,
                    Event::NewGame(ColorMap::new(Computer, Computer)),
                );
            }
            ui.separator();
            if ui.menu_item(im_str!("Quit")).build() {
                insert_if_empty(&mut event, Event::Quit);
            }
        });

        let mut depth = i32::from(model.ai_search_depth);
        ui.menu(im_str!("Computer")).build(|| {
            ui.slider_int(im_str!("Search depth"), &mut depth, 1, 7)
                .build();
            if ui.is_item_hovered() {
                ui.tooltip_text("How many moves ahead the computer will search.\nFewer moves is faster and easier, while more moves is slower and more difficult.");
            }
        });
        if depth as u8 != model.ai_search_depth {
            insert_if_empty(&mut event, Event::SetAIDepth(depth as u8));
        }
    });

    ui.with_style_var(StyleVar::WindowRounding(0.0), || {
        draw_window(ui, size, model, &mut event);
    });

    event
}

fn draw_window(ui: &Ui, size: (f32, f32), model: &Model, event: &mut Option<Event>) {
    ui.window(im_str!("Coerceo"))
        .size(size, ImGuiCond::Always)
        .position((0.0, 27.0), ImGuiCond::Always)
        .title_bar(false)
        .resizable(false)
        .movable(false)
        .build(|| {
            ui.text("Welcome to Coerceo!");
            ui.text(format!(
                "{:?} vs. {:?}",
                model.players.white, model.players.black
            ));

            if let Some(click) = board(ui, model, Vec2::new(size.0 - 16.0, size.1 - 230.0)) {
                insert_if_empty(event, click);
            }

            let format_piece_count = |count| match count {
                1 => String::from("1 piece"),
                _ => format!("{} pieces", count),
            };

            let format_hex_count = |count| match count {
                1 => String::from("1 captured hex"),
                _ => format!("{} captured hexes", count),
            };

            let display_vitals = || {
                ui.text(format!(
                    "{:?} has {} and {}.",
                    Color::White,
                    format_piece_count(model.board.pieces(Color::White)),
                    format_hex_count(model.board.hexes(Color::White)),
                ));
                ui.text(format!(
                    "{:?} has {} and {}.",
                    Color::Black,
                    format_piece_count(model.board.pieces(Color::Black)),
                    format_hex_count(model.board.hexes(Color::Black)),
                ));
            };

            let button_size = Vec2::new(120.0, 28.0);
            use model::Outcome::*;
            match model.outcome {
                Win(color) => {
                    ui.text(format!("{:?} wins!", color));
                    display_vitals();
                    if model.can_undo() && ui.button(im_str!("Undo"), button_size) {
                        insert_if_empty(event, Event::Undo);
                    }
                }
                InProgress => {
                    if model.players.white == model.players.black {
                        ui.text(format!("It's {:?}'s turn.", model.board.turn,));
                    } else {
                        ui.text(match model.current_player() {
                            Player::Computer => "Waiting for the computer...",
                            Player::Human => "It's your turn.",
                        });
                    }

                    display_vitals();

                    horz_button_layout(
                        ui,
                        vec![
                            (model.can_undo(), im_str!("Undo"), Event::Undo),
                            (model.can_redo(), im_str!("Redo"), Event::Redo),
                        ],
                        &button_size,
                        event,
                    );
                    let is_human_player = model.current_player() == Player::Human;
                    horz_button_layout(
                        ui,
                        vec![
                            (is_human_player, im_str!("Resign"), Event::Resign),
                            (
                                model.board.can_exchange() && is_human_player,
                                if model.exchanging {
                                    im_str!("Stop Exchanging")
                                } else {
                                    im_str!("Exchange")
                                },
                                Event::Exchange,
                            ),
                        ],
                        &button_size,
                        event,
                    );
                }
                // Draw cases
                _ => {
                    let message = match model.outcome {
                        DrawStalemate => "It's a draw by stalemate!",
                        DrawThreefoldRepetition => "It's a draw by threefold repetition!",
                        DrawInsufficientMaterial => "It's a draw by insufficient material!",
                        _ => unreachable!(),
                    };
                    ui.text(message);
                    display_vitals();
                    if model.can_undo() && ui.button(im_str!("Undo"), button_size) {
                        insert_if_empty(event, Event::Undo);
                    }
                }
            }
        });
}

fn horz_button_layout(
    ui: &Ui,
    buttons: Vec<(bool, &ImStr, Event)>,
    size: &Vec2,
    event: &mut Option<Event>,
) {
    if !buttons.iter().any(|&(show, _, _)| show) {
        return;
    }

    let size: ImVec2 = (*size).into();

    for (show, label, action) in buttons {
        if show {
            if ui.button(label, size) {
                insert_if_empty(event, action);
            }
        } else {
            ui.dummy(size);
        }
        ui.same_line(0.0);
    }
    ui.new_line();
}

fn insert_if_empty<T>(a: &mut Option<T>, b: T) {
    if a.is_none() {
        *a = Some(b);
    }
}
<|endoftext|>


<commit_before>extern crate glm;

pub trait Shape {
    fn get_center(&self) -> glm::DVec2;
    fn contains(&self, point: &glm::DVec2) -> bool;
    fn intersects_with_circle(&self, circle: &Circle) -> bool;
    fn intersects_with_rectangle(&self, rectangle: &Rectangle) -> bool;
    fn distance<S>(&self, other: &S) -> f64
        where S: Shape
    {
        glm::distance(self.get_center(), other.get_center())
    }
}

pub struct Rectangle {
    dims: glm::DVec2,
    center: glm::DVec2,
}

impl Shape for Rectangle {
    fn get_center(&self) -> glm::DVec2 {
        self.center
    }

    fn contains(&self, point: &glm::DVec2) -> bool {
        let half_x = self.dims.x / 2.;
        let half_y = self.dims.y / 2.;

        if self.center.x - half_x > point.x {
            false
        } else if self.center.x + half_x < point.x {
            false
        } else if self.center.y - half_y > point.y {
            false
        } else if self.center.y + half_y < point.y {
            false
        } else {
            true
        }
    }

    fn intersects_with_rectangle(&self, rectangle: &Rectangle) -> bool {
        let half_x = self.dims.x / 2.;
        let half_y = self.dims.y / 2.;

        if self.center.x - half_x > rectangle.center.x + rectangle.dims.x / 2. {
            false
        } else if self.center.x + half_x < rectangle.center.x - rectangle.dims.x / 2. {
            false
        } else if self.center.y - half_y > rectangle.center.y + rectangle.dims.y / 2. {
            false
        } else if self.center.y + half_y < rectangle.center.y - rectangle.dims.y / 2. {
            false
        } else {
            true
        }
    }

    fn intersects_with_circle(&self, circle: &Circle) -> bool {
        circle.intersects_with_rectangle(&self)
    }
}

pub struct Circle {
    radius: f64,
    center: glm::DVec2,
}

impl Shape for Circle {
    fn get_center(&self) -> glm::DVec2 {
        self.center
    }

    fn contains(&self, point: &glm::DVec2) -> bool {
        let distance = glm::distance(self.center, *point);
        distance < self.radius
    }

    fn intersects_with_rectangle(&self, rectangle: &Rectangle) -> bool {
        if self.contains(&rectangle.center) {
            true
        } else if rectangle.contains(&self.center) {
            true
        } else {
            false
        }
    }

    fn intersects_with_circle(&self, circle: &Circle) -> bool {
        let distance = glm::distance(self.center, circle.center);
        distance < (self.radius + circle.radius)
    }
}

#[cfg(test)]
mod test {
    extern crate glm;
    use super::*;

    #[test]
    fn circle_no_contains() {
        let circle = Circle {
            radius: 1 as f64,
            center: glm::dvec2(2 as f64, 0 as f64),
        };
        let point = glm::dvec2(4 as f64, 4 as f64);
        assert!(!circle.contains(&point));
    }

    #[test]
    fn circle_contains() {
        let circle = Circle {
            radius: 3 as f64,
            center: glm::dvec2(2 as f64, 3 as f64),
        };
        let point = glm::dvec2(4 as f64, 4 as f64);
        assert!(circle.contains(&point));
    }

    #[test]
    fn circle_circle_no_intersect() {
        let circle_a = Circle {
            radius: 3 as f64,
            center: glm::dvec2(2 as f64, 3 as f64),
        };

        let circle_b = Circle {
            radius: 1 as f64,
            center: glm::dvec2(4 as f64, 7 as f64),
        };

        assert!(!circle_a.intersects_with_circle(&circle_b));
        assert!(!circle_b.intersects_with_circle(&circle_a));
    }

    #[test]
    fn circle_circle_intersect() {
        let circle_a = Circle {
            radius: 3 as f64,
            center: glm::dvec2(2 as f64, 3 as f64),
        };

        let circle_b = Circle {
            radius: 1 as f64,
            center: glm::dvec2(1 as f64, 5 as f64),
        };

        assert!(circle_a.intersects_with_circle(&circle_b));
        assert!(circle_b.intersects_with_circle(&circle_a));
    }

    #[test]
    fn rectangle_no_contains() {
        let rectangle = Rectangle {
            dims: glm::dvec2(2 as f64, 3 as f64),
            center: glm::dvec2(3 as f64, 0 as f64),
        };
        let point = glm::dvec2(3.5 as f64, -1 as f64);
        assert!(rectangle.contains(&point));
    }

    #[test]
    fn rectangle_contains() {
        let rectangle = Rectangle {
            dims: glm::dvec2(2 as f64, 3 as f64),
            center: glm::dvec2(3 as f64, 0 as f64),
        };
        let point = glm::dvec2(3.5 as f64, 2 as f64);
        assert!(!rectangle.contains(&point));
    }

    #[test]
    fn rectangle_rectangle_no_intersect() {
        let rectangle_a = Rectangle {
            dims: glm::dvec2(2 as f64, 3 as f64),
            center: glm::dvec2(3 as f64, 0 as f64),
        };

        let rectangle_b = Rectangle {
            dims: glm::dvec2(5 as f64, 2 as f64),
            center: glm::dvec2(0 as f64, 8 as f64),
        };
        assert!(!rectangle_a.intersects_with_rectangle(&rectangle_b));
        assert!(!rectangle_b.intersects_with_rectangle(&rectangle_a));
    }

    #[test]
    fn rectangle_rectangle_intersect() {
        let rectangle_a = Rectangle {
            dims: glm::dvec2(2 as f64, 3 as f64),
            center: glm::dvec2(3 as f64, 0 as f64),
        };

        let rectangle_b = Rectangle {
            dims: glm::dvec2(1 as f64, 2 as f64),
            center: glm::dvec2(2.5 as f64, 1 as f64),
        };
        assert!(rectangle_a.intersects_with_rectangle(&rectangle_b));
        assert!(rectangle_b.intersects_with_rectangle(&rectangle_a));
    }

    #[test]
    fn rectangle_circle_no_intersect() {
        let rectangle = Rectangle {
            dims: glm::dvec2(5 as f64, 2 as f64),
            center: glm::dvec2(6 as f64, 0 as f64),
        };

        let circle = Circle {
            radius: 3 as f64,
            center: glm::dvec2(2 as f64, 3 as f64),
        };

        assert!(!rectangle.intersects_with_circle(&circle));
        assert!(!circle.intersects_with_rectangle(&rectangle));
    }

    #[test]
    fn rectangle_inside_circle() {
        let rectangle = Rectangle {
            dims: glm::dvec2(1 as f64, 2 as f64),
            center: glm::dvec2(3 as f64, 5 as f64),
        };

        let circle = Circle {
            radius: 5 as f64,
            center: glm::dvec2(2 as f64, 3 as f64),
        };

        assert!(rectangle.intersects_with_circle(&circle));
        assert!(circle.intersects_with_rectangle(&rectangle));
    }

    #[test]
    fn circle_inside_rectangle() {
        let rectangle = Rectangle {
            dims: glm::dvec2(5 as f64, 7 as f64),
            center: glm::dvec2(4 as f64, 2 as f64),
        };

        let circle = Circle {
            radius: 1 as f64,
            center: glm::dvec2(5 as f64, 3 as f64),
        };

        assert!(rectangle.intersects_with_circle(&circle));
        assert!(circle.intersects_with_rectangle(&rectangle));
    }
}
<commit_msg>WIP intersecting circle with line<commit_after>extern crate glm;
use std::ops::Add;

pub trait Shape {
    fn get_center(&self) -> glm::DVec2;
    fn contains(&self, point: &glm::DVec2) -> bool;
    fn intersects_with_circle(&self, circle: &Circle) -> bool;
    fn intersects_with_rectangle(&self, rectangle: &Rectangle) -> bool;
    fn distance<S>(&self, other: &S) -> f64
        where S: Shape
    {
        glm::distance(self.get_center(), other.get_center())
    }
}

pub struct Rectangle {
    dims: glm::DVec2,
    center: glm::DVec2,
}

impl Shape for Rectangle {
    fn get_center(&self) -> glm::DVec2 {
        self.center
    }

    fn contains(&self, point: &glm::DVec2) -> bool {
        let half_x = self.dims.x / 2.;
        let half_y = self.dims.y / 2.;

        if self.center.x - half_x > point.x {
            false
        } else if self.center.x + half_x < point.x {
            false
        } else if self.center.y - half_y > point.y {
            false
        } else if self.center.y + half_y < point.y {
            false
        } else {
            true
        }
    }

    fn intersects_with_rectangle(&self, rectangle: &Rectangle) -> bool {
        let half_x = self.dims.x / 2.;
        let half_y = self.dims.y / 2.;

        if self.center.x - half_x > rectangle.center.x + rectangle.dims.x / 2. {
            false
        } else if self.center.x + half_x < rectangle.center.x - rectangle.dims.x / 2. {
            false
        } else if self.center.y - half_y > rectangle.center.y + rectangle.dims.y / 2. {
            false
        } else if self.center.y + half_y < rectangle.center.y - rectangle.dims.y / 2. {
            false
        } else {
            true
        }
    }

    fn intersects_with_circle(&self, circle: &Circle) -> bool {
        circle.intersects_with_rectangle(&self)
    }
}

pub struct Circle {
    radius: f64,
    center: glm::DVec2,
}

impl Circle {
    fn intersects_with_line(&self, p1: glm::DVec2, p2: glm::DVec2) -> bool {
        let d = p2 + p1;
        let f = p1 - self.center;
        let a = glm::dot(d, d);
        let b = 2_f64 * glm::dot(f, d);
        let c = glm::dot(f, f) - self.radius * self.radius;
        let mut discriminant = b * b - 4_f64 * a * c;

        if discriminant < 0_f64 {
            return false;
        }

        discriminant = discriminant.sqrt();

        let t1 = (-b - discriminant) / (2_f64 * a);
        let t2 = (-b + discriminant) / (2_f64 * a);

        if t1 >= 0_f64 && t1 <= 1_f64 {
            true
        } else if t2 >= 0_f64 && t2 <= 1_f64 {
            true
        } else {
            false
        }
    }
}

impl Shape for Circle {
    fn get_center(&self) -> glm::DVec2 {
        self.center
    }

    fn contains(&self, point: &glm::DVec2) -> bool {
        let distance = glm::distance(self.center, *point);
        distance < self.radius
    }

    fn intersects_with_rectangle(&self, rectangle: &Rectangle) -> bool {
        if self.contains(&rectangle.center) {
            true
        } else if rectangle.contains(&self.center) {
            true
        } else {
            false
        }
    }

    fn intersects_with_circle(&self, circle: &Circle) -> bool {
        let distance = glm::distance(self.center, circle.center);
        distance < (self.radius + circle.radius)
    }
}

#[cfg(test)]
mod test {
    extern crate glm;
    use super::*;

    #[test]
    fn circle_no_contains() {
        let circle = Circle {
            radius: 1 as f64,
            center: glm::dvec2(2 as f64, 0 as f64),
        };
        let point = glm::dvec2(4 as f64, 4 as f64);
        assert!(!circle.contains(&point));
    }

    #[test]
    fn circle_contains() {
        let circle = Circle {
            radius: 3 as f64,
            center: glm::dvec2(2 as f64, 3 as f64),
        };
        let point = glm::dvec2(4 as f64, 4 as f64);
        assert!(circle.contains(&point));
    }

    #[test]
    fn circle_circle_no_intersect() {
        let circle_a = Circle {
            radius: 3 as f64,
            center: glm::dvec2(2 as f64, 3 as f64),
        };

        let circle_b = Circle {
            radius: 1 as f64,
            center: glm::dvec2(4 as f64, 7 as f64),
        };

        assert!(!circle_a.intersects_with_circle(&circle_b));
        assert!(!circle_b.intersects_with_circle(&circle_a));
    }

    #[test]
    fn circle_circle_intersect() {
        let circle_a = Circle {
            radius: 3 as f64,
            center: glm::dvec2(2 as f64, 3 as f64),
        };

        let circle_b = Circle {
            radius: 1 as f64,
            center: glm::dvec2(1 as f64, 5 as f64),
        };

        assert!(circle_a.intersects_with_circle(&circle_b));
        assert!(circle_b.intersects_with_circle(&circle_a));
    }

    #[test]
    fn rectangle_no_contains() {
        let rectangle = Rectangle {
            dims: glm::dvec2(2 as f64, 3 as f64),
            center: glm::dvec2(3 as f64, 0 as f64),
        };
        let point = glm::dvec2(3.5 as f64, -1 as f64);
        assert!(rectangle.contains(&point));
    }

    #[test]
    fn rectangle_contains() {
        let rectangle = Rectangle {
            dims: glm::dvec2(2 as f64, 3 as f64),
            center: glm::dvec2(3 as f64, 0 as f64),
        };
        let point = glm::dvec2(3.5 as f64, 2 as f64);
        assert!(!rectangle.contains(&point));
    }

    #[test]
    fn rectangle_rectangle_no_intersect() {
        let rectangle_a = Rectangle {
            dims: glm::dvec2(2 as f64, 3 as f64),
            center: glm::dvec2(3 as f64, 0 as f64),
        };

        let rectangle_b = Rectangle {
            dims: glm::dvec2(5 as f64, 2 as f64),
            center: glm::dvec2(0 as f64, 8 as f64),
        };
        assert!(!rectangle_a.intersects_with_rectangle(&rectangle_b));
        assert!(!rectangle_b.intersects_with_rectangle(&rectangle_a));
    }

    #[test]
    fn rectangle_rectangle_intersect() {
        let rectangle_a = Rectangle {
            dims: glm::dvec2(2 as f64, 3 as f64),
            center: glm::dvec2(3 as f64, 0 as f64),
        };

        let rectangle_b = Rectangle {
            dims: glm::dvec2(1 as f64, 2 as f64),
            center: glm::dvec2(2.5 as f64, 1 as f64),
        };
        assert!(rectangle_a.intersects_with_rectangle(&rectangle_b));
        assert!(rectangle_b.intersects_with_rectangle(&rectangle_a));
    }

    #[test]
    fn rectangle_circle_no_intersect() {
        let rectangle = Rectangle {
            dims: glm::dvec2(5 as f64, 2 as f64),
            center: glm::dvec2(6 as f64, 0 as f64),
        };

        let circle = Circle {
            radius: 3 as f64,
            center: glm::dvec2(2 as f64, 3 as f64),
        };

        assert!(!rectangle.intersects_with_circle(&circle));
        assert!(!circle.intersects_with_rectangle(&rectangle));
    }

    #[test]
    fn rectangle_inside_circle() {
        let rectangle = Rectangle {
            dims: glm::dvec2(1 as f64, 2 as f64),
            center: glm::dvec2(3 as f64, 5 as f64),
        };

        let circle = Circle {
            radius: 5 as f64,
            center: glm::dvec2(2 as f64, 3 as f64),
        };

        assert!(rectangle.intersects_with_circle(&circle));
        assert!(circle.intersects_with_rectangle(&rectangle));
    }

    #[test]
    fn circle_inside_rectangle() {
        let rectangle = Rectangle {
            dims: glm::dvec2(5 as f64, 7 as f64),
            center: glm::dvec2(4 as f64, 2 as f64),
        };

        let circle = Circle {
            radius: 1 as f64,
            center: glm::dvec2(5 as f64, 3 as f64),
        };

        assert!(rectangle.intersects_with_circle(&circle));
        assert!(circle.intersects_with_rectangle(&rectangle));
    }

    #[test]
    fn rectangle_circle_intersect() {
        let rectangle = Rectangle {
            dims: glm::dvec2(2 as f64, 2 as f64),
            center: glm::dvec2(5 as f64, 3 as f64),
        };

        let circle = Circle {
            radius: 2 as f64,
            center: glm::dvec2(2 as f64, 3 as f64),
        };

        assert!(rectangle.intersects_with_circle(&circle));
        assert!(circle.intersects_with_rectangle(&rectangle));
    }
}
<|endoftext|>


<commit_before>pub fn clear() -> String {
    "\x1b[2J".to_string()
}

pub fn begin() -> String {
    "\x1bPq".to_string()
}

pub fn end() -> String {
    "\x1b\\".to_string()
}

pub fn from(memory: &[u32], width: usize, height: usize) -> String {
    let mut pixels: Vec<u8> = Vec::new();
    let mut row = 0;

    while row < height {
        for x in 0..width {
            let mut byte: u8 = 0;

            for y in 0..6 {
                let offset = x + (y * width);
                if memory[offset] > 0 {
                    byte = byte | (1 << offset);
                }
            }

            pixels.push(byte + 63);
        }

        row += 6;
    }

    String::from_utf8(pixels).unwrap()
}

#[cfg(test)]
mod tests {
    #[test]
    fn it_converts_000000_to_question_mark() {
        assert_eq!(super::from(&[0, 0, 0, 0, 0, 0], 1, 6), "?");
    }

    #[test]
    fn it_converts_000111_to_w() {
        assert_eq!(super::from(&[0, 0, 0, 1, 1, 1], 1, 6), "w");
    }

    #[test]
    fn it_converts_111000_to_capital_f() {
        assert_eq!(super::from(&[1, 1, 1, 0, 0, 0], 1, 6), "F");
    }

    #[test]
    fn it_converts_111111_to_tilde() {
        assert_eq!(super::from(&[1, 1, 1, 1, 1, 1], 1, 6), "~");
    }
}
<commit_msg>Fix a offset rendering bug when calculating Sixel codes.<commit_after>pub fn clear() -> String {
    "\x1b[2J".to_string()
}

pub fn begin() -> String {
    "\x1bPq".to_string()
}

pub fn end() -> String {
    "\x1b\\".to_string()
}

pub fn from(memory: &[u32], width: usize, height: usize) -> String {
    let mut pixels: Vec<u8> = Vec::new();
    let mut row = 0;

    while row < height {
        for x in 0..width {
            let mut byte: u8 = 0;

            for y in 0..6 {
                let offset = x + (y * width);
                if memory[offset] > 0 {
                    byte = byte | (1 << y);
                }
            }

            pixels.push(byte + 63);
        }

        row += 6;
    }

    String::from_utf8(pixels).unwrap()
}

#[cfg(test)]
mod tests {
    #[test]
    fn it_converts_000000_to_question_mark() {
        assert_eq!(super::from(&[0, 0, 0, 0, 0, 0], 1, 6), "?");
    }

    #[test]
    fn it_converts_000111_to_w() {
        assert_eq!(super::from(&[0, 0, 0, 1, 1, 1], 1, 6), "w");
    }

    #[test]
    fn it_converts_111000_to_capital_f() {
        assert_eq!(super::from(&[1, 1, 1, 0, 0, 0], 1, 6), "F");
    }

    #[test]
    fn it_converts_111111_to_tilde() {
        assert_eq!(super::from(&[1, 1, 1, 1, 1, 1], 1, 6), "~");
    }

    #[test]
    fn it_displays_capital_a() {
        assert_eq!(super::from(&[0, 1, 1, 0, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 0, 1, 0,
                                 0, 0, 0],
                               4,
                               6),
                   "]DD]");
    }
}
<|endoftext|>


<commit_before>use std::rc::Rc;

use Message;
use MiliSec;
use TimerEvent;

#[derive(Debug)]
pub struct State {
    elapsed_time: MiliSec,
    elapsed_time_since_last_message: MiliSec,
    messages: Vec<Rc<Message>>,
    opened: bool,
}

impl State {
    pub fn new() -> State {
        State {
            elapsed_time: 0,
            elapsed_time_since_last_message: 0,
            messages: Vec::new(),
            opened: false,
        }
    }

    pub fn is_open(&self) -> bool {
        self.opened
    }

    pub fn open(&mut self) {
        self.opened = true;
    }

    pub fn close(&mut self) {
        self.opened = false;
    }

    pub fn elapsed_time(&self) -> MiliSec {
        self.elapsed_time
    }

    pub fn elapsed_time_since_last_message(&self) -> MiliSec {
        self.elapsed_time_since_last_message
    }

    pub fn messages(&self) -> &Vec<Rc<Message>> {
        &self.messages
    }

    pub fn add_message(&mut self, message: Rc<Message>) {
        self.messages.push(message);
        self.elapsed_time_since_last_message = 0;
    }

    pub fn on_timer(&mut self, event: &TimerEvent) {
        let delta = event.0;
        self.elapsed_time += delta;
        self.elapsed_time_since_last_message += delta;
    }
}
<commit_msg>State: reset variables on close() call<commit_after>use std::rc::Rc;

use Message;
use MiliSec;
use TimerEvent;

#[derive(Debug)]
pub struct State {
    elapsed_time: MiliSec,
    elapsed_time_since_last_message: MiliSec,
    messages: Vec<Rc<Message>>,
    opened: bool,
}

impl State {
    pub fn new() -> State {
        State {
            elapsed_time: 0,
            elapsed_time_since_last_message: 0,
            messages: Vec::new(),
            opened: false,
        }
    }

    pub fn is_open(&self) -> bool {
        self.opened
    }

    pub fn open(&mut self) {
        self.opened = true;
    }

    pub fn close(&mut self) {
        self.reset();
    }

    pub fn elapsed_time(&self) -> MiliSec {
        self.elapsed_time
    }

    pub fn elapsed_time_since_last_message(&self) -> MiliSec {
        self.elapsed_time_since_last_message
    }

    pub fn messages(&self) -> &Vec<Rc<Message>> {
        &self.messages
    }

    pub fn add_message(&mut self, message: Rc<Message>) {
        self.messages.push(message);
        self.elapsed_time_since_last_message = 0;
    }

    pub fn on_timer(&mut self, event: &TimerEvent) {
        let delta = event.0;
        self.elapsed_time += delta;
        self.elapsed_time_since_last_message += delta;
    }

    fn reset(&mut self) {
        self.elapsed_time = 0;
        self.elapsed_time_since_last_message = 0;
        self.messages.clear();
        self.opened = false;
    }
}
<|endoftext|>


<commit_before>//! Compiler plugin defining macros that create PHF data structures.
//!
//! # Example
//!
//! ```rust
//! #![feature(plugin, core)]
//! #![plugin(phf_macros)]
//!
//! extern crate phf;
//!
//! #[derive(Clone)]
//! pub enum Keyword {
//!     Loop,
//!     Continue,
//!     Break,
//!     Fn,
//!     Extern,
//! }
//!
//! static KEYWORDS: phf::Map<&'static str, Keyword> = phf_map! {
//!     "loop" => Keyword::Loop,
//!     "continue" => Keyword::Continue,
//!     "break" => Keyword::Break,
//!     "fn" => Keyword::Fn,
//!     "extern" => Keyword::Extern,
//! };
//!
//! pub fn parse_keyword(keyword: &str) -> Option<Keyword> {
//!     KEYWORDS.get(keyword).cloned()
//! }
//! # fn main() {}
//! ```
#![doc(html_root_url="http://sfackler.github.io/rust-phf/doc/v0.7.10")]
#![feature(plugin_registrar, quote, rustc_private)]

extern crate syntax;
#[cfg(feature = "stats")]
extern crate time;
extern crate rustc_plugin;
extern crate phf_shared;
extern crate phf_generator;

use std::collections::HashMap;
use std::collections::hash_map::Entry::{Occupied, Vacant};
use syntax::ast::{self, TokenTree, Expr, ExprLit, ExprVec};
use syntax::codemap::{Span, Spanned};
use syntax::ext::base::{DummyResult, ExtCtxt, MacResult};
use syntax::fold::Folder;
use syntax::parse;
use syntax::parse::token::{InternedString, Comma, Eof, FatArrow};
use syntax::print::pprust;
use rustc_plugin::Registry;
use phf_generator::HashState;
use std::env;

use util::{Entry, Key};
use util::{create_map, create_set, create_ordered_map, create_ordered_set};

pub mod util;
mod macros;

#[plugin_registrar]
#[doc(hidden)]
pub fn macro_registrar(reg: &mut Registry) {
    reg.register_macro("phf_map", expand_phf_map);
    reg.register_macro("phf_set", expand_phf_set);
    reg.register_macro("phf_ordered_map", expand_phf_ordered_map);
    reg.register_macro("phf_ordered_set", expand_phf_ordered_set);
}

fn generate_hash(cx: &mut ExtCtxt, sp: Span, entries: &[Entry]) -> HashState {
    #[cfg(feature = "stats")]
    use time::precise_time_s;
    #[cfg(not(feature = "stats"))]
    fn precise_time_s() -> f64 {
        0.
    }

    let start = precise_time_s();
    let state = phf_generator::generate_hash(entries);
    let time = precise_time_s() - start;

    if cfg!(feature = "stats") && env::var_os("PHF_STATS").is_some() {
        cx.span_warn(sp, &format!("PHF generation took {} seconds", time));
    }

    state
}

fn expand_phf_map(cx: &mut ExtCtxt, sp: Span, tts: &[TokenTree]) -> Box<MacResult + 'static> {
    let entries = match parse_map(cx, tts) {
        Some(entries) => entries,
        None => return DummyResult::expr(sp),
    };

    if has_duplicates(cx, sp, &*entries) {
        return DummyResult::expr(sp);
    }

    let state = generate_hash(cx, sp, &*entries);

    create_map(cx, sp, entries, state)
}

fn expand_phf_set(cx: &mut ExtCtxt, sp: Span, tts: &[TokenTree]) -> Box<MacResult + 'static> {
    let entries = match parse_set(cx, tts) {
        Some(entries) => entries,
        None => return DummyResult::expr(sp),
    };

    if has_duplicates(cx, sp, &*entries) {
        return DummyResult::expr(sp);
    }

    let state = generate_hash(cx, sp, &*entries);

    create_set(cx, sp, entries, state)
}

fn expand_phf_ordered_map(cx: &mut ExtCtxt,
                          sp: Span,
                          tts: &[TokenTree])
                          -> Box<MacResult + 'static> {
    let entries = match parse_map(cx, tts) {
        Some(entries) => entries,
        None => return DummyResult::expr(sp),
    };

    if has_duplicates(cx, sp, &*entries) {
        return DummyResult::expr(sp);
    }

    let state = generate_hash(cx, sp, &*entries);

    create_ordered_map(cx, sp, entries, state)
}

fn expand_phf_ordered_set(cx: &mut ExtCtxt,
                          sp: Span,
                          tts: &[TokenTree])
                          -> Box<MacResult + 'static> {
    let entries = match parse_set(cx, tts) {
        Some(entries) => entries,
        None => return DummyResult::expr(sp),
    };

    if has_duplicates(cx, sp, &*entries) {
        return DummyResult::expr(sp);
    }

    let state = generate_hash(cx, sp, &*entries);

    create_ordered_set(cx, sp, entries, state)
}

fn parse_map(cx: &mut ExtCtxt, tts: &[TokenTree]) -> Option<Vec<Entry>> {
    let mut parser = parse::new_parser_from_tts(cx.parse_sess(), cx.cfg(), tts.to_vec());
    let mut entries = Vec::new();

    let mut bad = false;
    while parser.token != Eof {
        let key = cx.expander().fold_expr(parser.parse_expr().unwrap());
        let key_contents = parse_key(cx, &*key).unwrap_or_else(|| {
            bad = true;
            Key::Str(InternedString::new(""))
        });

        if !parser.eat(&FatArrow).ok().unwrap() {
            cx.span_err(parser.span, "expected `=>`");
            return None;
        }

        let value = parser.parse_expr().unwrap();

        entries.push(Entry {
            key_contents: key_contents,
            key: key,
            value: value,
        });

        if !parser.eat(&Comma).ok().unwrap() && parser.token != Eof {
            cx.span_err(parser.span, "expected `,`");
            return None;
        }
    }

    if bad {
        return None;
    }

    Some(entries)
}

fn parse_set(cx: &mut ExtCtxt, tts: &[TokenTree]) -> Option<Vec<Entry>> {
    let mut parser = parse::new_parser_from_tts(cx.parse_sess(), cx.cfg(), tts.to_vec());
    let mut entries = Vec::new();
    let value = quote_expr!(&*cx, ());

    let mut bad = false;
    while parser.token != Eof {
        let key = cx.expander().fold_expr(parser.parse_expr().unwrap());
        let key_contents = parse_key(cx, &*key).unwrap_or_else(|| {
            bad = true;
            Key::Str(InternedString::new(""))
        });

        entries.push(Entry {
            key_contents: key_contents,
            key: key,
            value: value.clone(),
        });

        if !parser.eat(&Comma).ok().unwrap() && parser.token != Eof {
            cx.span_err(parser.span, "expected `,`");
            return None;
        }
    }

    if bad {
        return None;
    }

    Some(entries)
}

fn parse_key(cx: &mut ExtCtxt, e: &Expr) -> Option<Key> {
    match e.node {
        ExprLit(ref lit) => {
            match lit.node {
                ast::LitStr(ref s, _) => Some(Key::Str(s.clone())),
                ast::LitByteStr(ref b) => Some(Key::Binary(b.clone())),
                ast::LitByte(b) => Some(Key::U8(b)),
                ast::LitChar(c) => Some(Key::Char(c)),
                ast::LitInt(i, ast::SignedIntLit(ast::TyI8, ast::Plus)) => Some(Key::I8(i as i8)),
                ast::LitInt(i, ast::SignedIntLit(ast::TyI8, ast::Minus)) =>
                    Some(Key::I8(-(i as i8))),
                ast::LitInt(i, ast::SignedIntLit(ast::TyI16, ast::Plus)) =>
                    Some(Key::I16(i as i16)),
                ast::LitInt(i, ast::SignedIntLit(ast::TyI16, ast::Minus)) =>
                    Some(Key::I16(-(i as i16))),
                ast::LitInt(i, ast::SignedIntLit(ast::TyI32, ast::Plus)) =>
                    Some(Key::I32(i as i32)),
                ast::LitInt(i, ast::SignedIntLit(ast::TyI32, ast::Minus)) =>
                    Some(Key::I32(-(i as i32))),
                ast::LitInt(i, ast::SignedIntLit(ast::TyI64, ast::Plus)) =>
                    Some(Key::I64(i as i64)),
                ast::LitInt(i, ast::SignedIntLit(ast::TyI64, ast::Minus)) =>
                    Some(Key::I64(-(i as i64))),
                ast::LitInt(i, ast::UnsignedIntLit(ast::TyU8)) => Some(Key::U8(i as u8)),
                ast::LitInt(i, ast::UnsignedIntLit(ast::TyU16)) => Some(Key::U16(i as u16)),
                ast::LitInt(i, ast::UnsignedIntLit(ast::TyU32)) => Some(Key::U32(i as u32)),
                ast::LitInt(i, ast::UnsignedIntLit(ast::TyU64)) => Some(Key::U64(i as u64)),
                ast::LitBool(b) => Some(Key::Bool(b)),
                _ => {
                    cx.span_err(e.span, "unsupported literal type");
                    None
                }
            }
        }
        ExprVec(ref v) => {
            let bytes: Vec<Option<u8>> = v.iter().map(|expr|
                if let ExprLit(ref p) = expr.node {
                    match **p {
                        Spanned {node: ast::LitInt(val, ast::UnsignedIntLit(ast::UintTy::TyU8)), ..} if val < 256 => Some(val as u8),
                        Spanned {node: ast::LitInt(val, ast::UnsuffixedIntLit(ast::Plus)), ..} if val < 256 => Some(val as u8),
                        _ => None,
                    }
                } else {
                    None
            }).collect();
            if bytes.iter().all(|x| x.is_some()) {
                Some(Key::Binary(std::rc::Rc::new(bytes.iter().map(|x| x.unwrap()).collect())))
            } else {
                cx.span_err(e.span,
                            "not all elements of an expected u8 array literal were u8 literals");
                None
            }
        }
        _ => {
            cx.span_err(e.span, "expected a literal");
            None
        }
    }
}

fn has_duplicates(cx: &mut ExtCtxt, sp: Span, entries: &[Entry]) -> bool {
    let mut dups = false;
    let mut strings = HashMap::new();
    for entry in entries.iter() {
        let &mut (ref mut spans, _) = match strings.entry(&entry.key_contents) {
            Occupied(e) => e.into_mut(),
            Vacant(e) => e.insert((vec![], &entry.key)),
        };
        spans.push(entry.key.span);
    }

    for &(ref spans, key) in strings.values() {
        if spans.len() == 1 {
            continue;
        }

        dups = true;
        let mut err = cx.struct_span_err(sp, &*format!("duplicate key {}",
                                                       pprust::expr_to_string(&**key)));
        for span in spans.iter() {
            err.span_note(*span, "one occurrence here");
        }
        err.emit();
    }

    dups
}
<commit_msg>Switch timing info back to a hint<commit_after>//! Compiler plugin defining macros that create PHF data structures.
//!
//! # Example
//!
//! ```rust
//! #![feature(plugin, core)]
//! #![plugin(phf_macros)]
//!
//! extern crate phf;
//!
//! #[derive(Clone)]
//! pub enum Keyword {
//!     Loop,
//!     Continue,
//!     Break,
//!     Fn,
//!     Extern,
//! }
//!
//! static KEYWORDS: phf::Map<&'static str, Keyword> = phf_map! {
//!     "loop" => Keyword::Loop,
//!     "continue" => Keyword::Continue,
//!     "break" => Keyword::Break,
//!     "fn" => Keyword::Fn,
//!     "extern" => Keyword::Extern,
//! };
//!
//! pub fn parse_keyword(keyword: &str) -> Option<Keyword> {
//!     KEYWORDS.get(keyword).cloned()
//! }
//! # fn main() {}
//! ```
#![doc(html_root_url="http://sfackler.github.io/rust-phf/doc/v0.7.10")]
#![feature(plugin_registrar, quote, rustc_private)]

extern crate syntax;
#[cfg(feature = "stats")]
extern crate time;
extern crate rustc_plugin;
extern crate phf_shared;
extern crate phf_generator;

use std::collections::HashMap;
use std::collections::hash_map::Entry::{Occupied, Vacant};
use syntax::ast::{self, TokenTree, Expr, ExprLit, ExprVec};
use syntax::codemap::{Span, Spanned};
use syntax::ext::base::{DummyResult, ExtCtxt, MacResult};
use syntax::fold::Folder;
use syntax::parse;
use syntax::parse::token::{InternedString, Comma, Eof, FatArrow};
use syntax::print::pprust;
use rustc_plugin::Registry;
use phf_generator::HashState;
use std::env;

use util::{Entry, Key};
use util::{create_map, create_set, create_ordered_map, create_ordered_set};

pub mod util;
mod macros;

#[plugin_registrar]
#[doc(hidden)]
pub fn macro_registrar(reg: &mut Registry) {
    reg.register_macro("phf_map", expand_phf_map);
    reg.register_macro("phf_set", expand_phf_set);
    reg.register_macro("phf_ordered_map", expand_phf_ordered_map);
    reg.register_macro("phf_ordered_set", expand_phf_ordered_set);
}

fn generate_hash(cx: &mut ExtCtxt, sp: Span, entries: &[Entry]) -> HashState {
    #[cfg(feature = "stats")]
    use time::precise_time_s;
    #[cfg(not(feature = "stats"))]
    fn precise_time_s() -> f64 {
        0.
    }

    let start = precise_time_s();
    let state = phf_generator::generate_hash(entries);
    let time = precise_time_s() - start;

    if cfg!(feature = "stats") && env::var_os("PHF_STATS").is_some() {
        cx.parse_sess
          .span_diagnostic
          .span_note_without_error(sp, &format!("PHF generation took {} seconds", time));
    }

    state
}

fn expand_phf_map(cx: &mut ExtCtxt, sp: Span, tts: &[TokenTree]) -> Box<MacResult + 'static> {
    let entries = match parse_map(cx, tts) {
        Some(entries) => entries,
        None => return DummyResult::expr(sp),
    };

    if has_duplicates(cx, sp, &*entries) {
        return DummyResult::expr(sp);
    }

    let state = generate_hash(cx, sp, &*entries);

    create_map(cx, sp, entries, state)
}

fn expand_phf_set(cx: &mut ExtCtxt, sp: Span, tts: &[TokenTree]) -> Box<MacResult + 'static> {
    let entries = match parse_set(cx, tts) {
        Some(entries) => entries,
        None => return DummyResult::expr(sp),
    };

    if has_duplicates(cx, sp, &*entries) {
        return DummyResult::expr(sp);
    }

    let state = generate_hash(cx, sp, &*entries);

    create_set(cx, sp, entries, state)
}

fn expand_phf_ordered_map(cx: &mut ExtCtxt,
                          sp: Span,
                          tts: &[TokenTree])
                          -> Box<MacResult + 'static> {
    let entries = match parse_map(cx, tts) {
        Some(entries) => entries,
        None => return DummyResult::expr(sp),
    };

    if has_duplicates(cx, sp, &*entries) {
        return DummyResult::expr(sp);
    }

    let state = generate_hash(cx, sp, &*entries);

    create_ordered_map(cx, sp, entries, state)
}

fn expand_phf_ordered_set(cx: &mut ExtCtxt,
                          sp: Span,
                          tts: &[TokenTree])
                          -> Box<MacResult + 'static> {
    let entries = match parse_set(cx, tts) {
        Some(entries) => entries,
        None => return DummyResult::expr(sp),
    };

    if has_duplicates(cx, sp, &*entries) {
        return DummyResult::expr(sp);
    }

    let state = generate_hash(cx, sp, &*entries);

    create_ordered_set(cx, sp, entries, state)
}

fn parse_map(cx: &mut ExtCtxt, tts: &[TokenTree]) -> Option<Vec<Entry>> {
    let mut parser = parse::new_parser_from_tts(cx.parse_sess(), cx.cfg(), tts.to_vec());
    let mut entries = Vec::new();

    let mut bad = false;
    while parser.token != Eof {
        let key = cx.expander().fold_expr(parser.parse_expr().unwrap());
        let key_contents = parse_key(cx, &*key).unwrap_or_else(|| {
            bad = true;
            Key::Str(InternedString::new(""))
        });

        if !parser.eat(&FatArrow).ok().unwrap() {
            cx.span_err(parser.span, "expected `=>`");
            return None;
        }

        let value = parser.parse_expr().unwrap();

        entries.push(Entry {
            key_contents: key_contents,
            key: key,
            value: value,
        });

        if !parser.eat(&Comma).ok().unwrap() && parser.token != Eof {
            cx.span_err(parser.span, "expected `,`");
            return None;
        }
    }

    if bad {
        return None;
    }

    Some(entries)
}

fn parse_set(cx: &mut ExtCtxt, tts: &[TokenTree]) -> Option<Vec<Entry>> {
    let mut parser = parse::new_parser_from_tts(cx.parse_sess(), cx.cfg(), tts.to_vec());
    let mut entries = Vec::new();
    let value = quote_expr!(&*cx, ());

    let mut bad = false;
    while parser.token != Eof {
        let key = cx.expander().fold_expr(parser.parse_expr().unwrap());
        let key_contents = parse_key(cx, &*key).unwrap_or_else(|| {
            bad = true;
            Key::Str(InternedString::new(""))
        });

        entries.push(Entry {
            key_contents: key_contents,
            key: key,
            value: value.clone(),
        });

        if !parser.eat(&Comma).ok().unwrap() && parser.token != Eof {
            cx.span_err(parser.span, "expected `,`");
            return None;
        }
    }

    if bad {
        return None;
    }

    Some(entries)
}

fn parse_key(cx: &mut ExtCtxt, e: &Expr) -> Option<Key> {
    match e.node {
        ExprLit(ref lit) => {
            match lit.node {
                ast::LitStr(ref s, _) => Some(Key::Str(s.clone())),
                ast::LitByteStr(ref b) => Some(Key::Binary(b.clone())),
                ast::LitByte(b) => Some(Key::U8(b)),
                ast::LitChar(c) => Some(Key::Char(c)),
                ast::LitInt(i, ast::SignedIntLit(ast::TyI8, ast::Plus)) => Some(Key::I8(i as i8)),
                ast::LitInt(i, ast::SignedIntLit(ast::TyI8, ast::Minus)) =>
                    Some(Key::I8(-(i as i8))),
                ast::LitInt(i, ast::SignedIntLit(ast::TyI16, ast::Plus)) =>
                    Some(Key::I16(i as i16)),
                ast::LitInt(i, ast::SignedIntLit(ast::TyI16, ast::Minus)) =>
                    Some(Key::I16(-(i as i16))),
                ast::LitInt(i, ast::SignedIntLit(ast::TyI32, ast::Plus)) =>
                    Some(Key::I32(i as i32)),
                ast::LitInt(i, ast::SignedIntLit(ast::TyI32, ast::Minus)) =>
                    Some(Key::I32(-(i as i32))),
                ast::LitInt(i, ast::SignedIntLit(ast::TyI64, ast::Plus)) =>
                    Some(Key::I64(i as i64)),
                ast::LitInt(i, ast::SignedIntLit(ast::TyI64, ast::Minus)) =>
                    Some(Key::I64(-(i as i64))),
                ast::LitInt(i, ast::UnsignedIntLit(ast::TyU8)) => Some(Key::U8(i as u8)),
                ast::LitInt(i, ast::UnsignedIntLit(ast::TyU16)) => Some(Key::U16(i as u16)),
                ast::LitInt(i, ast::UnsignedIntLit(ast::TyU32)) => Some(Key::U32(i as u32)),
                ast::LitInt(i, ast::UnsignedIntLit(ast::TyU64)) => Some(Key::U64(i as u64)),
                ast::LitBool(b) => Some(Key::Bool(b)),
                _ => {
                    cx.span_err(e.span, "unsupported literal type");
                    None
                }
            }
        }
        ExprVec(ref v) => {
            let bytes: Vec<Option<u8>> = v.iter().map(|expr|
                if let ExprLit(ref p) = expr.node {
                    match **p {
                        Spanned {node: ast::LitInt(val, ast::UnsignedIntLit(ast::UintTy::TyU8)), ..} if val < 256 => Some(val as u8),
                        Spanned {node: ast::LitInt(val, ast::UnsuffixedIntLit(ast::Plus)), ..} if val < 256 => Some(val as u8),
                        _ => None,
                    }
                } else {
                    None
            }).collect();
            if bytes.iter().all(|x| x.is_some()) {
                Some(Key::Binary(std::rc::Rc::new(bytes.iter().map(|x| x.unwrap()).collect())))
            } else {
                cx.span_err(e.span,
                            "not all elements of an expected u8 array literal were u8 literals");
                None
            }
        }
        _ => {
            cx.span_err(e.span, "expected a literal");
            None
        }
    }
}

fn has_duplicates(cx: &mut ExtCtxt, sp: Span, entries: &[Entry]) -> bool {
    let mut dups = false;
    let mut strings = HashMap::new();
    for entry in entries.iter() {
        let &mut (ref mut spans, _) = match strings.entry(&entry.key_contents) {
            Occupied(e) => e.into_mut(),
            Vacant(e) => e.insert((vec![], &entry.key)),
        };
        spans.push(entry.key.span);
    }

    for &(ref spans, key) in strings.values() {
        if spans.len() == 1 {
            continue;
        }

        dups = true;
        let mut err = cx.struct_span_err(sp, &*format!("duplicate key {}",
                                                       pprust::expr_to_string(&**key)));
        for span in spans.iter() {
            err.span_note(*span, "one occurrence here");
        }
        err.emit();
    }

    dups
}
<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

//! The bulk of the HTML parser integration is in `script::parse::html`.
//! This module is mostly about its interaction with DOM memory management.

use document_loader::LoadType;
use dom::bindings::cell::DOMRefCell;
use dom::bindings::codegen::Bindings::ServoHTMLParserBinding;
use dom::bindings::global::GlobalRef;
use dom::bindings::js::{JS, Root};
use dom::bindings::refcounted::Trusted;
use dom::bindings::reflector::{Reflector, reflect_dom_object};
use dom::bindings::trace::JSTraceable;
use dom::document::Document;
use dom::node::Node;
use dom::text::Text;
use dom::window::Window;
use encoding::all::UTF_8;
use encoding::types::{DecoderTrap, Encoding};
use html5ever::tokenizer;
use html5ever::tree_builder;
use html5ever::tree_builder::{NodeOrText, TreeBuilder, TreeBuilderOpts};
use hyper::header::ContentType;
use hyper::mime::{Mime, SubLevel, TopLevel};
use js::jsapi::JSTracer;
use msg::constellation_msg::{PipelineId, SubpageId};
use net_traits::{AsyncResponseListener, Metadata};
use network_listener::PreInvoke;
use parse::Parser;
use script_task::{ScriptChan, ScriptTask};
use std::cell::Cell;
use std::default::Default;
use url::Url;
use util::str::DOMString;

#[must_root]
#[derive(JSTraceable, HeapSizeOf)]
pub struct Sink {
    pub base_url: Option<Url>,
    pub document: JS<Document>,
}

impl Sink {
    #[allow(unrooted_must_root)] // method is only run at parse time
    pub fn get_or_create(&self, child: NodeOrText<JS<Node>>) -> Root<Node> {
        match child {
            NodeOrText::AppendNode(n) => Root::from_ref(&*n),
            NodeOrText::AppendText(t) => {
                // FIXME(ajeffrey): convert directly from tendrils to DOMStrings
                let s: String = t.into();
                let text = Text::new(DOMString::from(s), &self.document);
                Root::upcast(text)
            }
        }
    }
}

/// FragmentContext is used only to pass this group of related values
/// into functions.
#[derive(Copy, Clone)]
pub struct FragmentContext<'a> {
    pub context_elem: &'a Node,
    pub form_elem: Option<&'a Node>,
}

pub type Tokenizer = tokenizer::Tokenizer<TreeBuilder<JS<Node>, Sink>>;

/// The context required for asynchronously fetching a document and parsing it progressively.
pub struct ParserContext {
    /// The parser that initiated the request.
    parser: Option<Trusted<ServoHTMLParser>>,
    /// Is this a synthesized document
    is_synthesized_document: bool,
    /// The pipeline associated with this document.
    id: PipelineId,
    /// The subpage associated with this document.
    subpage: Option<SubpageId>,
    /// The target event loop for the response notifications.
    script_chan: Box