<commit_before>//! Windows specific definitions
use std::io::{self, Stdout, Write};
use std::mem;
use std::sync::atomic;

use kernel32;
use unicode_width::UnicodeWidthChar;
use winapi;

use config::Config;
use consts::{self, KeyPress};
use error;
use Result;
use line_buffer::LineBuffer;
use super::{Position, RawMode, RawReader, Renderer, Term};

const STDIN_FILENO: winapi::DWORD = winapi::STD_INPUT_HANDLE;
const STDOUT_FILENO: winapi::DWORD = winapi::STD_OUTPUT_HANDLE;

fn get_std_handle(fd: winapi::DWORD) -> Result<winapi::HANDLE> {
    let handle = unsafe { kernel32::GetStdHandle(fd) };
    if handle == winapi::INVALID_HANDLE_VALUE {
        try!(Err(io::Error::last_os_error()));
    } else if handle.is_null() {
        try!(Err(io::Error::new(io::ErrorKind::Other,
                                "no stdio handle available for this process")));
    }
    Ok(handle)
}

#[macro_export]
macro_rules! check {
    ($funcall:expr) => {
        {
        let rc = unsafe { $funcall };
        if rc == 0 {
            try!(Err(io::Error::last_os_error()));
        }
        rc
        }
    };
}

fn get_win_size(handle: winapi::HANDLE) -> (usize, usize) {
    let mut info = unsafe { mem::zeroed() };
    match unsafe { kernel32::GetConsoleScreenBufferInfo(handle, &mut info) } {
        0 => (80, 24),
        _ => (info.dwSize.X as usize, (1 + info.srWindow.Bottom - info.srWindow.Top) as usize),
    }
}

fn get_console_mode(handle: winapi::HANDLE) -> Result<winapi::DWORD> {
    let mut original_mode = 0;
    check!(kernel32::GetConsoleMode(handle, &mut original_mode));
    Ok(original_mode)
}

pub type Mode = ConsoleMode;

#[derive(Clone,Copy,Debug)]
pub struct ConsoleMode {
    original_mode: winapi::DWORD,
    stdin_handle: winapi::HANDLE,
}

impl RawMode for Mode {
    /// Disable RAW mode for the terminal.
    fn disable_raw_mode(&self) -> Result<()> {
        check!(kernel32::SetConsoleMode(self.stdin_handle, self.original_mode));
        Ok(())
    }
}

/// Console input reader
pub struct ConsoleRawReader {
    handle: winapi::HANDLE,
    buf: Option<u16>,
}

impl ConsoleRawReader {
    pub fn new() -> Result<ConsoleRawReader> {
        let handle = try!(get_std_handle(STDIN_FILENO));
        Ok(ConsoleRawReader {
               handle: handle,
               buf: None,
           })
    }
}

impl RawReader for ConsoleRawReader {
    fn next_key(&mut self) -> Result<KeyPress> {
        use std::char::decode_utf16;
        // use winapi::{LEFT_ALT_PRESSED, LEFT_CTRL_PRESSED, RIGHT_ALT_PRESSED, RIGHT_CTRL_PRESSED};
        use winapi::{LEFT_ALT_PRESSED, RIGHT_ALT_PRESSED};

        let mut rec: winapi::INPUT_RECORD = unsafe { mem::zeroed() };
        let mut count = 0;
        loop {
            // TODO GetNumberOfConsoleInputEvents
            check!(kernel32::ReadConsoleInputW(self.handle,
                                               &mut rec,
                                               1 as winapi::DWORD,
                                               &mut count));

            if rec.EventType == winapi::WINDOW_BUFFER_SIZE_EVENT {
                SIGWINCH.store(true, atomic::Ordering::SeqCst);
                debug!(target: "rustyline", "SIGWINCH");
                return Err(error::ReadlineError::WindowResize);
            } else if rec.EventType != winapi::KEY_EVENT {
                continue;
            }
            let key_event = unsafe { rec.KeyEvent() };
            // writeln!(io::stderr(), "key_event: {:?}", key_event).unwrap();
            if key_event.bKeyDown == 0 &&
               key_event.wVirtualKeyCode != winapi::VK_MENU as winapi::WORD {
                continue;
            }
            // key_event.wRepeatCount seems to be always set to 1 (maybe because we only read one character at a time)

            // let alt_gr = key_event.dwControlKeyState & (LEFT_CTRL_PRESSED | RIGHT_ALT_PRESSED) != 0;
            let alt = key_event.dwControlKeyState & (LEFT_ALT_PRESSED | RIGHT_ALT_PRESSED) != 0;
            let ctrl = key_event.dwControlKeyState & (LEFT_CTRL_PRESSED | RIGHT_CTRL_PRESSED) != 0;
            let meta = alt;

            let utf16 = key_event.UnicodeChar;
            if utf16 == 0 {
                match key_event.wVirtualKeyCode as i32 {
                    winapi::VK_LEFT => {
                        return Ok(if ctrl {
                                      KeyPress::ControlLeft
                                  } else {
                                      KeyPress::Left
                                  })
                    }
                    winapi::VK_RIGHT => {
                        return Ok(if ctrl {
                                      KeyPress::ControlRight
                                  } else {
                                      KeyPress::Right
                                  })
                    }
                    winapi::VK_UP => {
                        return Ok(if ctrl {
                                      KeyPress::ControlUp
                                  } else {
                                      KeyPress::Up
                                  })
                    }
                    winapi::VK_DOWN => {
                        return Ok(if ctrl {
                                      KeyPress::ControlDown
                                  } else {
                                      KeyPress::Down
                                  })
                    }
                    winapi::VK_DELETE => return Ok(KeyPress::Delete),
                    winapi::VK_HOME => return Ok(KeyPress::Home),
                    winapi::VK_END => return Ok(KeyPress::End),
                    winapi::VK_PRIOR => return Ok(KeyPress::PageUp),
                    winapi::VK_NEXT => return Ok(KeyPress::PageDown),
                    winapi::VK_INSERT => return Ok(KeyPress::Insert),
                    winapi::VK_F1 => return Ok(KeyPress::F(1)),
                    winapi::VK_F2 => return Ok(KeyPress::F(2)),
                    winapi::VK_F3 => return Ok(KeyPress::F(3)),
                    winapi::VK_F4 => return Ok(KeyPress::F(4)),
                    winapi::VK_F5 => return Ok(KeyPress::F(5)),
                    winapi::VK_F6 => return Ok(KeyPress::F(6)),
                    winapi::VK_F7 => return Ok(KeyPress::F(7)),
                    winapi::VK_F8 => return Ok(KeyPress::F(8)),
                    winapi::VK_F9 => return Ok(KeyPress::F(9)),
                    winapi::VK_F10 => return Ok(KeyPress::F(10)),
                    winapi::VK_F11 => return Ok(KeyPress::F(11)),
                    winapi::VK_F12 => return Ok(KeyPress::F(12)),
                    // winapi::VK_BACK is correctly handled because the key_event.UnicodeChar is also set.
                    _ => continue,
                };
            } else if utf16 == 27 {
                return Ok(KeyPress::Esc);
            } else {
                // TODO How to support surrogate pair ?
                self.buf = Some(utf16);
                let orc = decode_utf16(self).next();
                if orc.is_none() {
                    return Err(error::ReadlineError::Eof);
                }
                let c = try!(orc.unwrap());
                if meta {
                    return Ok(KeyPress::Meta(c));
                } else {
                    return Ok(consts::char_to_key_press(c));
                }
            }
        }
    }
}

impl Iterator for ConsoleRawReader {
    type Item = u16;

    fn next(&mut self) -> Option<u16> {
        let buf = self.buf;
        self.buf = None;
        buf
    }
}

pub struct ConsoleRenderer {
    out: Stdout,
    handle: winapi::HANDLE,
    cols: usize, // Number of columns in terminal
}

impl ConsoleRenderer {
    fn new(handle: winapi::HANDLE) -> ConsoleRenderer {
        let (cols, _) = get_win_size(handle);
        ConsoleRenderer {
            out: io::stdout(),
            handle: handle,
            cols: cols,
        }
    }

    fn get_console_screen_buffer_info(&self) -> Result<winapi::CONSOLE_SCREEN_BUFFER_INFO> {
        let mut info = unsafe { mem::zeroed() };
        check!(kernel32::GetConsoleScreenBufferInfo(self.handle, &mut info));
        Ok(info)
    }

    fn set_console_cursor_position(&mut self, pos: winapi::COORD) -> Result<()> {
        check!(kernel32::SetConsoleCursorPosition(self.handle, pos));
        Ok(())
    }

    fn fill_console_output_character(&mut self,
                                     length: winapi::DWORD,
                                     pos: winapi::COORD)
                                     -> Result<()> {
        let mut _count = 0;
        check!(kernel32::FillConsoleOutputCharacterA(self.handle,
                                                     ' ' as winapi::CHAR,
                                                     length,
                                                     pos,
                                                     &mut _count));
        Ok(())
    }
}

impl Renderer for ConsoleRenderer {
    fn move_cursor(&mut self, old: Position, new: Position) -> Result<()> {
        let mut info = try!(self.get_console_screen_buffer_info());
        if new.row > old.row {
            info.dwCursorPosition.Y += (new.row - old.row) as i16;
        } else {
            info.dwCursorPosition.Y -= (old.row - new.row) as i16;
        }
        if new.col > old.col {
            info.dwCursorPosition.X += (new.col - old.col) as i16;
        } else {
            info.dwCursorPosition.X -= (old.col - new.col) as i16;
        }
        self.set_console_cursor_position(info.dwCursorPosition)
    }

    fn refresh_line(&mut self,
                    prompt: &str,
                    prompt_size: Position,
                    line: &LineBuffer,
                    current_row: usize,
                    old_rows: usize)
                    -> Result<(Position, Position)> {
        // calculate the position of the end of the input line
        let end_pos = self.calculate_position(line, prompt_size);
        // calculate the desired position of the cursor
        let cursor = self.calculate_position(&line[..line.pos()], prompt_size);

        // position at the start of the prompt, clear to end of previous input
        let mut info = try!(self.get_console_screen_buffer_info());
        info.dwCursorPosition.X = 0;
        info.dwCursorPosition.Y -= current_row as i16;
        try!(self.set_console_cursor_position(info.dwCursorPosition));
        let mut _count = 0;
        try!(self.fill_console_output_character((info.dwSize.X * (old_rows as i16 + 1)) as u32,
                                                info.dwCursorPosition));
        let mut ab = String::new();
        // display the prompt
        ab.push_str(prompt); // TODO handle ansi escape code (SetConsoleTextAttribute)
        // display the input line
        ab.push_str(&line);
        try!(self.write_and_flush(ab.as_bytes()));

        // position the cursor
        let mut info = try!(self.get_console_screen_buffer_info());
        info.dwCursorPosition.X = cursor.col as i16;
        info.dwCursorPosition.Y -= (end_pos.row - cursor.row) as i16;
        try!(self.set_console_cursor_position(info.dwCursorPosition));
        Ok((cursor, end_pos))
    }

    fn write_and_flush(&mut self, buf: &[u8]) -> Result<()> {
        try!(self.out.write_all(buf));
        try!(self.out.flush());
        Ok(())
    }

    /// Characters with 2 column width are correctly handled (not splitted).
    fn calculate_position(&self, s: &str, orig: Position) -> Position {
        let mut pos = orig;
        for c in s.chars() {
            let cw = if c == '\n' {
                pos.col = 0;
                pos.row += 1;
                None
            } else {
                c.width()
            };
            if let Some(cw) = cw {
                pos.col += cw;
                if pos.col > self.cols {
                    pos.row += 1;
                    pos.col = cw;
                }
            }
        }
        if pos.col == self.cols {
            pos.col = 0;
            pos.row += 1;
        }
        pos
    }

    /// Clear the screen. Used to handle ctrl+l
    fn clear_screen(&mut self) -> Result<()> {
        let info = try!(self.get_console_screen_buffer_info());
        let coord = winapi::COORD { X: 0, Y: 0 };
        check!(kernel32::SetConsoleCursorPosition(self.handle, coord));
        let mut _count = 0;
        let n = info.dwSize.X as winapi::DWORD * info.dwSize.Y as winapi::DWORD;
        check!(kernel32::FillConsoleOutputCharacterA(self.handle,
                                                     ' ' as winapi::CHAR,
                                                     n,
                                                     coord,
                                                     &mut _count));
        Ok(())
    }

    fn sigwinch(&self) -> bool {
        SIGWINCH.compare_and_swap(true, false, atomic::Ordering::SeqCst)
    }

    /// Try to get the number of columns in the current terminal,
    /// or assume 80 if it fails.
    fn update_size(&mut self) {
        let (cols, _) = get_win_size(self.handle);
        self.cols = cols;
    }

    fn get_columns(&self) -> usize {
        self.cols
    }

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

static SIGWINCH: atomic::AtomicBool = atomic::ATOMIC_BOOL_INIT;

pub type Terminal = Console;

#[derive(Clone,Debug)]
pub struct Console {
    stdin_isatty: bool,
    stdin_handle: winapi::HANDLE,
    stdout_handle: winapi::HANDLE,
}

impl Console {}

impl Term for Console {
    type Reader = ConsoleRawReader;
    type Writer = ConsoleRenderer;
    type Mode = Mode;

    fn new() -> Console {
        use std::ptr;
        let stdin_handle = get_std_handle(STDIN_FILENO);
        let stdin_isatty = match stdin_handle {
            Ok(handle) => {
                // If this function doesn't fail then fd is a TTY
                get_console_mode(handle).is_ok()
            }
            Err(_) => false,
        };

        let stdout_handle = get_std_handle(STDOUT_FILENO).unwrap_or(ptr::null_mut());
        Console {
            stdin_isatty: stdin_isatty,
            stdin_handle: stdin_handle.unwrap_or(ptr::null_mut()),
            stdout_handle: stdout_handle,
        }
    }

    /// Checking for an unsupported TERM in windows is a no-op
    fn is_unsupported(&self) -> bool {
        false
    }

    fn is_stdin_tty(&self) -> bool {
        self.stdin_isatty
    }

    // pub fn install_sigwinch_handler(&mut self) {
    // See ReadConsoleInputW && WINDOW_BUFFER_SIZE_EVENT
    // }

    /// Enable RAW mode for the terminal.
    fn enable_raw_mode(&self) -> Result<Mode> {
        if !self.stdin_isatty {
            try!(Err(io::Error::new(io::ErrorKind::Other,
                                    "no stdio handle available for this process")));
        }
        let original_mode = try!(get_console_mode(self.stdin_handle));
        // Disable these modes
        let raw = original_mode &
                  !(winapi::wincon::ENABLE_LINE_INPUT | winapi::wincon::ENABLE_ECHO_INPUT |
                    winapi::wincon::ENABLE_PROCESSED_INPUT);
        // Enable these modes
        let raw = raw | winapi::wincon::ENABLE_EXTENDED_FLAGS;
        let raw = raw | winapi::wincon::ENABLE_INSERT_MODE;
        let raw = raw | winapi::wincon::ENABLE_QUICK_EDIT_MODE;
        let raw = raw | winapi::wincon::ENABLE_WINDOW_INPUT;
        check!(kernel32::SetConsoleMode(self.stdin_handle, raw));
        Ok(Mode {
               original_mode: original_mode,
               stdin_handle: self.stdin_handle,
           })
    }

    fn create_reader(&self, _: &Config) -> Result<ConsoleRawReader> {
        ConsoleRawReader::new()
    }

    fn create_writer(&self) -> ConsoleRenderer {
        ConsoleRenderer::new(self.stdout_handle)
    }
}
<commit_msg>Fix windows build<commit_after>//! Windows specific definitions
use std::io::{self, Stdout, Write};
use std::mem;
use std::sync::atomic;

use kernel32;
use unicode_width::UnicodeWidthChar;
use winapi;

use config::Config;
use consts::{self, KeyPress};
use error;
use Result;
use line_buffer::LineBuffer;
use super::{Position, RawMode, RawReader, Renderer, Term};

const STDIN_FILENO: winapi::DWORD = winapi::STD_INPUT_HANDLE;
const STDOUT_FILENO: winapi::DWORD = winapi::STD_OUTPUT_HANDLE;

fn get_std_handle(fd: winapi::DWORD) -> Result<winapi::HANDLE> {
    let handle = unsafe { kernel32::GetStdHandle(fd) };
    if handle == winapi::INVALID_HANDLE_VALUE {
        try!(Err(io::Error::last_os_error()));
    } else if handle.is_null() {
        try!(Err(io::Error::new(io::ErrorKind::Other,
                                "no stdio handle available for this process")));
    }
    Ok(handle)
}

#[macro_export]
macro_rules! check {
    ($funcall:expr) => {
        {
        let rc = unsafe { $funcall };
        if rc == 0 {
            try!(Err(io::Error::last_os_error()));
        }
        rc
        }
    };
}

fn get_win_size(handle: winapi::HANDLE) -> (usize, usize) {
    let mut info = unsafe { mem::zeroed() };
    match unsafe { kernel32::GetConsoleScreenBufferInfo(handle, &mut info) } {
        0 => (80, 24),
        _ => (info.dwSize.X as usize, (1 + info.srWindow.Bottom - info.srWindow.Top) as usize),
    }
}

fn get_console_mode(handle: winapi::HANDLE) -> Result<winapi::DWORD> {
    let mut original_mode = 0;
    check!(kernel32::GetConsoleMode(handle, &mut original_mode));
    Ok(original_mode)
}

pub type Mode = ConsoleMode;

#[derive(Clone,Copy,Debug)]
pub struct ConsoleMode {
    original_mode: winapi::DWORD,
    stdin_handle: winapi::HANDLE,
}

impl RawMode for Mode {
    /// Disable RAW mode for the terminal.
    fn disable_raw_mode(&self) -> Result<()> {
        check!(kernel32::SetConsoleMode(self.stdin_handle, self.original_mode));
        Ok(())
    }
}

/// Console input reader
pub struct ConsoleRawReader {
    handle: winapi::HANDLE,
    buf: Option<u16>,
}

impl ConsoleRawReader {
    pub fn new() -> Result<ConsoleRawReader> {
        let handle = try!(get_std_handle(STDIN_FILENO));
        Ok(ConsoleRawReader {
               handle: handle,
               buf: None,
           })
    }
}

impl RawReader for ConsoleRawReader {
    fn next_key(&mut self) -> Result<KeyPress> {
        use std::char::decode_utf16;
        use winapi::{LEFT_ALT_PRESSED, LEFT_CTRL_PRESSED, RIGHT_ALT_PRESSED, RIGHT_CTRL_PRESSED};

        let mut rec: winapi::INPUT_RECORD = unsafe { mem::zeroed() };
        let mut count = 0;
        loop {
            // TODO GetNumberOfConsoleInputEvents
            check!(kernel32::ReadConsoleInputW(self.handle,
                                               &mut rec,
                                               1 as winapi::DWORD,
                                               &mut count));

            if rec.EventType == winapi::WINDOW_BUFFER_SIZE_EVENT {
                SIGWINCH.store(true, atomic::Ordering::SeqCst);
                debug!(target: "rustyline", "SIGWINCH");
                return Err(error::ReadlineError::WindowResize);
            } else if rec.EventType != winapi::KEY_EVENT {
                continue;
            }
            let key_event = unsafe { rec.KeyEvent() };
            // writeln!(io::stderr(), "key_event: {:?}", key_event).unwrap();
            if key_event.bKeyDown == 0 &&
               key_event.wVirtualKeyCode != winapi::VK_MENU as winapi::WORD {
                continue;
            }
            // key_event.wRepeatCount seems to be always set to 1 (maybe because we only read one character at a time)

            // let alt_gr = key_event.dwControlKeyState & (LEFT_CTRL_PRESSED | RIGHT_ALT_PRESSED) != 0;
            let alt = key_event.dwControlKeyState & (LEFT_ALT_PRESSED | RIGHT_ALT_PRESSED) != 0;
            let ctrl = key_event.dwControlKeyState & (LEFT_CTRL_PRESSED | RIGHT_CTRL_PRESSED) != 0;
            let meta = alt;

            let utf16 = key_event.UnicodeChar;
            if utf16 == 0 {
                match key_event.wVirtualKeyCode as i32 {
                    winapi::VK_LEFT => {
                        return Ok(if ctrl {
                                      KeyPress::ControlLeft
                                  } else {
                                      KeyPress::Left
                                  })
                    }
                    winapi::VK_RIGHT => {
                        return Ok(if ctrl {
                                      KeyPress::ControlRight
                                  } else {
                                      KeyPress::Right
                                  })
                    }
                    winapi::VK_UP => {
                        return Ok(if ctrl {
                                      KeyPress::ControlUp
                                  } else {
                                      KeyPress::Up
                                  })
                    }
                    winapi::VK_DOWN => {
                        return Ok(if ctrl {
                                      KeyPress::ControlDown
                                  } else {
                                      KeyPress::Down
                                  })
                    }
                    winapi::VK_DELETE => return Ok(KeyPress::Delete),
                    winapi::VK_HOME => return Ok(KeyPress::Home),
                    winapi::VK_END => return Ok(KeyPress::End),
                    winapi::VK_PRIOR => return Ok(KeyPress::PageUp),
                    winapi::VK_NEXT => return Ok(KeyPress::PageDown),
                    winapi::VK_INSERT => return Ok(KeyPress::Insert),
                    winapi::VK_F1 => return Ok(KeyPress::F(1)),
                    winapi::VK_F2 => return Ok(KeyPress::F(2)),
                    winapi::VK_F3 => return Ok(KeyPress::F(3)),
                    winapi::VK_F4 => return Ok(KeyPress::F(4)),
                    winapi::VK_F5 => return Ok(KeyPress::F(5)),
                    winapi::VK_F6 => return Ok(KeyPress::F(6)),
                    winapi::VK_F7 => return Ok(KeyPress::F(7)),
                    winapi::VK_F8 => return Ok(KeyPress::F(8)),
                    winapi::VK_F9 => return Ok(KeyPress::F(9)),
                    winapi::VK_F10 => return Ok(KeyPress::F(10)),
                    winapi::VK_F11 => return Ok(KeyPress::F(11)),
                    winapi::VK_F12 => return Ok(KeyPress::F(12)),
                    // winapi::VK_BACK is correctly handled because the key_event.UnicodeChar is also set.
                    _ => continue,
                };
            } else if utf16 == 27 {
                return Ok(KeyPress::Esc);
            } else {
                // TODO How to support surrogate pair ?
                self.buf = Some(utf16);
                let orc = decode_utf16(self).next();
                if orc.is_none() {
                    return Err(error::ReadlineError::Eof);
                }
                let c = try!(orc.unwrap());
                if meta {
                    return Ok(KeyPress::Meta(c));
                } else {
                    return Ok(consts::char_to_key_press(c));
                }
            }
        }
    }
}

impl Iterator for ConsoleRawReader {
    type Item = u16;

    fn next(&mut self) -> Option<u16> {
        let buf = self.buf;
        self.buf = None;
        buf
    }
}

pub struct ConsoleRenderer {
    out: Stdout,
    handle: winapi::HANDLE,
    cols: usize, // Number of columns in terminal
}

impl ConsoleRenderer {
    fn new(handle: winapi::HANDLE) -> ConsoleRenderer {
        let (cols, _) = get_win_size(handle);
        ConsoleRenderer {
            out: io::stdout(),
            handle: handle,
            cols: cols,
        }
    }

    fn get_console_screen_buffer_info(&self) -> Result<winapi::CONSOLE_SCREEN_BUFFER_INFO> {
        let mut info = unsafe { mem::zeroed() };
        check!(kernel32::GetConsoleScreenBufferInfo(self.handle, &mut info));
        Ok(info)
    }

    fn set_console_cursor_position(&mut self, pos: winapi::COORD) -> Result<()> {
        check!(kernel32::SetConsoleCursorPosition(self.handle, pos));
        Ok(())
    }

    fn fill_console_output_character(&mut self,
                                     length: winapi::DWORD,
                                     pos: winapi::COORD)
                                     -> Result<()> {
        let mut _count = 0;
        check!(kernel32::FillConsoleOutputCharacterA(self.handle,
                                                     ' ' as winapi::CHAR,
                                                     length,
                                                     pos,
                                                     &mut _count));
        Ok(())
    }
}

impl Renderer for ConsoleRenderer {
    fn move_cursor(&mut self, old: Position, new: Position) -> Result<()> {
        let mut info = try!(self.get_console_screen_buffer_info());
        if new.row > old.row {
            info.dwCursorPosition.Y += (new.row - old.row) as i16;
        } else {
            info.dwCursorPosition.Y -= (old.row - new.row) as i16;
        }
        if new.col > old.col {
            info.dwCursorPosition.X += (new.col - old.col) as i16;
        } else {
            info.dwCursorPosition.X -= (old.col - new.col) as i16;
        }
        self.set_console_cursor_position(info.dwCursorPosition)
    }

    fn refresh_line(&mut self,
                    prompt: &str,
                    prompt_size: Position,
                    line: &LineBuffer,
                    current_row: usize,
                    old_rows: usize)
                    -> Result<(Position, Position)> {
        // calculate the position of the end of the input line
        let end_pos = self.calculate_position(line, prompt_size);
        // calculate the desired position of the cursor
        let cursor = self.calculate_position(&line[..line.pos()], prompt_size);

        // position at the start of the prompt, clear to end of previous input
        let mut info = try!(self.get_console_screen_buffer_info());
        info.dwCursorPosition.X = 0;
        info.dwCursorPosition.Y -= current_row as i16;
        try!(self.set_console_cursor_position(info.dwCursorPosition));
        let mut _count = 0;
        try!(self.fill_console_output_character((info.dwSize.X * (old_rows as i16 + 1)) as u32,
                                                info.dwCursorPosition));
        let mut ab = String::new();
        // display the prompt
        ab.push_str(prompt); // TODO handle ansi escape code (SetConsoleTextAttribute)
        // display the input line
        ab.push_str(&line);
        try!(self.write_and_flush(ab.as_bytes()));

        // position the cursor
        let mut info = try!(self.get_console_screen_buffer_info());
        info.dwCursorPosition.X = cursor.col as i16;
        info.dwCursorPosition.Y -= (end_pos.row - cursor.row) as i16;
        try!(self.set_console_cursor_position(info.dwCursorPosition));
        Ok((cursor, end_pos))
    }

    fn write_and_flush(&mut self, buf: &[u8]) -> Result<()> {
        try!(self.out.write_all(buf));
        try!(self.out.flush());
        Ok(())
    }

    /// Characters with 2 column width are correctly handled (not splitted).
    fn calculate_position(&self, s: &str, orig: Position) -> Position {
        let mut pos = orig;
        for c in s.chars() {
            let cw = if c == '\n' {
                pos.col = 0;
                pos.row += 1;
                None
            } else {
                c.width()
            };
            if let Some(cw) = cw {
                pos.col += cw;
                if pos.col > self.cols {
                    pos.row += 1;
                    pos.col = cw;
                }
            }
        }
        if pos.col == self.cols {
            pos.col = 0;
            pos.row += 1;
        }
        pos
    }

    /// Clear the screen. Used to handle ctrl+l
    fn clear_screen(&mut self) -> Result<()> {
        let info = try!(self.get_console_screen_buffer_info());
        let coord = winapi::COORD { X: 0, Y: 0 };
        check!(kernel32::SetConsoleCursorPosition(self.handle, coord));
        let mut _count = 0;
        let n = info.dwSize.X as winapi::DWORD * info.dwSize.Y as winapi::DWORD;
        check!(kernel32::FillConsoleOutputCharacterA(self.handle,
                                                     ' ' as winapi::CHAR,
                                                     n,
                                                     coord,
                                                     &mut _count));
        Ok(())
    }

    fn sigwinch(&self) -> bool {
        SIGWINCH.compare_and_swap(true, false, atomic::Ordering::SeqCst)
    }

    /// Try to get the number of columns in the current terminal,
    /// or assume 80 if it fails.
    fn update_size(&mut self) {
        let (cols, _) = get_win_size(self.handle);
        self.cols = cols;
    }

    fn get_columns(&self) -> usize {
        self.cols
    }

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

static SIGWINCH: atomic::AtomicBool = atomic::ATOMIC_BOOL_INIT;

pub type Terminal = Console;

#[derive(Clone,Debug)]
pub struct Console {
    stdin_isatty: bool,
    stdin_handle: winapi::HANDLE,
    stdout_handle: winapi::HANDLE,
}

impl Console {}

impl Term for Console {
    type Reader = ConsoleRawReader;
    type Writer = ConsoleRenderer;
    type Mode = Mode;

    fn new() -> Console {
        use std::ptr;
        let stdin_handle = get_std_handle(STDIN_FILENO);
        let stdin_isatty = match stdin_handle {
            Ok(handle) => {
                // If this function doesn't fail then fd is a TTY
                get_console_mode(handle).is_ok()
            }
            Err(_) => false,
        };

        let stdout_handle = get_std_handle(STDOUT_FILENO).unwrap_or(ptr::null_mut());
        Console {
            stdin_isatty: stdin_isatty,
            stdin_handle: stdin_handle.unwrap_or(ptr::null_mut()),
            stdout_handle: stdout_handle,
        }
    }

    /// Checking for an unsupported TERM in windows is a no-op
    fn is_unsupported(&self) -> bool {
        false
    }

    fn is_stdin_tty(&self) -> bool {
        self.stdin_isatty
    }

    // pub fn install_sigwinch_handler(&mut self) {
    // See ReadConsoleInputW && WINDOW_BUFFER_SIZE_EVENT
    // }

    /// Enable RAW mode for the terminal.
    fn enable_raw_mode(&self) -> Result<Mode> {
        if !self.stdin_isatty {
            try!(Err(io::Error::new(io::ErrorKind::Other,
                                    "no stdio handle available for this process")));
        }
        let original_mode = try!(get_console_mode(self.stdin_handle));
        // Disable these modes
        let raw = original_mode &
                  !(winapi::wincon::ENABLE_LINE_INPUT | winapi::wincon::ENABLE_ECHO_INPUT |
                    winapi::wincon::ENABLE_PROCESSED_INPUT);
        // Enable these modes
        let raw = raw | winapi::wincon::ENABLE_EXTENDED_FLAGS;
        let raw = raw | winapi::wincon::ENABLE_INSERT_MODE;
        let raw = raw | winapi::wincon::ENABLE_QUICK_EDIT_MODE;
        let raw = raw | winapi::wincon::ENABLE_WINDOW_INPUT;
        check!(kernel32::SetConsoleMode(self.stdin_handle, raw));
        Ok(Mode {
               original_mode: original_mode,
               stdin_handle: self.stdin_handle,
           })
    }

    fn create_reader(&self, _: &Config) -> Result<ConsoleRawReader> {
        ConsoleRawReader::new()
    }

    fn create_writer(&self) -> ConsoleRenderer {
        ConsoleRenderer::new(self.stdout_handle)
    }
}
<|endoftext|>


<commit_before>infer_schema!( "dotenv:DATABASE_URL" );
<commit_msg>Update schema to use new database env var<commit_after>infer_schema!( "dotenv:TICK_DATABASE_FILE" );
<|endoftext|>


<commit_before>use super::{error::TomlHelper, parsable::Parse};
use log::error;
use toml::Value;

#[derive(Clone, Debug)]
pub struct ChildProperty {
    pub name: String,
    pub rename_getter: Option<String>,
    pub type_name: String,
    pub doc_hidden: bool,
}

impl Parse for ChildProperty {
    fn parse(toml: &Value, object_name: &str) -> Option<ChildProperty> {
        let name = toml
            .lookup("name")
            .and_then(Value::as_str)
            .map(ToOwned::to_owned);
        let name = if let Some(name) = name {
            name
        } else {
            error!("No child property name for `{}`", object_name);
            return None;
        };

        toml.check_unwanted(
            &["name", "type", "doc_hidden", "rename_getter"],
            &format!("child property {}", object_name),
        );

        let type_name = toml
            .lookup("type")
            .and_then(Value::as_str)
            .map(ToOwned::to_owned);
        let type_name = if let Some(type_name) = type_name {
            type_name
        } else {
            error!(
                "No type for child property `{}` for `{}`",
                name, object_name
            );
            return None;
        };
        let doc_hidden = toml
            .lookup("doc_hidden")
            .and_then(Value::as_bool)
            .unwrap_or(false);
        let rename_getter = toml
            .lookup("rename_getter")
            .and_then(Value::as_str)
            .map(ToOwned::to_owned);

        Some(ChildProperty {
            name,
            type_name,
            doc_hidden,
            rename_getter,
        })
    }
}

#[derive(Clone, Debug)]
pub struct ChildProperties {
    pub child_name: Option<String>,
    pub child_type: Option<String>,
    pub properties: Vec<ChildProperty>,
}

impl Parse for ChildProperties {
    fn parse(toml_object: &Value, object_name: &str) -> Option<ChildProperties> {
        let child_name = toml_object
            .lookup("child_name")
            .and_then(Value::as_str)
            .map(ToOwned::to_owned);
        let child_type = toml_object
            .lookup("child_type")
            .and_then(Value::as_str)
            .map(ToOwned::to_owned);
        let mut properties: Vec<ChildProperty> = Vec::new();
        if let Some(configs) = toml_object.lookup("child_prop").and_then(Value::as_array) {
            for config in configs {
                if let Some(item) = ChildProperty::parse(config, object_name) {
                    properties.push(item);
                }
            }
        }

        if !properties.is_empty() {
            Some(ChildProperties {
                child_name,
                child_type,
                properties,
            })
        } else {
            if child_name.is_some() {
                error!("`{}` has child_name but no child_prop's", object_name);
            }
            if child_type.is_some() {
                error!("`{}` has child_type but no child_prop's", object_name);
            }
            None
        }
    }
}

#[cfg(test)]
mod tests {
    use super::{super::parsable::Parse, *};

    fn toml(input: &str) -> ::toml::Value {
        let value = ::toml::from_str(&input);
        assert!(value.is_ok());
        value.unwrap()
    }

    #[test]
    fn child_property_parse() {
        let toml = toml(
            r#"
name = "prop"
type = "prop_type"
"#,
        );
        let child = ChildProperty::parse(&toml, "a").unwrap();
        assert_eq!("prop", child.name);
        assert_eq!("prop_type", child.type_name);
    }

    #[test]
    fn child_property_parse_not_all() {
        let tml = toml(
            r#"
name = "prop"
"#,
        );
        assert!(ChildProperty::parse(&tml, "a").is_none());

        let tml = toml(
            r#"
type_name = "prop_type"
"#,
        );
        assert!(ChildProperty::parse(&tml, "a").is_none());
    }

    #[test]
    fn child_properties_parse() {
        let toml = toml(
            r#"
child_name = "child_name"
child_type = "child_type"
[[child_prop]]
name = "prop"
type = "prop_type"
[[child_prop]]
name = "prop2"
type = "prop_type2"
"#,
        );
        let props = ChildProperties::parse(&toml, "a").unwrap();
        assert_eq!(Some("child_name".into()), props.child_name);
        assert_eq!(Some("child_type".into()), props.child_type);
        assert_eq!(2, props.properties.len());
        assert_eq!("prop", props.properties[0].name);
        assert_eq!("prop_type", props.properties[0].type_name);
        assert_eq!("prop2", props.properties[1].name);
        assert_eq!("prop_type2", props.properties[1].type_name);
    }

    #[test]
    fn child_property_no_parse_without_children() {
        let toml = toml(
            r#"
child_name = "child_name"
child_type = "child_type"
"#,
        );
        let props = ChildProperties::parse(&toml, "a");
        assert!(props.is_none());
    }

    #[test]
    fn child_properties_parse_without_child_type_name() {
        let toml = toml(
            r#"
[[child_prop]]
name = "prop"
type = "prop_type"
"#,
        );
        let props = ChildProperties::parse(&toml, "a").unwrap();
        assert_eq!(None, props.child_name);
        assert_eq!(None, props.child_type);
        assert_eq!(1, props.properties.len());
    }

    #[test]
    fn child_properties_parse_without_child_type() {
        let toml = toml(
            r#"
child_name = "child_name"
[[child_prop]]
name = "prop"
type = "prop_type"
"#,
        );
        let props = ChildProperties::parse(&toml, "a").unwrap();
        assert_eq!(Some("child_name".into()), props.child_name);
        assert_eq!(None, props.child_type);
        assert_eq!(1, props.properties.len());
    }

    #[test]
    fn child_properties_parse_without_child_name() {
        let toml = toml(
            r#"
child_type = "child_type"
[[child_prop]]
name = "prop"
type = "prop_type"
"#,
        );
        let props = ChildProperties::parse(&toml, "a").unwrap();
        assert_eq!(None, props.child_name);
        assert_eq!(Some("child_type".into()), props.child_type);
        assert_eq!(1, props.properties.len());
    }
}
<commit_msg>Fix new 1.52 clippy warning<commit_after>use super::{error::TomlHelper, parsable::Parse};
use log::error;
use toml::Value;

#[derive(Clone, Debug)]
pub struct ChildProperty {
    pub name: String,
    pub rename_getter: Option<String>,
    pub type_name: String,
    pub doc_hidden: bool,
}

impl Parse for ChildProperty {
    fn parse(toml: &Value, object_name: &str) -> Option<ChildProperty> {
        let name = toml
            .lookup("name")
            .and_then(Value::as_str)
            .map(ToOwned::to_owned);
        let name = if let Some(name) = name {
            name
        } else {
            error!("No child property name for `{}`", object_name);
            return None;
        };

        toml.check_unwanted(
            &["name", "type", "doc_hidden", "rename_getter"],
            &format!("child property {}", object_name),
        );

        let type_name = toml
            .lookup("type")
            .and_then(Value::as_str)
            .map(ToOwned::to_owned);
        let type_name = if let Some(type_name) = type_name {
            type_name
        } else {
            error!(
                "No type for child property `{}` for `{}`",
                name, object_name
            );
            return None;
        };
        let doc_hidden = toml
            .lookup("doc_hidden")
            .and_then(Value::as_bool)
            .unwrap_or(false);
        let rename_getter = toml
            .lookup("rename_getter")
            .and_then(Value::as_str)
            .map(ToOwned::to_owned);

        Some(ChildProperty {
            name,
            rename_getter,
            type_name,
            doc_hidden,
        })
    }
}

#[derive(Clone, Debug)]
pub struct ChildProperties {
    pub child_name: Option<String>,
    pub child_type: Option<String>,
    pub properties: Vec<ChildProperty>,
}

impl Parse for ChildProperties {
    fn parse(toml_object: &Value, object_name: &str) -> Option<ChildProperties> {
        let child_name = toml_object
            .lookup("child_name")
            .and_then(Value::as_str)
            .map(ToOwned::to_owned);
        let child_type = toml_object
            .lookup("child_type")
            .and_then(Value::as_str)
            .map(ToOwned::to_owned);
        let mut properties: Vec<ChildProperty> = Vec::new();
        if let Some(configs) = toml_object.lookup("child_prop").and_then(Value::as_array) {
            for config in configs {
                if let Some(item) = ChildProperty::parse(config, object_name) {
                    properties.push(item);
                }
            }
        }

        if !properties.is_empty() {
            Some(ChildProperties {
                child_name,
                child_type,
                properties,
            })
        } else {
            if child_name.is_some() {
                error!("`{}` has child_name but no child_prop's", object_name);
            }
            if child_type.is_some() {
                error!("`{}` has child_type but no child_prop's", object_name);
            }
            None
        }
    }
}

#[cfg(test)]
mod tests {
    use super::{super::parsable::Parse, *};

    fn toml(input: &str) -> ::toml::Value {
        let value = ::toml::from_str(&input);
        assert!(value.is_ok());
        value.unwrap()
    }

    #[test]
    fn child_property_parse() {
        let toml = toml(
            r#"
name = "prop"
type = "prop_type"
"#,
        );
        let child = ChildProperty::parse(&toml, "a").unwrap();
        assert_eq!("prop", child.name);
        assert_eq!("prop_type", child.type_name);
    }

    #[test]
    fn child_property_parse_not_all() {
        let tml = toml(
            r#"
name = "prop"
"#,
        );
        assert!(ChildProperty::parse(&tml, "a").is_none());

        let tml = toml(
            r#"
type_name = "prop_type"
"#,
        );
        assert!(ChildProperty::parse(&tml, "a").is_none());
    }

    #[test]
    fn child_properties_parse() {
        let toml = toml(
            r#"
child_name = "child_name"
child_type = "child_type"
[[child_prop]]
name = "prop"
type = "prop_type"
[[child_prop]]
name = "prop2"
type = "prop_type2"
"#,
        );
        let props = ChildProperties::parse(&toml, "a").unwrap();
        assert_eq!(Some("child_name".into()), props.child_name);
        assert_eq!(Some("child_type".into()), props.child_type);
        assert_eq!(2, props.properties.len());
        assert_eq!("prop", props.properties[0].name);
        assert_eq!("prop_type", props.properties[0].type_name);
        assert_eq!("prop2", props.properties[1].name);
        assert_eq!("prop_type2", props.properties[1].type_name);
    }

    #[test]
    fn child_property_no_parse_without_children() {
        let toml = toml(
            r#"
child_name = "child_name"
child_type = "child_type"
"#,
        );
        let props = ChildProperties::parse(&toml, "a");
        assert!(props.is_none());
    }

    #[test]
    fn child_properties_parse_without_child_type_name() {
        let toml = toml(
            r#"
[[child_prop]]
name = "prop"
type = "prop_type"
"#,
        );
        let props = ChildProperties::parse(&toml, "a").unwrap();
        assert_eq!(None, props.child_name);
        assert_eq!(None, props.child_type);
        assert_eq!(1, props.properties.len());
    }

    #[test]
    fn child_properties_parse_without_child_type() {
        let toml = toml(
            r#"
child_name = "child_name"
[[child_prop]]
name = "prop"
type = "prop_type"
"#,
        );
        let props = ChildProperties::parse(&toml, "a").unwrap();
        assert_eq!(Some("child_name".into()), props.child_name);
        assert_eq!(None, props.child_type);
        assert_eq!(1, props.properties.len());
    }

    #[test]
    fn child_properties_parse_without_child_name() {
        let toml = toml(
            r#"
child_type = "child_type"
[[child_prop]]
name = "prop"
type = "prop_type"
"#,
        );
        let props = ChildProperties::parse(&toml, "a").unwrap();
        assert_eq!(None, props.child_name);
        assert_eq!(Some("child_type".into()), props.child_type);
        assert_eq!(1, props.properties.len());
    }
}
<|endoftext|>


<commit_before>#[derive(Debug)]
pub struct AttributeInfo {
    pub attribute_name_index: u16,
    pub attribute_length: u32,
    pub info: Vec<u8>,
}

#[derive(Debug)]
pub struct ExceptionEntry {
    pub start_pc: u16,
    pub end_pc: u16,
    pub handler_pc: u16,
    pub catch_type: u16
}

#[derive(Debug)]
pub struct CodeAttribute {
    pub max_stack: u16,
    pub max_locals: u16,
    pub code_length: u32,
    pub code: Vec<u8>,
    pub exception_table_length: u16,
    pub exception_table: Vec<ExceptionEntry>,
    pub attributes_count: u16,
    pub attributes: Vec<AttributeInfo>,
}

#[derive(Debug)]
pub struct ExceptionsAttribute {
    pub exception_table_length: u16,
    pub exception_table: Vec<u16>,
}

#[derive(Debug)]
pub struct ConstantValueAttribute {
    pub constant_value_index: u16,
}
<commit_msg>Add types for StackMapTableAttribute<commit_after>#[derive(Debug)]
pub struct AttributeInfo {
    pub attribute_name_index: u16,
    pub attribute_length: u32,
    pub info: Vec<u8>,
}

#[derive(Debug)]
pub struct ExceptionEntry {
    pub start_pc: u16,
    pub end_pc: u16,
    pub handler_pc: u16,
    pub catch_type: u16
}

#[derive(Debug)]
pub struct CodeAttribute {
    pub max_stack: u16,
    pub max_locals: u16,
    pub code_length: u32,
    pub code: Vec<u8>,
    pub exception_table_length: u16,
    pub exception_table: Vec<ExceptionEntry>,
    pub attributes_count: u16,
    pub attributes: Vec<AttributeInfo>,
}

#[derive(Copy,Clone,Debug)]
#[repr(u8)]
pub enum VerificationTypeInfo {
    Top               = 0,
    Integer           = 1,
    Float             = 2,
    Double            = 3,
    Long              = 4,
    Null              = 5,
    UninitializedThis = 6,
    Object            = 7,
    Uninitialized     = 8,
}

#[derive(Debug)]
pub enum StackMapFrame {
    SameFrame { frame_type: u8 },
    SameLocals1StackItemFrame { frame_type: u8, stack: VerificationTypeInfo },
    SameLocals1StackItemFrameExtended { frame_type: u8, offset_delta: u16, stack: VerificationTypeInfo },
    ChopFrame { frame_type: u8, offset_delta: u16 },
    SameFrameExtended { frame_type: u8, offset_delta: u16 },
    AppendFrame { frame_type: u8, offset_delta: u16, locals: Vec<VerificationTypeInfo> },
    FullFrame { frame_type: u8, offset_delta: u16, number_of_locals: u16, locals: Vec<VerificationTypeInfo>, number_of_stack_items: u16, stack: Vec<VerificationTypeInfo> },
}

#[derive(Debug)]
pub struct StackMapTableAttribute {
    pub number_of_entries: u16,
    pub entries: Vec<StackMapFrame>,
}

#[derive(Debug)]
pub struct ExceptionsAttribute {
    pub exception_table_length: u16,
    pub exception_table: Vec<u16>,
}

#[derive(Debug)]
pub struct ConstantValueAttribute {
    pub constant_value_index: u16,
}
<|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/.

//! Structs and methods for searching for tweets.
//!
//! Since there are several optional parameters for searches, egg-mode handles it with a builder
//! pattern. To begin, call `search` with your requested search term. Additional parameters can be
//! added onto the `SearchBuilder` struct that is returned. When you're ready to load the first
//! page of results, hand your tokens to `call`.
//!
//! ```rust,no_run
//! # let token = egg_mode::Token::Access {
//! #     consumer: egg_mode::KeyPair::new("", ""),
//! #     access: egg_mode::KeyPair::new("", ""),
//! # };
//! use egg_mode::search::{self, ResultType};
//!
//! let search = search::search("rustlang")
//!                     .result_type(ResultType::Recent)
//!                     .call(&token)
//!                     .unwrap();
//!
//! for tweet in &search.statuses {
//!     println!("(@{}) {}", tweet.user.as_ref().unwrap().screen_name, tweet.text);
//! }
//! ```
//!
//! Once you have your `SearchResult`, you can navigate the search results by calling `older` and
//! `newer` to get the next and previous pages, respsectively. In addition, you can see your
//! original query in the search result struct as well, so you can categorize multiple searches by
//! their query. While this is given as a regular field, note that modifying `query` will not
//! change what is searched for when you call `older` or `newer`; the `SearchResult` keeps its
//! search arguments in a separate private field.
//!
//! The search parameter given in the initial call to `search` has several options itself. A full
//! reference is available in [Twitter's Search API documentation][search-doc]. This listing by
//! itself does not include the search by Place ID, as mentioned on [a separate Tweets by Place
//! page][search-place]. A future version of egg-mode might break these options into further
//! methods on `SearchBuilder`.
//!
//! [search-doc]: https://dev.twitter.com/rest/public/search
//! [search-place]: https://dev.twitter.com/rest/public/search-by-place

use std::collections::HashMap;
use std::fmt;

use rustc_serialize::json;

use auth;
use error;
use error::Error::{InvalidResponse, MissingValue};
use links;
use tweet::Tweet;
use common::*;

///Begin setting up a tweet search with the given query.
pub fn search<'a>(query: &'a str) -> SearchBuilder<'a> {
    SearchBuilder {
        query: query,
        lang: None,
        result_type: None,
        count: None,
        until: None,
        geocode: None,
        since_id: None,
        max_id: None,
    }
}

///Represents what kind of tweets should be included in search results.
#[derive(Debug, Copy, Clone)]
pub enum ResultType {
    ///Return only the most recent tweets in the response.
    Recent,
    ///Return only the most popular tweets in the response.
    Popular,
    ///Include both popular and real-time results in the response.
    Mixed,
}

///Display impl that turns the variants into strings that can be used as search parameters.
impl fmt::Display for ResultType {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            ResultType::Recent => write!(f, "recent"),
            ResultType::Popular => write!(f, "popular"),
            ResultType::Mixed => write!(f, "mixed"),
        }
    }
}

///Represents a radius around a given location to return search results for.
pub enum Distance {
    ///A radius given in miles.
    Miles(f32),
    ///A radius given in kilometers.
    Kilometers(f32),
}

///Represents a tweet search query before being sent.
#[must_use = "SearchBuilder is lazy and won't do anything unless `call`ed"]
pub struct SearchBuilder<'a> {
    ///The text to search for.
    query: &'a str,
    lang: Option<&'a str>,
    result_type: Option<ResultType>,
    count: Option<u32>,
    until: Option<(u32, u32, u32)>,
    geocode: Option<(f32, f32, Distance)>,
    since_id: Option<u64>,
    max_id: Option<u64>,
}

impl<'a> SearchBuilder<'a> {
    ///Restrict search results to those that have been machine-parsed as the given two-letter
    ///language code.
    pub fn lang(self, lang: &'a str) -> Self {
        SearchBuilder {
            lang: Some(lang),
            ..self
        }
    }

    ///Specify the type of search results to include. The default is `Recent`.
    pub fn result_type(self, result_type: ResultType) -> Self {
        SearchBuilder {
            result_type: Some(result_type),
            ..self
        }
    }

    ///Set the number of tweets to return per-page, up to a maximum of 100. The default is 15.
    pub fn count(self, count: u32) -> Self {
        SearchBuilder {
            count: Some(count),
            ..self
        }
    }

    ///Returns tweets created before the given date. Keep in mind that search is limited to the
    ///last 7 days of results, so giving a date here that's older than a week will return no
    ///results.
    pub fn until(self, year: u32, month: u32, day: u32) -> Self {
        SearchBuilder {
            until: Some((year, month, day)),
            ..self
        }
    }

    ///Restricts results to users located within the given radius of the given coordinate. This is
    ///preferably populated from location-tagged tweets, but can be filled in from the user's
    ///profile as a fallback.
    pub fn geocode(self, latitude: f32, longitude: f32, radius: Distance) -> Self {
        SearchBuilder {
            geocode: Some((latitude, longitude, radius)),
            ..self
        }
    }

    ///Restricts results to those with higher IDs than (i.e. that were posted after) the given
    ///tweet ID.
    pub fn since_tweet(self, since_id: u64) -> Self {
        SearchBuilder {
            since_id: Some(since_id),
            ..self
        }
    }

    ///Restricts results to those with IDs no higher than (i.e. were posted earlier than) the given
    ///tweet ID. Will include the given tweet in search results.
    pub fn max_tweet(self, max_id: u64) -> Self {
        SearchBuilder {
            max_id: Some(max_id),
            ..self
        }
    }

    ///Finalize the search terms and return the first page of responses.
    pub fn call(self, token: &auth::Token) -> WebResponse<SearchResult<'a>> {
        let mut params = HashMap::new();

        add_param(&mut params, "q", self.query);

        if let Some(lang) = self.lang {
            add_param(&mut params, "lang", lang);
        }

        if let Some(result_type) = self.result_type {
            add_param(&mut params, "result_type", result_type.to_string());
        }

        if let Some(count) = self.count {
            add_param(&mut params, "count", count.to_string());
        }

        if let Some((year, month, day)) = self.until {
            add_param(&mut params, "until", format!("{}-{}-{}", year, month, day));
        }

        if let Some((lat, lon, radius)) = self.geocode {
            match radius {
                Distance::Miles(r) => add_param(&mut params, "geocode", format!("{:.6},{:.6},{}mi", lat, lon, r)),
                Distance::Kilometers(r) => add_param(&mut params, "geocode", format!("{:.6},{:.6},{}km", lat, lon, r)),
            };
        }

        if let Some(since_id) = self.since_id {
            add_param(&mut params, "since_id", since_id.to_string());
        }

        if let Some(max_id) = self.max_id {
            add_param(&mut params, "max_id", max_id.to_string());
        }

        let mut resp = try!(auth::get(links::statuses::SEARCH, token, Some(&params)));

        let mut ret: Response<SearchResult> = try!(parse_response(&mut resp));
        ret.params = Some(params);
        Ok(ret)
    }
}

///Represents a page of search results, along with metadata to request the next or previous page.
#[derive(Debug)]
pub struct SearchResult<'a> {
    ///The list of statuses in this page of results.
    pub statuses: Vec<Tweet>,
    ///The query used to generate this page of results. Note that changing this will not affect the
    ///`next_page` method.
    pub query: String,
    max_id: u64,
    since_id: u64,
    params: Option<ParamList<'a>>,
}

impl<'a> FromJson for SearchResult<'a> {
    fn from_json(input: &json::Json) -> Result<Self, error::Error> {
        if !input.is_object() {
            return Err(InvalidResponse("SearchResult received json that wasn't an object", Some(input.to_string())));
        }

        let metadata = try!(input.find("search_metadata").ok_or(MissingValue("search_metadata")));

        Ok(SearchResult {
            statuses: try!(field(input, "statuses")),
            query: try!(field(metadata, "query")),
            max_id: try!(field(metadata, "max_id")),
            since_id: try!(field(metadata, "since_id")),
            params: None,
        })
    }
}

impl<'a> SearchResult<'a> {
    ///Load the next page of search results for the same query.
    pub fn older(&self, token: &auth::Token) -> WebResponse<SearchResult> {
        let mut params = self.params.as_ref().cloned().unwrap_or_default();
        params.remove("since_id");

        if let Some(min_id) = self.statuses.iter().map(|t| t.id).min() {
            add_param(&mut params, "max_id", (min_id - 1).to_string());
        }
        else {
            params.remove("max_id");
        }

        let mut resp = try!(auth::get(links::statuses::SEARCH, token, Some(&params)));

        let mut ret: Response<SearchResult> = try!(parse_response(&mut resp));
        ret.params = Some(params);
        Ok(ret)
    }

    ///Load the previous page of search results for the same query.
    pub fn newer(&self, token: &auth::Token) -> WebResponse<SearchResult> {
        let mut params = self.params.as_ref().cloned().unwrap_or_default();
        params.remove("max_id");

        if let Some(max_id) = self.statuses.iter().map(|t| t.id).max() {
            add_param(&mut params, "since_id", max_id.to_string());
        }
        else {
            params.remove("since_id");
        }

        let mut resp = try!(auth::get(links::statuses::SEARCH, token, Some(&params)));

        let mut ret: Response<SearchResult> = try!(parse_response(&mut resp));
        ret.params = Some(params);
        Ok(ret)
    }
}
<commit_msg>Make `max_id` and `since_id` fields public in SearchResults<commit_after>// 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/.

//! Structs and methods for searching for tweets.
//!
//! Since there are several optional parameters for searches, egg-mode handles it with a builder
//! pattern. To begin, call `search` with your requested search term. Additional parameters can be
//! added onto the `SearchBuilder` struct that is returned. When you're ready to load the first
//! page of results, hand your tokens to `call`.
//!
//! ```rust,no_run
//! # let token = egg_mode::Token::Access {
//! #     consumer: egg_mode::KeyPair::new("", ""),
//! #     access: egg_mode::KeyPair::new("", ""),
//! # };
//! use egg_mode::search::{self, ResultType};
//!
//! let search = search::search("rustlang")
//!                     .result_type(ResultType::Recent)
//!                     .call(&token)
//!                     .unwrap();
//!
//! for tweet in &search.statuses {
//!     println!("(@{}) {}", tweet.user.as_ref().unwrap().screen_name, tweet.text);
//! }
//! ```
//!
//! Once you have your `SearchResult`, you can navigate the search results by calling `older` and
//! `newer` to get the next and previous pages, respsectively. In addition, you can see your
//! original query in the search result struct as well, so you can categorize multiple searches by
//! their query. While this is given as a regular field, note that modifying `query` will not
//! change what is searched for when you call `older` or `newer`; the `SearchResult` keeps its
//! search arguments in a separate private field.
//!
//! The search parameter given in the initial call to `search` has several options itself. A full
//! reference is available in [Twitter's Search API documentation][search-doc]. This listing by
//! itself does not include the search by Place ID, as mentioned on [a separate Tweets by Place
//! page][search-place]. A future version of egg-mode might break these options into further
//! methods on `SearchBuilder`.
//!
//! [search-doc]: https://dev.twitter.com/rest/public/search
//! [search-place]: https://dev.twitter.com/rest/public/search-by-place

use std::collections::HashMap;
use std::fmt;

use rustc_serialize::json;

use auth;
use error;
use error::Error::{InvalidResponse, MissingValue};
use links;
use tweet::Tweet;
use common::*;

///Begin setting up a tweet search with the given query.
pub fn search<'a>(query: &'a str) -> SearchBuilder<'a> {
    SearchBuilder {
        query: query,
        lang: None,
        result_type: None,
        count: None,
        until: None,
        geocode: None,
        since_id: None,
        max_id: None,
    }
}

///Represents what kind of tweets should be included in search results.
#[derive(Debug, Copy, Clone)]
pub enum ResultType {
    ///Return only the most recent tweets in the response.
    Recent,
    ///Return only the most popular tweets in the response.
    Popular,
    ///Include both popular and real-time results in the response.
    Mixed,
}

///Display impl that turns the variants into strings that can be used as search parameters.
impl fmt::Display for ResultType {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            ResultType::Recent => write!(f, "recent"),
            ResultType::Popular => write!(f, "popular"),
            ResultType::Mixed => write!(f, "mixed"),
        }
    }
}

///Represents a radius around a given location to return search results for.
pub enum Distance {
    ///A radius given in miles.
    Miles(f32),
    ///A radius given in kilometers.
    Kilometers(f32),
}

///Represents a tweet search query before being sent.
#[must_use = "SearchBuilder is lazy and won't do anything unless `call`ed"]
pub struct SearchBuilder<'a> {
    ///The text to search for.
    query: &'a str,
    lang: Option<&'a str>,
    result_type: Option<ResultType>,
    count: Option<u32>,
    until: Option<(u32, u32, u32)>,
    geocode: Option<(f32, f32, Distance)>,
    since_id: Option<u64>,
    max_id: Option<u64>,
}

impl<'a> SearchBuilder<'a> {
    ///Restrict search results to those that have been machine-parsed as the given two-letter
    ///language code.
    pub fn lang(self, lang: &'a str) -> Self {
        SearchBuilder {
            lang: Some(lang),
            ..self
        }
    }

    ///Specify the type of search results to include. The default is `Recent`.
    pub fn result_type(self, result_type: ResultType) -> Self {
        SearchBuilder {
            result_type: Some(result_type),
            ..self
        }
    }

    ///Set the number of tweets to return per-page, up to a maximum of 100. The default is 15.
    pub fn count(self, count: u32) -> Self {
        SearchBuilder {
            count: Some(count),
            ..self
        }
    }

    ///Returns tweets created before the given date. Keep in mind that search is limited to the
    ///last 7 days of results, so giving a date here that's older than a week will return no
    ///results.
    pub fn until(self, year: u32, month: u32, day: u32) -> Self {
        SearchBuilder {
            until: Some((year, month, day)),
            ..self
        }
    }

    ///Restricts results to users located within the given radius of the given coordinate. This is
    ///preferably populated from location-tagged tweets, but can be filled in from the user's
    ///profile as a fallback.
    pub fn geocode(self, latitude: f32, longitude: f32, radius: Distance) -> Self {
        SearchBuilder {
            geocode: Some((latitude, longitude, radius)),
            ..self
        }
    }

    ///Restricts results to those with higher IDs than (i.e. that were posted after) the given
    ///tweet ID.
    pub fn since_tweet(self, since_id: u64) -> Self {
        SearchBuilder {
            since_id: Some(since_id),
            ..self
        }
    }

    ///Restricts results to those with IDs no higher than (i.e. were posted earlier than) the given
    ///tweet ID. Will include the given tweet in search results.
    pub fn max_tweet(self, max_id: u64) -> Self {
        SearchBuilder {
            max_id: Some(max_id),
            ..self
        }
    }

    ///Finalize the search terms and return the first page of responses.
    pub fn call(self, token: &auth::Token) -> WebResponse<SearchResult<'a>> {
        let mut params = HashMap::new();

        add_param(&mut params, "q", self.query);

        if let Some(lang) = self.lang {
            add_param(&mut params, "lang", lang);
        }

        if let Some(result_type) = self.result_type {
            add_param(&mut params, "result_type", result_type.to_string());
        }

        if let Some(count) = self.count {
            add_param(&mut params, "count", count.to_string());
        }

        if let Some((year, month, day)) = self.until {
            add_param(&mut params, "until", format!("{}-{}-{}", year, month, day));
        }

        if let Some((lat, lon, radius)) = self.geocode {
            match radius {
                Distance::Miles(r) => add_param(&mut params, "geocode", format!("{:.6},{:.6},{}mi", lat, lon, r)),
                Distance::Kilometers(r) => add_param(&mut params, "geocode", format!("{:.6},{:.6},{}km", lat, lon, r)),
            };
        }

        if let Some(since_id) = self.since_id {
            add_param(&mut params, "since_id", since_id.to_string());
        }

        if let Some(max_id) = self.max_id {
            add_param(&mut params, "max_id", max_id.to_string());
        }

        let mut resp = try!(auth::get(links::statuses::SEARCH, token, Some(&params)));

        let mut ret: Response<SearchResult> = try!(parse_response(&mut resp));
        ret.params = Some(params);
        Ok(ret)
    }
}

///Represents a page of search results, along with metadata to request the next or previous page.
#[derive(Debug)]
pub struct SearchResult<'a> {
    ///The list of statuses in this page of results.
    pub statuses: Vec<Tweet>,
    ///The query used to generate this page of results. Note that changing this will not affect the
    ///`next_page` method.
    pub query: String,
    ///Last tweet id in this page of results. This id can be used in `SearchBuilder::since_tweet`
    pub max_id: u64,
    ///First tweet id in this page of results. This id can be used in `SearchBuilder::since_tweet`
    pub since_id: u64,
    params: Option<ParamList<'a>>,
}

impl<'a> FromJson for SearchResult<'a> {
    fn from_json(input: &json::Json) -> Result<Self, error::Error> {
        if !input.is_object() {
            return Err(InvalidResponse("SearchResult received json that wasn't an object", Some(input.to_string())));
        }

        let metadata = try!(input.find("search_metadata").ok_or(MissingValue("search_metadata")));

        Ok(SearchResult {
            statuses: try!(field(input, "statuses")),
            query: try!(field(metadata, "query")),
            max_id: try!(field(metadata, "max_id")),
            since_id: try!(field(metadata, "since_id")),
            params: None,
        })
    }
}

impl<'a> SearchResult<'a> {
    ///Load the next page of search results for the same query.
    pub fn older(&self, token: &auth::Token) -> WebResponse<SearchResult> {
        let mut params = self.params.as_ref().cloned().unwrap_or_default();
        params.remove("since_id");

        if let Some(min_id) = self.statuses.iter().map(|t| t.id).min() {
            add_param(&mut params, "max_id", (min_id - 1).to_string());
        }
        else {
            params.remove("max_id");
        }

        let mut resp = try!(auth::get(links::statuses::SEARCH, token, Some(&params)));

        let mut ret: Response<SearchResult> = try!(parse_response(&mut resp));
        ret.params = Some(params);
        Ok(ret)
    }

    ///Load the previous page of search results for the same query.
    pub fn newer(&self, token: &auth::Token) -> WebResponse<SearchResult> {
        let mut params = self.params.as_ref().cloned().unwrap_or_default();
        params.remove("max_id");

        if let Some(max_id) = self.statuses.iter().map(|t| t.id).max() {
            add_param(&mut params, "since_id", max_id.to_string());
        }
        else {
            params.remove("since_id");
        }

        let mut resp = try!(auth::get(links::statuses::SEARCH, token, Some(&params)));

        let mut ret: Response<SearchResult> = try!(parse_response(&mut resp));
        ret.params = Some(params);
        Ok(ret)
    }
}
<|endoftext|>


<commit_before>use super::{Jira, Result, SearchOptions, SearchResults, Issue};
use url::form_urlencoded;

// search interface
pub struct Search<'a> {
    jira: &'a Jira<'a>,
}

impl<'a> Search<'a> {
    pub fn new(jira: &'a Jira<'a>) -> Search<'a> {
        Search { jira: jira }
    }

    /// https://docs.atlassian.com/jira/REST/latest/#api/2/search
    pub fn list<J>(&self, jql: J, options: &SearchOptions) -> Result<SearchResults>
        where J: Into<String>
    {
        let mut path = vec!["/search".to_owned()];
        let query_options = options.serialize().unwrap_or(String::new());
        let query = form_urlencoded::Serializer::new(query_options)
            .append_pair("jql", &jql.into())
            .finish();
        path.push(query);
        self.jira.get::<SearchResults>(path.join("?").as_ref())
    }

    pub fn iter<J>(&self, jql: J, options: &SearchOptions) -> Result<SearchIter> where J: Into<String> {
        SearchIter::new(jql, options, self.jira)
    }
}

pub struct SearchIter<'a> {
    jira: &'a Jira<'a>,
    jql: String,
    results: SearchResults
}

impl<'a> SearchIter<'a> {
    pub fn new<J>(jql: J, options: &SearchOptions, jira: &'a Jira<'a>) -> Result<SearchIter<'a>> where J: Into<String> {
        let query = jql.into();
        let results = try!(jira.search().list(query.clone(), options));
        Ok(SearchIter {
            jira: jira,
            jql: query,
            results: results
        })
    }

    fn more(&self) -> bool {
        (self.results.start_at + self.results.issues.len() as u64) < self.results.total
    }
}

impl <'a> Iterator for SearchIter<'a> {
    type Item = Issue;
    fn next(&mut self) -> Option<Issue> {
        self.results.issues.pop().or_else(||
            if self.more() {
                match self.jira.search().list(
                    self.jql.clone(),
                    &SearchOptions::builder()
                        .max_results(self.results.max_results)
                        .start_at(self.results.start_at + self.results.max_results)
                        .build()) {
                            Ok(new_results) => {
                                self.results = new_results;
                                self.results.issues.pop()
                            },
                            _ => None
                        }
            } else {
                None
            }
        )
    }
}
<commit_msg>private constructor<commit_after>use super::{Jira, Result, SearchOptions, SearchResults, Issue};
use url::form_urlencoded;

// search interface
pub struct Search<'a> {
    jira: &'a Jira<'a>,
}

impl<'a> Search<'a> {
    pub fn new(jira: &'a Jira<'a>) -> Search<'a> {
        Search { jira: jira }
    }

    /// https://docs.atlassian.com/jira/REST/latest/#api/2/search
    pub fn list<J>(&self, jql: J, options: &SearchOptions) -> Result<SearchResults>
        where J: Into<String>
    {
        let mut path = vec!["/search".to_owned()];
        let query_options = options.serialize().unwrap_or(String::new());
        let query = form_urlencoded::Serializer::new(query_options)
            .append_pair("jql", &jql.into())
            .finish();
        path.push(query);
        self.jira.get::<SearchResults>(path.join("?").as_ref())
    }

    pub fn iter<J>(&self, jql: J, options: &SearchOptions) -> Result<SearchIter> where J: Into<String> {
        SearchIter::new(jql, options, self.jira)
    }
}

pub struct SearchIter<'a> {
    jira: &'a Jira<'a>,
    jql: String,
    results: SearchResults
}

impl<'a> SearchIter<'a> {
    fn new<J>(jql: J, options: &SearchOptions, jira: &'a Jira<'a>) -> Result<SearchIter<'a>> where J: Into<String> {
        let query = jql.into();
        let results = try!(jira.search().list(query.clone(), options));
        Ok(SearchIter {
            jira: jira,
            jql: query,
            results: results
        })
    }

    fn more(&self) -> bool {
        (self.results.start_at + self.results.issues.len() as u64) < self.results.total
    }
}

impl <'a> Iterator for SearchIter<'a> {
    type Item = Issue;
    fn next(&mut self) -> Option<Issue> {
        self.results.issues.pop().or_else(||
            if self.more() {
                match self.jira.search().list(
                    self.jql.clone(),
                    &SearchOptions::builder()
                        .max_results(self.results.max_results)
                        .start_at(self.results.start_at + self.results.max_results)
                        .build()) {
                            Ok(new_results) => {
                                self.results = new_results;
                                self.results.issues.pop()
                            },
                            _ => None
                        }
            } else {
                None
            }
        )
    }
}
<|endoftext|>


<commit_before>pub trait StringUtil {
    fn between(&self, a: &str, b: &str) -> Result<String, String>;
    fn after(&self, a: &str) -> Result<String, String>;
}

impl StringUtil for str {
    fn between(&self, delim_a: &str, delim_b: &str) -> Result<String, String> {
        let start_idx =
            self
            .find(delim_a)
            .ok_or(format!("between(): start string \"{}\" not found", delim_a))?;

        let start_str = &self[start_idx + delim_a.len()..];

        let end =
            start_str
            .find(delim_b)
            .ok_or(format!("between(): end string \"{}\" not found", delim_b))?;

        Ok(start_str[..end].to_string())
    }

    fn after(&self, delim: &str) -> Result<String, String> {
        self
        .find(delim)
        .map(|i| self[i + delim.len()..].to_string())
        .ok_or(format!("after(): string delimiter \"{}\" not found", delim))
    }
}<commit_msg>Update trait parameter names<commit_after>pub trait StringUtil {
    fn between(&self, delim_a: &str, delim_b: &str) -> Result<String, String>;
    fn after(&self, delim: &str) -> Result<String, String>;
}

impl StringUtil for str {
    fn between(&self, delim_a: &str, delim_b: &str) -> Result<String, String> {
        let start_idx =
            self
            .find(delim_a)
            .ok_or(format!("between(): start string \"{}\" not found", delim_a))?;

        let start_str = &self[start_idx + delim_a.len()..];

        let end =
            start_str
            .find(delim_b)
            .ok_or(format!("between(): end string \"{}\" not found", delim_b))?;

        Ok(start_str[..end].to_string())
    }

    fn after(&self, delim: &str) -> Result<String, String> {
        self
        .find(delim)
        .map(|i| self[i + delim.len()..].to_string())
        .ok_or(format!("after(): string delimiter \"{}\" not found", delim))
    }
}<|endoftext|>