<commit_before>#[macro_use]
extern crate log;

#[cfg(windows)]
extern crate pdcurses;
#[cfg(unix)]
extern crate ncurses;

use std::ffi::CString;

#[cfg(windows)]
use pdcurses as curses;
#[cfg(windows)]
pub use pdcurses::chtype;
#[cfg(unix)]
use ncurses::ll as curses;
#[cfg(unix)]
pub use ncurses::ll::chtype;

mod input;
pub use self::input::*;

#[cfg(windows)]
mod windows;
#[cfg(windows)]
pub use self::windows::*;
#[cfg(unix)]
mod unix;
#[cfg(unix)]
pub use self::unix::*;

pub const OK: i32 = 0;
pub const ERR: i32 = -1;

#[derive(Copy, Clone, Debug)]
pub struct Window {
    #[cfg(windows)]
    _window: *mut curses::WINDOW,
    #[cfg(unix)]
    _window: curses::WINDOW,
}

impl Window {
    /// Sets the current attributes of the given window to attributes.
    pub fn attrset(&self, attributes: chtype) -> i32 {
        _attrset(self._window, attributes)
    }

    /// Not only change the background, but apply it immediately to every cell in the window.
    pub fn bkgd(&self, ch: chtype) -> i32 {
        unsafe { curses::wbkgd(self._window, ch) }
    }

    /// The same as subwin(), except that begy and begx are relative to the origin of the window
    /// rather than the screen.
    ///
    /// There is no difference between subwindows and derived windows.
    pub fn derwin(&self, nlines: i32, ncols: i32, begy: i32, begx: i32) -> Result<Window, i32> {
        self.subwin(nlines, ncols, begy + self.get_beg_y(), begx + self.get_beg_x())
    }

    pub fn draw_box(&self, verch: chtype, horch: chtype) -> i32 {
        _draw_box(self._window, verch, horch)
    }

    /// Copies blanks (i.e. the background chtype) to every cell of the window.
    pub fn erase(&self) -> i32 {
        unsafe { curses::werase(self._window) }
    }

    /// Get the upper-left y coordinate of this window
    pub fn get_beg_y(&self) -> i32 {
        unsafe { curses::getbegy(self._window) }
    }

    // Get the upper-left x coordinate of this window
    pub fn get_beg_x(&self) -> i32 {
        unsafe { curses::getbegx(self._window) }
    }

    /// Get the upper-left y and x coordinates of this window
    pub fn get_beg_yx(&self) -> (i32, i32) {
        (self.get_beg_y(), self.get_beg_x())
    }

    /// Read a character from the terminal associated with the window.
    ///
    /// In nodelay mode, if there is no input waiting, None is returned. In delay mode,
    /// the program will hang until the system  passes text through to the program. Depending on
    /// the setting of cbreak(), this will be after one character or after the first newline.
    /// Unless noecho() has been set, the character will also be echoed into the designated window.
    ///
    /// If keypad() is TRUE, and a function key is pressed, the token for that function key will be
    /// returned instead of the raw characters.
    /// If nodelay(win, TRUE) has been called on the window and no input is waiting, None is
    /// returned.
    pub fn getch(&self) -> Option<Input> {
        let i = unsafe { curses::wgetch(self._window) };
        if i < 0 {
            None
        } else if i <= u8::max_value() as i32 {
            Some(Input::Character(i as u8 as char))
        } else {
            Some(to_special_keycode(i))
        }
    }

    /// Return the maximum x value of this Window, in other words the number of columns.
    pub fn get_max_x(&self) -> i32 {
        unsafe { curses::getmaxx(self._window) }
    }

    /// Return the maximum y value of this Window, in other words the number of rows.
    pub fn get_max_y(&self) -> i32 {
        unsafe { curses::getmaxy(self._window) }
    }

    /// Return the maximum y and x value of this Window
    pub fn get_max_yx(&self) -> (i32, i32) {
        (self.get_max_y(), self.get_max_x())
    }

    /// Controls whether getch() returns function/special keys as single key codes (e.g., the left
    /// arrow key as KEY_LEFT).
    ///
    /// Per X/Open, the default for keypad mode is OFF. You'll probably want it on. With keypad
    /// mode off, if a special key is pressed, getch() does nothing or returns ERR.
    pub fn keypad(&self, use_keypad: bool) -> i32 {
        unsafe { curses::keypad(self._window, use_keypad as u8) }
    }

    /// The cursor associated with the window is moved to the given location.
    ///
    /// This does not move the physical cursor of the terminal until refresh() is called.  The
    /// position specified is relative to the upper left corner of the window, which is (0,0).
    pub fn mv(&self, y: i32, x: i32) -> i32 {
        unsafe { curses::wmove(self._window, y, x) }
    }

    /// moves the cursor to the specified position and adds ch to the specified window
    pub fn mvaddch(&self, y: i32, x: i32, ch: char) -> i32 {
        unsafe { curses::mvwaddch(self._window, y, x, ch as chtype) }
    }

    /// Write all the characters of the string str to the given window. The functionality is
    /// similar to calling waddch() once for each character in the string.
    pub fn mvaddstr(&self, y: i32, x: i32, string: &str) -> i32 {
        let s = CString::new(string).unwrap();
        unsafe { curses::mvwaddstr(self._window, y, x, s.as_ptr()) }
    }

    /// Controls whether wgetch() is a non-blocking call. If the option is enabled, and
    /// no input is ready, wgetch() will return ERR. If disabled, wgetch() will hang until input is
    /// ready.
    pub fn nodelay(&self, enabled: bool) -> i32 {
        unsafe { curses::nodelay(self._window, enabled as u8) as i32 }
    }

    ///Add a string to the window at the current cursor position.
    pub fn printw(&self, string: &str) -> i32 {
        let s = CString::new(string).unwrap();
        unsafe { curses::wprintw(self._window, s.as_ptr()) }
    }

    /// Copies the named window to the physical terminal screen, taking into account what
    /// is already there in order to optimize cursor movement.
    ///
    /// This function must be called to get any output on the terminal, as other routines only
    /// manipulate data structures. Unless leaveok() has been enabled, the physical cursor of the
    /// terminal is left at the location of the window's cursor.
    pub fn refresh(&self) -> i32 {
        unsafe { curses::wrefresh(self._window) }
    }

    /// Creates a new subwindow within a window.
    ///
    /// The dimensions of the subwindow are nlines lines and ncols columns. The subwindow is at
    /// position (begy, begx) on the screen. This position is relative to the screen, and not to
    /// the window orig. Changes made to either window will affect both. When using this routine,
    /// you will often need to call touchwin() before calling wrefresh().
    pub fn subwin(&self, nlines: i32, ncols: i32, begy: i32, begx: i32) -> Result<Window, i32> {
        let new_window = unsafe { curses::subwin(self._window, nlines, ncols, begy, begx) };
        if new_window.is_null() {
            Err(ERR)
        } else {
            Ok(Window { _window: new_window })
        }
    }

    /// Set blocking or non-blocking reads for the specified window.
    ///
    /// The delay is measured in milliseconds. If it's negative, a blocking read is used; if zero,
    /// then non-blocking reads are done -- if no input is waiting, ERR is returned immediately.
    /// If the delay is positive, the read blocks for the delay period; if the period expires,
    /// ERR is returned.
    pub fn timeout(&self, milliseconds: i32) {
        unsafe { curses::wtimeout(self._window, milliseconds) }
    }
}

/// Set cbreak mode.
///
/// In cbreak mode, characters typed by the user are made available immediately, and erase/kill
/// character processing is not performed.  In nocbreak mode, typed characters are buffered until
/// a newline or carriage return. Interrupt and flow control characters are unaffected by this
/// mode.
pub fn cbreak() -> i32 {
    unsafe { curses::cbreak() }
}

/// Alters the appearance of the cursor.
///
///  A visibility of 0 makes it disappear; 1 makes it appear "normal" (usually an underline) and 2
/// makes it "highly visible" (usually a block).
pub fn curs_set(visibility: i32) -> i32 {
    unsafe { curses::curs_set(visibility) }
}

/// Should be called before exiting or escaping from curses mode temporarily.
///
/// It will restore tty modes, move the cursor to the lower left corner of the screen and reset the
/// terminal into the proper non-visual mode.  To resume curses after a temporary escape, call
/// refresh() or doupdate().
pub fn endwin() -> i32 {
    unsafe { curses::endwin() }
}

/// Indicates if the terminal supports, and can maniplulate color.
pub fn has_colors() -> bool {
    unsafe { curses::has_colors() > 0 }
}

/// Initialize the curses system, this must be the first function that is called.
///
/// Returns a Window struct that is used to access Window specific functions.
pub fn initscr() -> Window {
    let window_pointer = unsafe { curses::initscr() };
    Window { _window: window_pointer }
}

/// Changes the definition of a color-pair.
///
/// It takes three arguments: the number of the color-pair to be redefined, and the new values of
/// the foreground and background colors. The pair number must be between 0 and COLOR_PAIRS - 1,
/// inclusive. The foreground and background must be between 0 and COLORS - 1, inclusive. If the
/// color pair was previously initialized, the screen is refreshed, and all occurrences of that
/// color-pair are changed to the new definition.
pub fn init_pair(pair_index: i16, foreground_color: i16, background_color: i16) -> i32 {
    unsafe { curses::init_pair(pair_index, foreground_color, background_color) as i32 }
}

/// Suspends the program for the specified number of milliseconds.
pub fn napms(ms: i32) -> i32 {
    unsafe { curses::napms(ms) }
}

/// Creates a new window with the given number of lines, nlines and columns, ncols.
///
/// The upper left corner of the window is at line begy, column begx. If nlines is zero, it
/// defaults to LINES - begy; ncols to COLS - begx. Create a new full-screen window by calling
/// newwin(0, 0, 0, 0).
pub fn newwin(nlines: i32, ncols: i32, begy: i32, begx: i32) -> Window {
    let window_pointer = unsafe { curses::newwin(nlines, ncols, begy, begx) };
    Window { _window: window_pointer }
}

/// Enables the translation of a carriage return into a newline on input.
///
/// nonl() disables this. Initially, the translation does occur.
pub fn nl() -> i32 {
    unsafe { curses::nl() }
}

/// Disables echoing typed characters.
///
/// Initially, input characters are echoed. Subsequent calls to echo() and noecho() do not flush
/// type-ahead.
pub fn noecho() -> i32 {
    unsafe { curses::noecho() }
}

/// Attempts to resize the screen to the given size.
///
/// resize_term() is effectively two functions: When called with nonzero values for nlines and
/// ncols, it attempts to resize the screen to the given size. When called with (0, 0), it merely
/// adjusts the internal structures to match the current size after the screen is resized by the
/// user. If you want to support user resizing, you should check for getch() returning KEY_RESIZE,
/// and/or call is_termresized() at appropriate times; if either condition occurs, call
/// resize_term(0, 0). Then, with either user or programmatic resizing, you'll have to resize any
/// windows you've created.
pub fn resize_term(nlines: i32, ncols: i32) -> i32 {
    _resize_term(nlines, ncols)
}

/// Initializes eight basic colors (black, red, green, yellow, blue, magenta, cyan,
/// and white), and two global variables; COLORS and COLOR_PAIRS (respectively defining the
/// maximum number of colors and color-pairs the terminal is capable of displaying).
pub fn start_color() -> i32 {
    unsafe { curses::start_color() as i32 }
}

/// Allows the use of -1 as a foreground or background color with init_pair().
///
/// Calls assume_default_colors(-1, -1); -1 represents the foreground or background color that
/// the terminal had at startup.
pub fn use_default_colors() -> i32 {
    unsafe { curses::use_default_colors() }
}
<commit_msg>Implemented delwin(), half_delay() and nocbreak()<commit_after>#[macro_use]
extern crate log;

#[cfg(windows)]
extern crate pdcurses;
#[cfg(unix)]
extern crate ncurses;

use std::ffi::CString;

#[cfg(windows)]
use pdcurses as curses;
#[cfg(windows)]
pub use pdcurses::chtype;
#[cfg(unix)]
use ncurses::ll as curses;
#[cfg(unix)]
pub use ncurses::ll::chtype;

mod input;
pub use self::input::*;

#[cfg(windows)]
mod windows;
#[cfg(windows)]
pub use self::windows::*;
#[cfg(unix)]
mod unix;
#[cfg(unix)]
pub use self::unix::*;

pub const OK: i32 = 0;
pub const ERR: i32 = -1;

#[derive(Debug)]
pub struct Window {
    #[cfg(windows)]
    _window: *mut curses::WINDOW,
    #[cfg(unix)]
    _window: curses::WINDOW,
}

impl Window {
    /// Sets the current attributes of the given window to attributes.
    pub fn attrset(&self, attributes: chtype) -> i32 {
        _attrset(self._window, attributes)
    }

    /// Not only change the background, but apply it immediately to every cell in the window.
    pub fn bkgd(&self, ch: chtype) -> i32 {
        unsafe { curses::wbkgd(self._window, ch) }
    }

    /// The same as subwin(), except that begy and begx are relative to the origin of the window
    /// rather than the screen.
    ///
    /// There is no difference between subwindows and derived windows.
    pub fn derwin(&self, nlines: i32, ncols: i32, begy: i32, begx: i32) -> Result<Window, i32> {
        self.subwin(nlines, ncols, begy + self.get_beg_y(), begx + self.get_beg_x())
    }

    pub fn draw_box(&self, verch: chtype, horch: chtype) -> i32 {
        _draw_box(self._window, verch, horch)
    }

    /// Copies blanks (i.e. the background chtype) to every cell of the window.
    pub fn erase(&self) -> i32 {
        unsafe { curses::werase(self._window) }
    }

    /// Get the upper-left y coordinate of this window
    pub fn get_beg_y(&self) -> i32 {
        unsafe { curses::getbegy(self._window) }
    }

    // Get the upper-left x coordinate of this window
    pub fn get_beg_x(&self) -> i32 {
        unsafe { curses::getbegx(self._window) }
    }

    /// Get the upper-left y and x coordinates of this window
    pub fn get_beg_yx(&self) -> (i32, i32) {
        (self.get_beg_y(), self.get_beg_x())
    }

    /// Read a character from the terminal associated with the window.
    ///
    /// In nodelay mode, if there is no input waiting, None is returned. In delay mode,
    /// the program will hang until the system  passes text through to the program. Depending on
    /// the setting of cbreak(), this will be after one character or after the first newline.
    /// Unless noecho() has been set, the character will also be echoed into the designated window.
    ///
    /// If keypad() is TRUE, and a function key is pressed, the token for that function key will be
    /// returned instead of the raw characters.
    /// If nodelay(win, TRUE) has been called on the window and no input is waiting, None is
    /// returned.
    pub fn getch(&self) -> Option<Input> {
        let i = unsafe { curses::wgetch(self._window) };
        if i < 0 {
            None
        } else if i <= u8::max_value() as i32 {
            Some(Input::Character(i as u8 as char))
        } else {
            Some(to_special_keycode(i))
        }
    }

    /// Return the maximum x value of this Window, in other words the number of columns.
    pub fn get_max_x(&self) -> i32 {
        unsafe { curses::getmaxx(self._window) }
    }

    /// Return the maximum y value of this Window, in other words the number of rows.
    pub fn get_max_y(&self) -> i32 {
        unsafe { curses::getmaxy(self._window) }
    }

    /// Return the maximum y and x value of this Window
    pub fn get_max_yx(&self) -> (i32, i32) {
        (self.get_max_y(), self.get_max_x())
    }

    /// Controls whether getch() returns function/special keys as single key codes (e.g., the left
    /// arrow key as KEY_LEFT).
    ///
    /// Per X/Open, the default for keypad mode is OFF. You'll probably want it on. With keypad
    /// mode off, if a special key is pressed, getch() does nothing or returns ERR.
    pub fn keypad(&self, use_keypad: bool) -> i32 {
        unsafe { curses::keypad(self._window, use_keypad as u8) }
    }

    /// The cursor associated with the window is moved to the given location.
    ///
    /// This does not move the physical cursor of the terminal until refresh() is called.  The
    /// position specified is relative to the upper left corner of the window, which is (0,0).
    pub fn mv(&self, y: i32, x: i32) -> i32 {
        unsafe { curses::wmove(self._window, y, x) }
    }

    /// moves the cursor to the specified position and adds ch to the specified window
    pub fn mvaddch(&self, y: i32, x: i32, ch: char) -> i32 {
        unsafe { curses::mvwaddch(self._window, y, x, ch as chtype) }
    }

    /// Write all the characters of the string str to the given window. The functionality is
    /// similar to calling waddch() once for each character in the string.
    pub fn mvaddstr(&self, y: i32, x: i32, string: &str) -> i32 {
        let s = CString::new(string).unwrap();
        unsafe { curses::mvwaddstr(self._window, y, x, s.as_ptr()) }
    }

    /// Controls whether wgetch() is a non-blocking call. If the option is enabled, and
    /// no input is ready, wgetch() will return ERR. If disabled, wgetch() will hang until input is
    /// ready.
    pub fn nodelay(&self, enabled: bool) -> i32 {
        unsafe { curses::nodelay(self._window, enabled as u8) as i32 }
    }

    ///Add a string to the window at the current cursor position.
    pub fn printw(&self, string: &str) -> i32 {
        let s = CString::new(string).unwrap();
        unsafe { curses::wprintw(self._window, s.as_ptr()) }
    }

    /// Copies the named window to the physical terminal screen, taking into account what
    /// is already there in order to optimize cursor movement.
    ///
    /// This function must be called to get any output on the terminal, as other routines only
    /// manipulate data structures. Unless leaveok() has been enabled, the physical cursor of the
    /// terminal is left at the location of the window's cursor.
    pub fn refresh(&self) -> i32 {
        unsafe { curses::wrefresh(self._window) }
    }

    /// Creates a new subwindow within a window.
    ///
    /// The dimensions of the subwindow are nlines lines and ncols columns. The subwindow is at
    /// position (begy, begx) on the screen. This position is relative to the screen, and not to
    /// the window orig. Changes made to either window will affect both. When using this routine,
    /// you will often need to call touchwin() before calling wrefresh().
    pub fn subwin(&self, nlines: i32, ncols: i32, begy: i32, begx: i32) -> Result<Window, i32> {
        let new_window = unsafe { curses::subwin(self._window, nlines, ncols, begy, begx) };
        if new_window.is_null() {
            Err(ERR)
        } else {
            Ok(Window { _window: new_window })
        }
    }

    /// Set blocking or non-blocking reads for the specified window.
    ///
    /// The delay is measured in milliseconds. If it's negative, a blocking read is used; if zero,
    /// then non-blocking reads are done -- if no input is waiting, ERR is returned immediately.
    /// If the delay is positive, the read blocks for the delay period; if the period expires,
    /// ERR is returned.
    pub fn timeout(&self, milliseconds: i32) {
        unsafe { curses::wtimeout(self._window, milliseconds) }
    }
}

/// Set cbreak mode.
///
/// In cbreak mode, characters typed by the user are made available immediately, and erase/kill
/// character processing is not performed.  In nocbreak mode, typed characters are buffered until
/// a newline or carriage return. Interrupt and flow control characters are unaffected by this
/// mode.
pub fn cbreak() -> i32 {
    unsafe { curses::cbreak() }
}

/// Alters the appearance of the cursor.
///
///  A visibility of 0 makes it disappear; 1 makes it appear "normal" (usually an underline) and 2
/// makes it "highly visible" (usually a block).
pub fn curs_set(visibility: i32) -> i32 {
    unsafe { curses::curs_set(visibility) }
}

/// Deletes the window, freeing all associated memory. In the case of overlapping windows,
/// subwindows should be deleted before the main window.
pub fn delwin(window: Window) -> i32 {
    unsafe { curses::delwin(window._window) }
}

/// Should be called before exiting or escaping from curses mode temporarily.
///
/// It will restore tty modes, move the cursor to the lower left corner of the screen and reset the
/// terminal into the proper non-visual mode.  To resume curses after a temporary escape, call
/// refresh() or doupdate().
pub fn endwin() -> i32 {
    unsafe { curses::endwin() }
}

/// Similar to cbreak(), but allows for a time limit to be specified, in tenths of a second.
///
/// This causes getch() to block for that period before returning None if no key has been received.
/// tenths must be between 1 and 255.
pub fn half_delay(tenths: i32) -> i32 {
    unsafe { curses::halfdelay(tenths) }
}

/// Indicates if the terminal supports, and can maniplulate color.
pub fn has_colors() -> bool {
    unsafe { curses::has_colors() > 0 }
}

/// Initialize the curses system, this must be the first function that is called.
///
/// Returns a Window struct that is used to access Window specific functions.
pub fn initscr() -> Window {
    let window_pointer = unsafe { curses::initscr() };
    Window { _window: window_pointer }
}

/// Changes the definition of a color-pair.
///
/// It takes three arguments: the number of the color-pair to be redefined, and the new values of
/// the foreground and background colors. The pair number must be between 0 and COLOR_PAIRS - 1,
/// inclusive. The foreground and background must be between 0 and COLORS - 1, inclusive. If the
/// color pair was previously initialized, the screen is refreshed, and all occurrences of that
/// color-pair are changed to the new definition.
pub fn init_pair(pair_index: i16, foreground_color: i16, background_color: i16) -> i32 {
    unsafe { curses::init_pair(pair_index, foreground_color, background_color) as i32 }
}

/// Suspends the program for the specified number of milliseconds.
pub fn napms(ms: i32) -> i32 {
    unsafe { curses::napms(ms) }
}

/// Creates a new window with the given number of lines, nlines and columns, ncols.
///
/// The upper left corner of the window is at line begy, column begx. If nlines is zero, it
/// defaults to LINES - begy; ncols to COLS - begx. Create a new full-screen window by calling
/// newwin(0, 0, 0, 0).
pub fn newwin(nlines: i32, ncols: i32, begy: i32, begx: i32) -> Window {
    let window_pointer = unsafe { curses::newwin(nlines, ncols, begy, begx) };
    Window { _window: window_pointer }
}

/// Enables the translation of a carriage return into a newline on input.
///
/// nonl() disables this. Initially, the translation does occur.
pub fn nl() -> i32 {
    unsafe { curses::nl() }
}

/// Set nocbreak mode.
///
/// In cbreak mode, characters typed by the user are made available immediately, and erase/kill
/// character processing is not performed.  In nocbreak mode, typed characters are buffered until
/// a newline or carriage return. Interrupt and flow control characters are unaffected by this
/// mode.
pub fn nocbreak() -> i32 {
    unsafe { curses::nocbreak() }
}

/// Disables echoing typed characters.
///
/// Initially, input characters are echoed. Subsequent calls to echo() and noecho() do not flush
/// type-ahead.
pub fn noecho() -> i32 {
    unsafe { curses::noecho() }
}

/// Attempts to resize the screen to the given size.
///
/// resize_term() is effectively two functions: When called with nonzero values for nlines and
/// ncols, it attempts to resize the screen to the given size. When called with (0, 0), it merely
/// adjusts the internal structures to match the current size after the screen is resized by the
/// user. If you want to support user resizing, you should check for getch() returning KEY_RESIZE,
/// and/or call is_termresized() at appropriate times; if either condition occurs, call
/// resize_term(0, 0). Then, with either user or programmatic resizing, you'll have to resize any
/// windows you've created.
pub fn resize_term(nlines: i32, ncols: i32) -> i32 {
    _resize_term(nlines, ncols)
}

/// Initializes eight basic colors (black, red, green, yellow, blue, magenta, cyan,
/// and white), and two global variables; COLORS and COLOR_PAIRS (respectively defining the
/// maximum number of colors and color-pairs the terminal is capable of displaying).
pub fn start_color() -> i32 {
    unsafe { curses::start_color() as i32 }
}

/// Allows the use of -1 as a foreground or background color with init_pair().
///
/// Calls assume_default_colors(-1, -1); -1 represents the foreground or background color that
/// the terminal had at startup.
pub fn use_default_colors() -> i32 {
    unsafe { curses::use_default_colors() }
}
<|endoftext|>


<commit_before>//! # Russet
//!
//! __Russet__ is a simple string tokenising library for Rust.  It supports
//! POSIX shell-style separation of a line of _words_ into a vector of
//! strings.
#![experimental]

use std::char::is_whitespace;

/// A tokeniser object.
///
/// A Tokeniser can be fed characters from an iterator, string, or individually.
/// It is an _immutable_ object: actions on a Tokeniser consume the Tokeniser,
/// and produce a fresh copy of the Tokeniser.
///
/// At any stage, a Tokeniser can be consumed to produce the vector of words
/// it has read, using the `into_strings` method.  This method may fail if the
/// Tokeniser ended in a bad state (in the middle of a quoted string, or in
/// the middle of an escape sequence).
#[deriving(Clone)]
pub struct Tokeniser<Q, E> {
    /// The current vector of parsed words.
    vec: Vec<String>,

    /// Whether or not we are currently in a word.
    in_word: bool,

    /// The current closing quote character, if any.
    quote: Option<char>,

    /// Whether the tokeniser is currently processing an escape character.
    escaping: bool,

    /// Maps from quote openers to quote closers.
    quote_pairs: Q,

    /// Map from escape characters to their replacements.
    escape_pairs: E,

    /// The character preceding escape characters.
    escape_leader: Option<char>
}

/// A tokeniser error.
///
/// A Tokeniser's `into_strings` method can fail with one of the following
/// errors if called while the Tokeniser is in an unfinished state.
#[deriving(Eq, PartialEq, Show)]
pub enum Error {
    /// A quotation was opened, but not closed.
    UnmatchedQuote,

    /// An escape sequence was started, but not finished.
    UnfinishedEscape
}

impl<Q: Map<char, char>+Clone, E: Map<char, char>+Clone> Tokeniser<Q, E> {
    /// Creates a new, blank Tokeniser.
    ///
    /// # Arguments
    ///
    /// * `quote_pairs`   - A map, mapping characters that serve as opening
    ///                     quotes to their closing quotes.
    /// * `escape_pairs`  - A map, mapping escape characters to the characters
    ///                     they represent.
    /// * `escape_leader` - The character, if any, that starts an escape
    ///                     sequence.
    ///
    /// # Return value
    ///
    /// A new Tokeniser, with an empty state.  Attempting to take the
    /// string vector of the Tokeniser yields the empty vector.
    ///
    /// # Example
    ///
    /// ```rust
    /// use std::collections::hashmap::HashMap;
    /// use russet::Tokeniser;
    ///
    /// let quote_pairs: HashMap<char, char> =
    ///     vec![ ( '\"', '\"' ), ( '\'', '\'' ) ].move_iter().collect();
    /// let escape_pairs: HashMap<char, char> =
    ///     vec![ ( 'n', '\n' ) ].move_iter().collect();
    /// let tok = Tokeniser::new(quote_pairs, escape_pairs, Some('\\'));
    /// assert_eq!(tok.into_strings(), Ok(vec![]));
    /// ```
    pub fn new(quote_pairs: Q, escape_pairs: E, escape_leader: Option<char>)
               -> Tokeniser<Q, E> {
        Tokeniser {
            vec: vec![ String::new() ],
            in_word: false,
            quote: None,
            escaping: false,
            quote_pairs: quote_pairs,
            escape_pairs: escape_pairs,
            escape_leader: escape_leader
        }
    }

    /// Feeds a single character `chr` to a Tokeniser.
    ///
    /// # Return value
    ///
    /// A new Tokeniser, representing the state of the Tokeniser after
    /// consuming `chr`.
    ///
    /// # Example
    ///
    /// ```rust
    /// use russet::Tokeniser;
    /// use std::collections::hashmap::HashMap;
    ///
    /// let quote_pairs: HashMap<char, char> =
    ///     vec![ ( '\"', '\"' ), ( '\'', '\'' ) ].move_iter().collect();
    /// let escape_pairs: HashMap<char, char> =
    ///     vec![ ( 'n', '\n' ) ].move_iter().collect();
    /// let tok = Tokeniser::new(quote_pairs, escape_pairs, Some('\\'));
    /// let tok2 = tok.add_char('a').add_char('b').add_char('c');
    /// assert_eq!(tok2.into_strings(), Ok(vec![ "abc".into_string() ]));
    /// ```
    pub fn add_char(self, chr: char) -> Tokeniser<Q, E> {
        let mut new = self.clone();

        match (chr, self) {
            // Escape character while not escaped
            // -> Begin escape (and word if not in one already)
            ( '\\', Tokeniser { escaping: false, .. } ) => {
                new.escaping = true;
                new.in_word = true;
            },
            // Unescaped quote opening character, not currently in quoted word
            // -> Start quoting
            ( q, Tokeniser {
                escaping: false,
                quote: None,
                quote_pairs: ref qs,
                ..
            } ) if qs.contains_key(&q) => {
                new.quote = Some(qs.find(&q).unwrap().clone());
                new.in_word = true;
            },
            // Unescaped quote closing character, in quoted word, quotes ok
            // -> Stop quoting
            ( c, Tokeniser { escaping: false, quote: Some(cc), .. } )
                if c == cc => {
                new.quote = None;
                new.in_word = true;
            },
            // Unescaped whitespace, while not in a word
            // -> Ignore
            ( a, Tokeniser { escaping: false, in_word: false, .. } )
                if is_whitespace(a) => (),
            // Unescaped whitespace, while in a non-quoted word
            // -> End word
            ( a, Tokeniser { escaping: false, in_word: true, quote: None, .. } )
                if is_whitespace(a) => {
                new.in_word = false;
                new.vec.push(String::new());
            },
            // Known escaped character
            // -> Escape character
            ( e, Tokeniser { escaping: true, escape_pairs: ref es, .. } )
                if es.contains_key(&e) => {
                let x = es.find(&e).unwrap();

                new.in_word = true;
                new.escaping = false;
                new.vec.mut_last().mutate(|s| { s.push_char(x.clone()); s });
            },
            // Anything else
            // -> Echo
            ( a, _ ) => {
                new.in_word   = true;
                new.escaping = false;
                new.vec.mut_last().mutate(|s| { s.push_char(a); s });
            }
        }

        new
    }

    /// Destroys the tokeniser, extracting the string vector.
    ///
    /// # Return value
    ///
    /// A Result, containing the tokenised string vector if the Tokeniser
    /// was in a valid ending state, and an Error otherwise.
    pub fn into_strings(mut self) -> Result<Vec<String>, Error> {
        if self.in_word && self.quote.is_some() {
            Err(UnmatchedQuote)
        } else if self.escaping {
            Err(UnfinishedEscape)
        } else {
            if self.vec.last().map(|s| s.len() == 0).unwrap_or(false) {
                self.vec.pop();
            }

            Ok(self.vec)
        }
    }
}

/// Unpacks a line into its constituent words.
#[experimental]
pub fn unpack(line: &str) -> Result<Vec<String>, Error> {
    use std::collections::hashmap::HashMap;

    let quote_pairs: HashMap<char, char> =
        vec![ ( '\"', '\"' ), ( '\'', '\'' ) ].move_iter().collect();
    let escape_pairs: HashMap<char, char> =
        vec![ ( 'n', '\n' ) ].move_iter().collect();

    line.trim().chars().fold(
        Tokeniser::new(quote_pairs, escape_pairs, Some('\\')),
        |s, chr| s.add_char(chr)
    ).into_strings()
}

#[cfg(test)]
mod test {
    use super::{
        unpack,
        UnmatchedQuote,
        UnfinishedEscape
    };

    #[test]
    fn unpack_unmatched_quote() {
        assert_eq!(unpack("\"abcde"), Err(UnmatchedQuote));
    }

    #[test]
    fn unpack_unfinished_escape() {
        assert_eq!(unpack("zxcvbn m\\"), Err(UnfinishedEscape));
    }

    #[test]
    fn unpack_empty_string() {
        assert_eq!(unpack(""), Ok(vec![]));
    }

    #[test]
    fn unpack_leading_whitespace() {
        let rhs = vec![ "abc".into_string(), "def".into_string() ];
        assert_eq!(unpack("     abc def"), Ok(rhs));
    }

    #[test]
    fn unpack_trailing_whitespace() {
        let rhs = vec![ "ghi".into_string(), "jkl".into_string() ];
        assert_eq!(unpack("ghi jkl     \n"), Ok(rhs));
    }

    #[test]
    fn unpack_enqueue_command() {
        let lhs =
            "enqueue file \"C:\\\\Users\\\\Test\\\\Artist - Title.mp3\" 1";
        let rhs = vec![ "enqueue".into_string(),
                        "file".into_string(),
                        "C:\\Users\\Test\\Artist - Title.mp3".into_string(),
                        "1".into_string() ];
        assert_eq!(unpack(lhs), Ok(rhs));
    }

    #[test]
    fn unpack_escaped_newline() {
        assert_eq!(unpack("abc\\nde"),     Ok(vec![ "abc\nde".into_string() ]));
        assert_eq!(unpack("\"abc\\nde\""), Ok(vec![ "abc\nde".into_string() ]));
    }
}
<commit_msg>Add add_line and simple whitespace-only tokeniser.<commit_after>//! # Russet
//!
//! __Russet__ is a simple string tokenising library for Rust.  It supports
//! POSIX shell-style separation of a line of _words_ into a vector of
//! strings.
#![experimental]

use std::char::is_whitespace;
use std::collections::hashmap::HashMap;

/// A tokeniser object.
///
/// A Tokeniser can be fed characters from an iterator, string, or individually.
/// It is an _immutable_ object: actions on a Tokeniser consume the Tokeniser,
/// and produce a fresh copy of the Tokeniser.
///
/// At any stage, a Tokeniser can be consumed to produce the vector of words
/// it has read, using the `into_strings` method.  This method may fail if the
/// Tokeniser ended in a bad state (in the middle of a quoted string, or in
/// the middle of an escape sequence).
#[deriving(Clone)]
pub struct Tokeniser<Q, E> {
    /// The current vector of parsed words.
    vec: Vec<String>,

    /// Whether or not we are currently in a word.
    in_word: bool,

    /// The current closing quote character, if any.
    quote: Option<char>,

    /// Whether the tokeniser is currently processing an escape character.
    escaping: bool,

    /// Maps from quote openers to quote closers.
    quote_pairs: Q,

    /// Map from escape characters to their replacements.
    escape_pairs: E,

    /// The character preceding escape characters.
    escape_leader: Option<char>
}

/// A tokeniser error.
///
/// A Tokeniser's `into_strings` method can fail with one of the following
/// errors if called while the Tokeniser is in an unfinished state.
#[deriving(Eq, PartialEq, Show)]
pub enum Error {
    /// A quotation was opened, but not closed.
    UnmatchedQuote,

    /// An escape sequence was started, but not finished.
    UnfinishedEscape
}

impl<Q: Map<char, char>+Clone, E: Map<char, char>+Clone> Tokeniser<Q, E> {
    /// Creates a new, blank Tokeniser.
    ///
    /// # Arguments
    ///
    /// * `quote_pairs`   - A map, mapping characters that serve as opening
    ///                     quotes to their closing quotes.
    /// * `escape_pairs`  - A map, mapping escape characters to the characters
    ///                     they represent.
    /// * `escape_leader` - The character, if any, that starts an escape
    ///                     sequence.
    ///
    /// # Return value
    ///
    /// A new Tokeniser, with an empty state.  Attempting to take the
    /// string vector of the Tokeniser yields the empty vector.
    ///
    /// # Example
    ///
    /// ```rust
    /// use std::collections::hashmap::HashMap;
    /// use russet::Tokeniser;
    ///
    /// let quote_pairs: HashMap<char, char> =
    ///     vec![ ( '\"', '\"' ), ( '\'', '\'' ) ].move_iter().collect();
    /// let escape_pairs: HashMap<char, char> =
    ///     vec![ ( 'n', '\n' ) ].move_iter().collect();
    /// let tok = Tokeniser::new(quote_pairs, escape_pairs, Some('\\'));
    /// assert_eq!(tok.into_strings(), Ok(vec![]));
    /// ```
    pub fn new(quote_pairs: Q, escape_pairs: E, escape_leader: Option<char>)
               -> Tokeniser<Q, E> {
        Tokeniser {
            vec: vec![ String::new() ],
            in_word: false,
            quote: None,
            escaping: false,
            quote_pairs: quote_pairs,
            escape_pairs: escape_pairs,
            escape_leader: escape_leader
        }
    }

    /// Feeds a single character `chr` to a Tokeniser.
    ///
    /// # Return value
    ///
    /// A new Tokeniser, representing the state of the Tokeniser after
    /// consuming `chr`.
    ///
    /// # Example
    ///
    /// ```rust
    /// use russet::Tokeniser;
    /// use std::collections::hashmap::HashMap;
    ///
    /// let quote_pairs: HashMap<char, char> =
    ///     vec![ ( '\"', '\"' ), ( '\'', '\'' ) ].move_iter().collect();
    /// let escape_pairs: HashMap<char, char> =
    ///     vec![ ( 'n', '\n' ) ].move_iter().collect();
    /// let tok = Tokeniser::new(quote_pairs, escape_pairs, Some('\\'));
    /// let tok2 = tok.add_char('a').add_char('b').add_char('c');
    /// assert_eq!(tok2.into_strings(), Ok(vec![ "abc".into_string() ]));
    /// ```
    pub fn add_char(self, chr: char) -> Tokeniser<Q, E> {
        let mut new = self.clone();

        match (chr, self) {
            // Escape character while not escaped
            // -> Begin escape (and word if not in one already)
            ( '\\', Tokeniser { escaping: false, .. } ) => {
                new.escaping = true;
                new.in_word = true;
            },
            // Unescaped quote opening character, not currently in quoted word
            // -> Start quoting
            ( q, Tokeniser {
                escaping: false,
                quote: None,
                quote_pairs: ref qs,
                ..
            } ) if qs.contains_key(&q) => {
                new.quote = Some(qs.find(&q).unwrap().clone());
                new.in_word = true;
            },
            // Unescaped quote closing character, in quoted word, quotes ok
            // -> Stop quoting
            ( c, Tokeniser { escaping: false, quote: Some(cc), .. } )
                if c == cc => {
                new.quote = None;
                new.in_word = true;
            },
            // Unescaped whitespace, while not in a word
            // -> Ignore
            ( a, Tokeniser { escaping: false, in_word: false, .. } )
                if is_whitespace(a) => (),
            // Unescaped whitespace, while in a non-quoted word
            // -> End word
            ( a, Tokeniser { escaping: false, in_word: true, quote: None, .. } )
                if is_whitespace(a) => {
                new.in_word = false;
                new.vec.push(String::new());
            },
            // Known escaped character
            // -> Escape character
            ( e, Tokeniser { escaping: true, escape_pairs: ref es, .. } )
                if es.contains_key(&e) => {
                let x = es.find(&e).unwrap();

                new.in_word = true;
                new.escaping = false;
                new.vec.mut_last().mutate(|s| { s.push_char(x.clone()); s });
            },
            // Anything else
            // -> Echo
            ( a, _ ) => {
                new.in_word = true;
                new.escaping = false;
                new.vec.mut_last().mutate(|s| { s.push_char(a); s });
            }
        }

        new
    }

    /// Feeds a line into the Tokeniser.
    ///
    /// # Return value
    ///
    /// A new Tokeniser, representing the state of the Tokeniser after
    /// consuming `line`.
    pub fn add_line(self, line: &str) -> Tokeniser<Q, E> {
        line.trim().chars().fold(self, |s, chr| s.add_char(chr))
    }

    /// Destroys the tokeniser, extracting the string vector.
    ///
    /// # Return value
    ///
    /// A Result, containing the tokenised string vector if the Tokeniser
    /// was in a valid ending state, and an Error otherwise.
    pub fn into_strings(mut self) -> Result<Vec<String>, Error> {
        if self.in_word && self.quote.is_some() {
            Err(UnmatchedQuote)
        } else if self.escaping {
            Err(UnfinishedEscape)
        } else {
            if self.vec.last().map(|s| s.len() == 0).unwrap_or(false) {
                self.vec.pop();
            }

            Ok(self.vec)
        }
    }
}

/// A type for tokenisers returned by Russet.
pub type StockTokeniser = Tokeniser<HashMap<char, char>, HashMap<char, char>>;

/// Creates a Tokeniser that doesn't support quoting or escaping.
///
/// This is effectively equivalent to the Words iterator on string slices.
///
/// # Return value
///
/// A Tokeniser with no quoting or escaping.
///
/// # Example
///
/// ```rust
/// use russet::whitespace_split_tokeniser;
///
/// let tok = whitespace_split_tokeniser();
/// let tok2 = tok.add_line("this \"ignores quotes\"  and\n  \\slashes");
/// assert_eq!(tok2.into_strings(), Ok(vec!("this".into_string(),
///                                         "\"ignores".into_string(),
///                                         "quotes\"".into_string(),
///                                         "and".into_string(),
///                                         "slashes".into_string())));
/// ```
#[experimental]
pub fn whitespace_split_tokeniser() -> StockTokeniser {
    let quote_pairs: HashMap<char, char> = HashMap::new();
    let escape_pairs: HashMap<char, char> = HashMap::new();
    Tokeniser::new(quote_pairs, escape_pairs, None)
}

/// Unpacks a line into its constituent words.
#[experimental]
pub fn unpack(line: &str) -> Result<Vec<String>, Error> {
    let quote_pairs: HashMap<char, char> =
        vec![ ( '\"', '\"' ), ( '\'', '\'' ) ].move_iter().collect();
    let escape_pairs: HashMap<char, char> =
        vec![ ( 'n', '\n' ) ].move_iter().collect();

    line.trim().chars().fold(
        Tokeniser::new(quote_pairs, escape_pairs, Some('\\')),
        |s, chr| s.add_char(chr)
    ).into_strings()
}

#[cfg(test)]
mod test {
    use super::{
        unpack,
        UnmatchedQuote,
        UnfinishedEscape
    };

    #[test]
    fn unpack_unmatched_quote() {
        assert_eq!(unpack("\"abcde"), Err(UnmatchedQuote));
    }

    #[test]
    fn unpack_unfinished_escape() {
        assert_eq!(unpack("zxcvbn m\\"), Err(UnfinishedEscape));
    }

    #[test]
    fn unpack_empty_string() {
        assert_eq!(unpack(""), Ok(vec![]));
    }

    #[test]
    fn unpack_leading_whitespace() {
        let rhs = vec![ "abc".into_string(), "def".into_string() ];
        assert_eq!(unpack("     abc def"), Ok(rhs));
    }

    #[test]
    fn unpack_trailing_whitespace() {
        let rhs = vec![ "ghi".into_string(), "jkl".into_string() ];
        assert_eq!(unpack("ghi jkl     \n"), Ok(rhs));
    }

    #[test]
    fn unpack_enqueue_command() {
        let lhs =
            "enqueue file \"C:\\\\Users\\\\Test\\\\Artist - Title.mp3\" 1";
        let rhs = vec![ "enqueue".into_string(),
                        "file".into_string(),
                        "C:\\Users\\Test\\Artist - Title.mp3".into_string(),
                        "1".into_string() ];
        assert_eq!(unpack(lhs), Ok(rhs));
    }

    #[test]
    fn unpack_escaped_newline() {
        assert_eq!(unpack("abc\\nde"),     Ok(vec![ "abc\nde".into_string() ]));
        assert_eq!(unpack("\"abc\\nde\""), Ok(vec![ "abc\nde".into_string() ]));
    }
}
<|endoftext|>


<commit_before>use std::ops::Deref;

/// Dirty wraps a value of type T with functions similiar to that of a Read/Write
/// lock but simply sets a dirty flag on write(), reset on clear().
/// Use read() or deref (*dirty_variable) to access the inner value.
pub struct Dirty<T> {
    value: T,
    dirty: bool,
}

impl<T> Dirty<T> {
    /// Create a new Dirty.
    pub fn new(val: T) -> Dirty<T> {
        Dirty {
            value: val,
            dirty: true,
        }
    }

    /// Returns true if dirty, false otherwise.
    #[allow(dead_code)]
    pub fn dirty(&self) -> bool {
        self.dirty
    }

    /// Writable value return, sets the dirty flag.
    pub fn write(&mut self) -> &mut T {
        self.dirty = true;
        &mut self.value
    }

    /// Read the value.
    pub fn read(&self) -> &T {
        &self.value
    }

    /// Clears the dirty flag.
    pub fn clear(&mut self) {
        self.dirty = false;
    }

    /// Read the value only if modified since last read.
    #[allow(dead_code)]
    pub fn read_dirty(&self) -> Option<&T> {
        match self.dirty {
            true => {
                Some(&self.value)
            },
            false => None,
        }
    }
}

impl<T> Deref for Dirty<T> {
    type Target = T;
    fn deref(&self) -> &T {
        &self.value
    }
}

impl<T> Default for Dirty<T> where T: Default {
    fn default() -> Self {
        Dirty::new(T::default())
    }
}

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

    #[test]
    fn new_dirty() {
        let dirty = Dirty::new(0);
        assert!(dirty.dirty() == true);
    }

    #[test]
    fn read_doesnt_clears_flag() {
        let dirty = Dirty::new(0);
        assert!(dirty.dirty() == true);
        assert!(*dirty.read() == 0);
        assert!(dirty.dirty() == true);
    }

    #[test]
    fn write_sets_flag() {
        let mut dirty = Dirty::new(0);
        assert!(*dirty.read() == 0);
        dirty.clear();
        assert!(dirty.dirty() == false);
        *dirty.write() += 1;
        assert!(dirty.dirty() == true);
    }

    #[test]
    fn read_dirty() {
        let mut dirty = Dirty::new(0);
        assert!(dirty.read_dirty().is_some());
		dirty.clear();
        assert!(dirty.dirty() == false);
        assert!(dirty.read_dirty() == None);
        assert!(dirty.dirty() == false);
        *dirty.write() += 1;
        assert!(dirty.dirty() == true);
        assert!(dirty.read_dirty().is_some());
		dirty.clear();
        assert!(dirty.dirty() == false);
        assert!(dirty.read_dirty() == None);
    }

    #[test]
    fn access_inner_deref() {
        let dirty = Dirty::new(0);
        assert!(*dirty == 0);
    }

    #[test]
    fn default_value() {
        let dirty = Dirty::<i32>::default();
        assert!(*dirty == 0);
    }
}
<commit_msg>Fixed naming. Removed allow_deadcode. Simplified tests<commit_after>use std::ops::Deref;

/// Dirty wraps a value of type T with functions similiar to that of a Read/Write
/// lock but simply sets a dirty flag on write(), reset on clear().
/// Use read() or deref (*dirty_variable) to access the inner value.
pub struct Dirty<T> {
    value: T,
    dirty: bool,
}

impl<T> Dirty<T> {
    /// Create a new Dirty.
    pub fn new(val: T) -> Dirty<T> {
        Dirty {
            value: val,
            dirty: true,
        }
    }

    /// Returns true if dirty, false otherwise.
    pub fn dirty(&self) -> bool {
        self.dirty
    }

    /// Writable value return, sets the dirty flag.
    pub fn write(&mut self) -> &mut T {
        self.dirty = true;
        &mut self.value
    }

    /// Read the value.
    pub fn read(&self) -> &T {
        &self.value
    }

    /// Clears the dirty flag.
    pub fn clear(&mut self) {
        self.dirty = false;
    }

    /// Read the value only if modified since last read.
    pub fn read_dirty(&self) -> Option<&T> {
        match self.dirty {
            true => {
                Some(&self.value)
            },
            false => None,
        }
    }
}

impl<T> Deref for Dirty<T> {
    type Target = T;
    fn deref(&self) -> &T {
        &self.value
    }
}

impl<T> Default for Dirty<T> where T: Default {
    fn default() -> Self {
        Dirty::new(T::default())
    }
}

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

    #[test]
    fn new_dirty() {
        let dirty = Dirty::new(0);
        assert!(dirty.dirty());
    }

    #[test]
    fn read_doesnt_clear_flag() {
        let dirty = Dirty::new(0);
        assert!(dirty.dirty());
        assert!(*dirty.read() == 0);
        assert!(dirty.dirty());
    }

    #[test]
    fn write_sets_flag() {
        let mut dirty = Dirty::new(0);
        assert!(*dirty.read() == 0);
        dirty.clear();
        assert!(!dirty.dirty());
        *dirty.write() += 1;
        assert!(dirty.dirty());
    }

    #[test]
    fn read_dirty() {
        let mut dirty = Dirty::new(0);
        assert!(dirty.read_dirty().is_some());
		dirty.clear();
        assert!(!dirty.dirty());
        assert!(dirty.read_dirty() == None);
        assert!(!dirty.dirty());
        *dirty.write() += 1;
        assert!(dirty.dirty());
        assert!(dirty.read_dirty().is_some());
		dirty.clear();
        assert!(!dirty.dirty());
        assert!(dirty.read_dirty() == None);
    }

    #[test]
    fn access_inner_deref() {
        let dirty = Dirty::new(0);
        assert!(*dirty == 0);
    }

    #[test]
    fn default_value() {
        let dirty = Dirty::<i32>::default();
        assert!(*dirty == 0);
    }
}
<|endoftext|>


<commit_before>//! zmq.rs is a native implementation of [ØMQ] in the Rust programming language. It is still in a
//! very early stage of designing and development, so it is **not** supposed to be used seriously now.
//!
//! # Examples
//!
//! There are only very few interfaces implemented till now. Try this example for now:
//!
//! ```rust
//! extern crate zeromq;
//!
//! fn main() {
//!     let ctx = zeromq::Context::new();
//!
//!     let mut req = ctx.socket(zeromq::SocketType::REQ);
//!     req.connect("tcp://127.0.0.1:12347").unwrap();
//!
//!     let mut rep = ctx.socket(zeromq::SocketType::REP);
//!     rep.bind("tcp://127.0.0.1:12347").unwrap();
//!
//!     let mut msg = box zeromq::Msg::new(4);
//!     msg.data.push_all([65u8, 66u8, 67u8, 68u8].as_slice());
//!
//!     req.msg_send(msg).unwrap();
//!     println!("We got: {}", rep.msg_recv().unwrap());
//! }
//! ```
//!
//!  [ØMQ]: http://zeromq.org/

#![crate_name = "zeromq"]
#![unstable]
#![crate_type = "rlib"]
#![crate_type = "dylib"]
#[macro_use] extern crate log;

pub use ctx::Context;
pub use consts::SocketType;
pub use consts::SocketOption;
pub use consts::ErrorCode;
pub use msg::Msg;
pub use rep::RepSocket;
pub use req::ReqSocket;
pub use result::{ZmqResult, ZmqError};
pub use socket::ZmqSocket;

mod ctx;
mod consts;
mod inproc;
mod msg;
mod rep;
mod req;
mod result;
mod socket;
mod socket_base;
mod stream_engine;
mod tcp_connecter;
mod tcp_listener;
mod options;
mod v2_encoder;
mod v2_decoder;
mod v2_protocol;

#[cfg(test)]
mod test {
    #[test]
    fn test_socket_type() {
        assert_eq!(super::SocketType::REQ as int, 3);
    }

    #[test]
    fn test_socket_create() {
        let c = super::Context::new();
        let s = c.socket(super::SocketType::REQ);
        assert_eq!(s.getsockopt(super::SocketOption::TYPE), super::SocketType::REQ as int);
    }

    #[test]
    fn test_socket_bind() {
        let c = super::Context::new();
        let s = c.socket(super::SocketType::REQ);
        assert_eq!(s.bind("").unwrap_err().code, super::ErrorCode::EINVAL);
        assert_eq!(s.bind("://127").unwrap_err().code, super::ErrorCode::EINVAL);
        assert_eq!(s.bind("tcp://").unwrap_err().code, super::ErrorCode::EINVAL);
        assert_eq!(s.bind("tcpp://127.0.0.1:12345").unwrap_err().code, super::ErrorCode::EPROTONOSUPPORT);
        assert_eq!(s.bind("tcp://10.0.1.255:12345").unwrap_err().code, super::ErrorCode::ECONNREFUSED);
        assert_eq!(s.bind("tcp://10.0.1.1:12z45").unwrap_err().code, super::ErrorCode::EINVAL);
        assert!(s.bind("tcp://127.0.0.1:12345").is_ok());
        assert_eq!(s.bind("tcp://127.0.0.1:12345").unwrap_err().code, super::ErrorCode::ECONNREFUSED);
    }

    #[test]
    fn test_socket_connect() {
        let c = super::Context::new();
        let s = c.socket(super::SocketType::REQ);
        assert_eq!(s.connect("").unwrap_err().code, super::ErrorCode::EINVAL);
        assert_eq!(s.connect("://127").unwrap_err().code, super::ErrorCode::EINVAL);
        assert_eq!(s.connect("tcp://").unwrap_err().code, super::ErrorCode::EINVAL);
        assert_eq!(s.connect("tcpp://127.0.0.1:12346").unwrap_err().code, super::ErrorCode::EPROTONOSUPPORT);
        assert_eq!(s.connect("tcp://10.0.1.1:12z46").unwrap_err().code, super::ErrorCode::EINVAL);
        assert!(s.connect("tcp://127.0.0.1:12346").is_ok());
        assert!(s.connect("tcp://127.0.0.1:12346").is_ok());
    }

    #[test]
    fn test_socket_small_message() {
        let c = super::Context::new();
        let mut req = c.socket(super::SocketType::REQ);
        let mut rep = c.socket(super::SocketType::REP);
        assert!(rep.bind("tcp://127.0.0.1:12347").is_ok());
        assert!(req.connect("tcp://127.0.0.1:12347").is_ok());

        let mut msg_sent = box super::Msg::new(4);
        msg_sent.data.push_all([65u8, 66u8, 67u8, 68u8].as_slice());
        assert!(req.msg_send(msg_sent).is_ok());

        let msg_recv = rep.msg_recv().unwrap();
        assert_eq!(msg_recv.data, [65u8, 66u8, 67u8, 68u8].to_vec());
    }

    #[test]
    fn test_inproc_and_moved_socket() {
        let c = super::Context::new();
        let req = c.socket(super::SocketType::REQ);

        spawn(move || {
            let mut req = req;
            assert!(req.connect("inproc://#1").is_ok());

            let mut msg_sent = box super::Msg::new(4);
            msg_sent.data.push_all([65u8, 66u8, 67u8, 68u8].as_slice());
            assert!(req.msg_send(msg_sent).is_ok());
        });

        let mut rep = c.socket(super::SocketType::REP);
        assert!(rep.bind("inproc://#1").is_ok());
        let msg_recv = rep.msg_recv().unwrap();
        assert_eq!(msg_recv.data, [65u8, 66u8, 67u8, 68u8].to_vec());
    }
}
<commit_msg>Enable box syntax and uint feature gate<commit_after>//! zmq.rs is a native implementation of [ØMQ] in the Rust programming language. It is still in a
//! very early stage of designing and development, so it is **not** supposed to be used seriously now.
//!
//! # Examples
//!
//! There are only very few interfaces implemented till now. Try this example for now:
//!
//! ```rust
//! extern crate zeromq;
//!
//! fn main() {
//!     let ctx = zeromq::Context::new();
//!
//!     let mut req = ctx.socket(zeromq::SocketType::REQ);
//!     req.connect("tcp://127.0.0.1:12347").unwrap();
//!
//!     let mut rep = ctx.socket(zeromq::SocketType::REP);
//!     rep.bind("tcp://127.0.0.1:12347").unwrap();
//!
//!     let mut msg = box zeromq::Msg::new(4);
//!     msg.data.push_all([65u8, 66u8, 67u8, 68u8].as_slice());
//!
//!     req.msg_send(msg).unwrap();
//!     println!("We got: {}", rep.msg_recv().unwrap());
//! }
//! ```
//!
//!  [ØMQ]: http://zeromq.org/

#![crate_name = "zeromq"]
#![unstable]
#![crate_type = "rlib"]
#![crate_type = "dylib"]
#![feature(box_syntax)]
#![feature(int_uint)]
#[macro_use] extern crate log;

pub use ctx::Context;
pub use consts::SocketType;
pub use consts::SocketOption;
pub use consts::ErrorCode;
pub use msg::Msg;
pub use rep::RepSocket;
pub use req::ReqSocket;
pub use result::{ZmqResult, ZmqError};
pub use socket::ZmqSocket;

mod ctx;
mod consts;
mod inproc;
mod msg;
mod rep;
mod req;
mod result;
mod socket;
mod socket_base;
mod stream_engine;
mod tcp_connecter;
mod tcp_listener;
mod options;
mod v2_encoder;
mod v2_decoder;
mod v2_protocol;

#[cfg(test)]
mod test {
    #[test]
    fn test_socket_type() {
        assert_eq!(super::SocketType::REQ as int, 3);
    }

    #[test]
    fn test_socket_create() {
        let c = super::Context::new();
        let s = c.socket(super::SocketType::REQ);
        assert_eq!(s.getsockopt(super::SocketOption::TYPE), super::SocketType::REQ as int);
    }

    #[test]
    fn test_socket_bind() {
        let c = super::Context::new();
        let s = c.socket(super::SocketType::REQ);
        assert_eq!(s.bind("").unwrap_err().code, super::ErrorCode::EINVAL);
        assert_eq!(s.bind("://127").unwrap_err().code, super::ErrorCode::EINVAL);
        assert_eq!(s.bind("tcp://").unwrap_err().code, super::ErrorCode::EINVAL);
        assert_eq!(s.bind("tcpp://127.0.0.1:12345").unwrap_err().code, super::ErrorCode::EPROTONOSUPPORT);
        assert_eq!(s.bind("tcp://10.0.1.255:12345").unwrap_err().code, super::ErrorCode::ECONNREFUSED);
        assert_eq!(s.bind("tcp://10.0.1.1:12z45").unwrap_err().code, super::ErrorCode::EINVAL);
        assert!(s.bind("tcp://127.0.0.1:12345").is_ok());
        assert_eq!(s.bind("tcp://127.0.0.1:12345").unwrap_err().code, super::ErrorCode::ECONNREFUSED);
    }

    #[test]
    fn test_socket_connect() {
        let c = super::Context::new();
        let s = c.socket(super::SocketType::REQ);
        assert_eq!(s.connect("").unwrap_err().code, super::ErrorCode::EINVAL);
        assert_eq!(s.connect("://127").unwrap_err().code, super::ErrorCode::EINVAL);
        assert_eq!(s.connect("tcp://").unwrap_err().code, super::ErrorCode::EINVAL);
        assert_eq!(s.connect("tcpp://127.0.0.1:12346").unwrap_err().code, super::ErrorCode::EPROTONOSUPPORT);
        assert_eq!(s.connect("tcp://10.0.1.1:12z46").unwrap_err().code, super::ErrorCode::EINVAL);
        assert!(s.connect("tcp://127.0.0.1:12346").is_ok());
        assert!(s.connect("tcp://127.0.0.1:12346").is_ok());
    }

    #[test]
    fn test_socket_small_message() {
        let c = super::Context::new();
        let mut req = c.socket(super::SocketType::REQ);
        let mut rep = c.socket(super::SocketType::REP);
        assert!(rep.bind("tcp://127.0.0.1:12347").is_ok());
        assert!(req.connect("tcp://127.0.0.1:12347").is_ok());

        let mut msg_sent = box super::Msg::new(4);
        msg_sent.data.push_all([65u8, 66u8, 67u8, 68u8].as_slice());
        assert!(req.msg_send(msg_sent).is_ok());

        let msg_recv = rep.msg_recv().unwrap();
        assert_eq!(msg_recv.data, [65u8, 66u8, 67u8, 68u8].to_vec());
    }

    #[test]
    fn test_inproc_and_moved_socket() {
        let c = super::Context::new();
        let req = c.socket(super::SocketType::REQ);

        spawn(move || {
            let mut req = req;
            assert!(req.connect("inproc://#1").is_ok());

            let mut msg_sent = box super::Msg::new(4);
            msg_sent.data.push_all([65u8, 66u8, 67u8, 68u8].as_slice());
            assert!(req.msg_send(msg_sent).is_ok());
        });

        let mut rep = c.socket(super::SocketType::REP);
        assert!(rep.bind("inproc://#1").is_ok());
        let msg_recv = rep.msg_recv().unwrap();
        assert_eq!(msg_recv.data, [65u8, 66u8, 67u8, 68u8].to_vec());
    }
}
<|endoftext|>


<commit_before>#![feature(conservative_impl_trait, universal_impl_trait)]
#![feature(nll)]
// #![deny(warnings)]
#![allow(unknown_lints)]
#![allow(clone_on_ref_ptr)]

#[macro_use]
extern crate error_chain;
extern crate fern;
extern crate libc;
#[macro_use]
extern crate log;
extern crate log_panics;
extern crate time;
extern crate x11;
extern crate xcb;
extern crate xcb_util;
extern crate xdg;

use std::cell::RefCell;
use std::cmp;
use std::rc::Rc;

use error_chain::ChainedError;

pub mod cmd;
pub mod errors;
mod groups;
mod keys;
pub mod layout;
mod stack;
mod x;

use errors::*;
use groups::Group;
use keys::{KeyCombo, KeyHandlers};
use layout::Layout;
use x::{Connection, Event, StrutPartial, WindowId, WindowType};

pub use groups::GroupBuilder;
pub use keys::ModKey;
pub use stack::Stack;

pub mod keysym {
    pub use x11::keysym::*;
}

/// Initializes a logger using the default configuration.
///
/// Outputs to stdout and `$XDG_DATA/lanta/lanta.log` by default.
/// You should feel free to initialize your own logger, instead of using this.
pub fn intiailize_logger() -> Result<()> {
    log_panics::init();

    let xdg_dirs = xdg::BaseDirectories::with_prefix("lanta")?;
    let log_path = xdg_dirs
        .place_data_file("lanta.log")
        .chain_err(|| "Could not create log file")?;

    fern::Dispatch::new()
        .format(|out, message, record| {
            out.finish(format_args!(
                "[{}] [{}] {}",
                time::now().rfc3339(),
                record.level(),
                message
            ))
        })
        .level(log::LevelFilter::Trace)
        .chain(std::io::stdout())
        .chain(fern::log_file(&log_path)?)
        .apply()?;

    Ok(())
}

#[macro_export]
macro_rules! keys {
    [ $( ([$( $mod:ident ),*], $key:ident, $cmd:expr) ),+ $(,)*] => (
        vec![
            $( (vec![$( $mod ),*],  $crate::keysym::$key, $cmd) ),+
        ]
    )
}

#[macro_export]
macro_rules! groups {
    {
        $keys:ident,
        $movemodkey:ident,
        [
            $(( [$( $modkey:ident ),+], $key:ident, $name:expr, $layout:expr )),+
            $(,)*
        ]
    }  => {{
        $keys.extend(keys![
            // Switch to group:
            $(
                ([$($modkey),+], $key, $crate::cmd::lazy::switch_group($name))
            ),+,
            // Move window to group:
            $(
                ([$($modkey),+, $movemodkey], $key,  $crate::cmd::lazy::move_window_to_group($name))
            ),+
        ]);
        vec![
            $(
                 $crate::GroupBuilder::new($name, $layout)
            ),+
        ]
    }}
}

#[macro_export]
macro_rules! layouts {
    [$( $layout:expr ),+ $(,)*] => (
        vec![
            $(
                Box::new($layout) as Box<$crate::layout::Layout>
            ),+
        ]
    )
}

#[derive(Clone, Copy, Debug, Default)]
pub struct Viewport {
    pub x: u32,
    pub y: u32,
    pub width: u32,
    pub height: u32,
}

struct Dock {
    window_id: WindowId,
    strut_partial: Option<StrutPartial>,
}

#[derive(Default)]
struct Screen {
    vec: RefCell<Vec<Dock>>,
}

impl Screen {
    pub fn add_dock(&mut self, conn: &Connection, window_id: WindowId) {
        let strut_partial = conn.get_strut_partial(&window_id);
        self.vec.borrow_mut().push(Dock {
            window_id,
            strut_partial,
        });
    }

    pub fn remove_dock(&mut self, window_id: &WindowId) {
        self.vec.borrow_mut().retain(|d| &d.window_id != window_id);
    }

    /// Figure out the usable area of the screen based on the STRUT_PARTIAL of
    /// all docks.
    pub fn viewport(&self, screen_width: u32, screen_height: u32) -> Viewport {
        let (left, right, top, bottom) = self
            .vec
            .borrow()
            .iter()
            .filter_map(|o| o.strut_partial.as_ref())
            .fold((0, 0, 0, 0), |(left, right, top, bottom), s| {
                // We don't bother looking at the start/end members of the
                // StrutPartial - treating it more like a Strut.
                (
                    cmp::max(left, s.left()),
                    cmp::max(right, s.right()),
                    cmp::max(top, s.top()),
                    cmp::max(bottom, s.bottom()),
                )
            });
        let viewport = Viewport {
            x: left,
            y: top,
            width: screen_width - left - right,
            height: screen_height - top - bottom,
        };
        debug!("Calculated Viewport as {:?}", viewport);
        viewport
    }
}

pub struct Lanta {
    connection: Rc<Connection>,
    keys: KeyHandlers,
    groups: Stack<Group>,
    screen: Screen,
}

impl Lanta {
    pub fn new<K>(keys: K, groups: Vec<GroupBuilder>, layouts: &[Box<Layout>]) -> Result<Self>
    where
        K: Into<KeyHandlers>,
    {
        let keys = keys.into();
        let connection = Rc::new(Connection::connect()?);
        connection.install_as_wm(&keys)?;

        let groups = Stack::from(
            groups
                .into_iter()
                .map(|group: GroupBuilder| group.build(connection.clone(), layouts.to_owned()))
                .collect::<Vec<Group>>(),
        );

        let mut wm = Lanta {
            connection: connection.clone(),
            keys: keys,
            groups: groups,
            screen: Screen::default(),
        };

        // Learn about existing top-level windows.
        let existing_windows = connection.top_level_windows()?;
        for window in existing_windows {
            wm.manage_window(window);
        }
        let viewport = wm.viewport();
        wm.group_mut().activate(viewport);
        wm.connection.update_ewmh_desktops(&wm.groups);

        Ok(wm)
    }

    fn viewport(&self) -> Viewport {
        let (width, height) = self
            .connection
            .get_window_geometry(self.connection.root_window_id());
        self.screen.viewport(width, height)
    }

    pub fn group(&self) -> &Group {
        self.groups.focused().expect("Invariant: No active group!")
    }

    pub fn group_mut(&mut self) -> &mut Group {
        self.groups
            .focused_mut()
            .expect("Invariant: No active group!")
    }

    pub fn switch_group<'a, S>(&'a mut self, name: S)
    where
        S: Into<&'a str>,
    {
        let name = name.into();

        // If we're already on this group, do nothing.
        if self.group().name() == name {
            return;
        }

        self.group_mut().deactivate();
        self.groups.focus(|group| group.name() == name);
        let viewport = self.viewport();
        self.group_mut().activate(viewport);
        self.connection.update_ewmh_desktops(&self.groups);
    }

    /// Move the focused window from the active group to another named group.
    ///
    /// If the other named group does not exist, then the window is
    /// (unfortunately) lost.
    pub fn move_focused_to_group<'a, S>(&'a mut self, name: S)
    where
        S: Into<&'a str>,
    {
        let name = name.into();

        // If the group is currently active, then do nothing. This avoids flicker as we
        // unmap/remap.
        if name == self.group().name() {
            return;
        }

        let removed = self.group_mut().remove_focused();
        let new_group_opt = self.groups.iter_mut().find(|group| group.name() == name);
        match new_group_opt {
            Some(new_group) => {
                removed.map(|window| new_group.add_window(window));
            }
            None => {
                // It would be nice to put the window back in its group (or avoid taking it out
                // of its group until we've checked the new group exists), but it's difficult
                // to do this while keeping the borrow checker happy.
                error!("Moved window to non-existent group: {}", name);
            }
        }
    }

    /// Returns whether the window is a member of any group.
    fn is_window_managed(&self, window_id: &WindowId) -> bool {
        self.groups.iter().any(|g| g.contains(window_id))
    }

    pub fn manage_window(&mut self, window_id: WindowId) {
        debug!("Managing window: {}", window_id);

        // If we are already managing the window, then do nothing. We do not
        // want the window to end up in two groups at once. We shouldn't
        // be called in such cases, so treat it as an error.
        if self.is_window_managed(&window_id) {
            error!(
                "Asked to manage window that's already managed: {}",
                window_id
            );
            return;
        }

        let window_types = self.connection.get_window_types(&window_id);
        let dock = window_types.contains(&WindowType::Dock);

        self.connection
            .enable_window_key_events(&window_id, &self.keys);

        if dock {
            self.connection.map_window(&window_id);
            self.screen.add_dock(&self.connection, window_id);
            let viewport = self.viewport();
            self.group_mut().update_viewport(viewport);
        } else {
            self.connection.enable_window_tracking(&window_id);
            self.group_mut().add_window(window_id);
        }
    }

    pub fn unmanage_window(&mut self, window_id: &WindowId) {
        debug!("Unmanaging window: {}", window_id);

        // Remove the window from whichever Group it is in. Special case for
        // docks which aren't in any group.
        self.groups
            .iter_mut()
            .find(|group| group.contains(window_id))
            .map(|group| group.remove_window(window_id));
        self.screen.remove_dock(window_id);

        // The viewport may have changed.
        let viewport = self.viewport();
        self.group_mut().update_viewport(viewport);
    }

    pub fn run(mut self) {
        info!("Started WM, entering event loop.");
        let event_loop_connection = self.connection.clone();
        let event_loop = event_loop_connection.get_event_loop();
        for event in event_loop {
            match event {
                Event::MapRequest(window_id) => self.on_map_request(window_id),
                Event::UnmapNotify(window_id) => self.on_unmap_notify(&window_id),
                Event::DestroyNotify(window_id) => self.on_destroy_notify(&window_id),
                Event::KeyPress(key) => self.on_key_press(key),
                Event::EnterNotify(window_id) => self.on_enter_notify(&window_id),
            }
        }
        info!("Event loop exiting");
    }

    fn on_map_request(&mut self, window_id: WindowId) {
        if !self.is_window_managed(&window_id) {
            // If the window isn't in any group, then add it to the current group.
            // (This will have the side-effect of mapping the window, as new windows are focused
            // and focused windows are mapped).
            self.manage_window(window_id);
        } else if self.group().contains(&window_id) {
            // Otherwise, if the window is in the active group, focus it. The application probably
            // wants us to make it prominent. Log as there may be misbehaving applications that
            // constantly re-map windows and cause focus issues.
            info!(
                "Window {} asked to be mapped but is already mapped: focusing.",
                window_id
            );
            self.group_mut().focus(&window_id);
        }
    }

    fn on_unmap_notify(&mut self, window_id: &WindowId) {
        // We only receive an unmap notify event when the window is actually
        // unmapped by its application. When our layouts unmap windows, they
        // (should) do it by disabling event tracking first.
        self.unmanage_window(window_id);
    }

    fn on_destroy_notify(&mut self, window_id: &WindowId) {
        self.unmanage_window(window_id);
    }

    fn on_key_press(&mut self, key: KeyCombo) {
        self.keys.get(&key).map(move |handler| {
            if let Err(error) = (handler)(self) {
                error!(
                    "Error running command for key command {:?}: {}",
                    key,
                    error.display_chain().to_string()
                );
            }
        });
    }

    fn on_enter_notify(&mut self, window_id: &WindowId) {
        self.group_mut().focus(window_id);
    }
}
<commit_msg>Remove unused nightly features.<commit_after>// #![deny(warnings)]
#![allow(unknown_lints)]
#![allow(clone_on_ref_ptr)]

#[macro_use]
extern crate error_chain;
extern crate fern;
extern crate libc;
#[macro_use]
extern crate log;
extern crate log_panics;
extern crate time;
extern crate x11;
extern crate xcb;
extern crate xcb_util;
extern crate xdg;

use std::cell::RefCell;
use std::cmp;
use std::rc::Rc;

use error_chain::ChainedError;

pub mod cmd;
pub mod errors;
mod groups;
mod keys;
pub mod layout;
mod stack;
mod x;

use errors::*;
use groups::Group;
use keys::{KeyCombo, KeyHandlers};
use layout::Layout;
use x::{Connection, Event, StrutPartial, WindowId, WindowType};

pub use groups::GroupBuilder;
pub use keys::ModKey;
pub use stack::Stack;

pub mod keysym {
    pub use x11::keysym::*;
}

/// Initializes a logger using the default configuration.
///
/// Outputs to stdout and `$XDG_DATA/lanta/lanta.log` by default.
/// You should feel free to initialize your own logger, instead of using this.
pub fn intiailize_logger() -> Result<()> {
    log_panics::init();

    let xdg_dirs = xdg::BaseDirectories::with_prefix("lanta")?;
    let log_path = xdg_dirs
        .place_data_file("lanta.log")
        .chain_err(|| "Could not create log file")?;

    fern::Dispatch::new()
        .format(|out, message, record| {
            out.finish(format_args!(
                "[{}] [{}] {}",
                time::now().rfc3339(),
                record.level(),
                message
            ))
        })
        .level(log::LevelFilter::Trace)
        .chain(std::io::stdout())
        .chain(fern::log_file(&log_path)?)
        .apply()?;

    Ok(())
}

#[macro_export]
macro_rules! keys {
    [ $( ([$( $mod:ident ),*], $key:ident, $cmd:expr) ),+ $(,)*] => (
        vec![
            $( (vec![$( $mod ),*],  $crate::keysym::$key, $cmd) ),+
        ]
    )
}

#[macro_export]
macro_rules! groups {
    {
        $keys:ident,
        $movemodkey:ident,
        [
            $(( [$( $modkey:ident ),+], $key:ident, $name:expr, $layout:expr )),+
            $(,)*
        ]
    }  => {{
        $keys.extend(keys![
            // Switch to group:
            $(
                ([$($modkey),+], $key, $crate::cmd::lazy::switch_group($name))
            ),+,
            // Move window to group:
            $(
                ([$($modkey),+, $movemodkey], $key,  $crate::cmd::lazy::move_window_to_group($name))
            ),+
        ]);
        vec![
            $(
                 $crate::GroupBuilder::new($name, $layout)
            ),+
        ]
    }}
}

#[macro_export]
macro_rules! layouts {
    [$( $layout:expr ),+ $(,)*] => (
        vec![
            $(
                Box::new($layout) as Box<$crate::layout::Layout>
            ),+
        ]
    )
}

#[derive(Clone, Copy, Debug, Default)]
pub struct Viewport {
    pub x: u32,
    pub y: u32,
    pub width: u32,
    pub height: u32,
}

struct Dock {
    window_id: WindowId,
    strut_partial: Option<StrutPartial>,
}

#[derive(Default)]
struct Screen {
    vec: RefCell<Vec<Dock>>,
}

impl Screen {
    pub fn add_dock(&mut self, conn: &Connection, window_id: WindowId) {
        let strut_partial = conn.get_strut_partial(&window_id);
        self.vec.borrow_mut().push(Dock {
            window_id,
            strut_partial,
        });
    }

    pub fn remove_dock(&mut self, window_id: &WindowId) {
        self.vec.borrow_mut().retain(|d| &d.window_id != window_id);
    }

    /// Figure out the usable area of the screen based on the STRUT_PARTIAL of
    /// all docks.
    pub fn viewport(&self, screen_width: u32, screen_height: u32) -> Viewport {
        let (left, right, top, bottom) = self
            .vec
            .borrow()
            .iter()
            .filter_map(|o| o.strut_partial.as_ref())
            .fold((0, 0, 0, 0), |(left, right, top, bottom), s| {
                // We don't bother looking at the start/end members of the
                // StrutPartial - treating it more like a Strut.
                (
                    cmp::max(left, s.left()),
                    cmp::max(right, s.right()),
                    cmp::max(top, s.top()),
                    cmp::max(bottom, s.bottom()),
                )
            });
        let viewport = Viewport {
            x: left,
            y: top,
            width: screen_width - left - right,
            height: screen_height - top - bottom,
        };
        debug!("Calculated Viewport as {:?}", viewport);
        viewport
    }
}

pub struct Lanta {
    connection: Rc<Connection>,
    keys: KeyHandlers,
    groups: Stack<Group>,
    screen: Screen,
}

impl Lanta {
    pub fn new<K>(keys: K, groups: Vec<GroupBuilder>, layouts: &[Box<Layout>]) -> Result<Self>
    where
        K: Into<KeyHandlers>,
    {
        let keys = keys.into();
        let connection = Rc::new(Connection::connect()?);
        connection.install_as_wm(&keys)?;

        let groups = Stack::from(
            groups
                .into_iter()
                .map(|group: GroupBuilder| group.build(connection.clone(), layouts.to_owned()))
                .collect::<Vec<Group>>(),
        );

        let mut wm = Lanta {
            connection: connection.clone(),
            keys: keys,
            groups: groups,
            screen: Screen::default(),
        };

        // Learn about existing top-level windows.
        let existing_windows = connection.top_level_windows()?;
        for window in existing_windows {
            wm.manage_window(window);
        }
        let viewport = wm.viewport();
        wm.group_mut().activate(viewport);
        wm.connection.update_ewmh_desktops(&wm.groups);

        Ok(wm)
    }

    fn viewport(&self) -> Viewport {
        let (width, height) = self
            .connection
            .get_window_geometry(self.connection.root_window_id());
        self.screen.viewport(width, height)
    }

    pub fn group(&self) -> &Group {
        self.groups.focused().expect("Invariant: No active group!")
    }

    pub fn group_mut(&mut self) -> &mut Group {
        self.groups
            .focused_mut()
            .expect("Invariant: No active group!")
    }

    pub fn switch_group<'a, S>(&'a mut self, name: S)
    where
        S: Into<&'a str>,
    {
        let name = name.into();

        // If we're already on this group, do nothing.
        if self.group().name() == name {
            return;
        }

        self.group_mut().deactivate();
        self.groups.focus(|group| group.name() == name);
        let viewport = self.viewport();
        self.group_mut().activate(viewport);
        self.connection.update_ewmh_desktops(&self.groups);
    }

    /// Move the focused window from the active group to another named group.
    ///
    /// If the other named group does not exist, then the window is
    /// (unfortunately) lost.
    pub fn move_focused_to_group<'a, S>(&'a mut self, name: S)
    where
        S: Into<&'a str>,
    {
        let name = name.into();

        // If the group is currently active, then do nothing. This avoids flicker as we
        // unmap/remap.
        if name == self.group().name() {
            return;
        }

        let removed = self.group_mut().remove_focused();
        let new_group_opt = self.groups.iter_mut().find(|group| group.name() == name);
        match new_group_opt {
            Some(new_group) => {
                removed.map(|window| new_group.add_window(window));
            }
            None => {
                // It would be nice to put the window back in its group (or avoid taking it out
                // of its group until we've checked the new group exists), but it's difficult
                // to do this while keeping the borrow checker happy.
                error!("Moved window to non-existent group: {}", name);
            }
        }
    }

    /// Returns whether the window is a member of any group.
    fn is_window_managed(&self, window_id: &WindowId) -> bool {
        self.groups.iter().any(|g| g.contains(window_id))
    }

    pub fn manage_window(&mut self, window_id: WindowId) {
        debug!("Managing window: {}", window_id);

        // If we are already managing the window, then do nothing. We do not
        // want the window to end up in two groups at once. We shouldn't
        // be called in such cases, so treat it as an error.
        if self.is_window_managed(&window_id) {
            error!(
                "Asked to manage window that's already managed: {}",
                window_id
            );
            return;
        }

        let window_types = self.connection.get_window_types(&window_id);
        let dock = window_types.contains(&WindowType::Dock);

        self.connection
            .enable_window_key_events(&window_id, &self.keys);

        if dock {
            self.connection.map_window(&window_id);
            self.screen.add_dock(&self.connection, window_id);
            let viewport = self.viewport();
            self.group_mut().update_viewport(viewport);
        } else {
            self.connection.enable_window_tracking(&window_id);
            self.group_mut().add_window(window_id);
        }
    }

    pub fn unmanage_window(&mut self, window_id: &WindowId) {
        debug!("Unmanaging window: {}", window_id);

        // Remove the window from whichever Group it is in. Special case for
        // docks which aren't in any group.
        self.groups
            .iter_mut()
            .find(|group| group.contains(window_id))
            .map(|group| group.remove_window(window_id));
        self.screen.remove_dock(window_id);

        // The viewport may have changed.
        let viewport = self.viewport();
        self.group_mut().update_viewport(viewport);
    }

    pub fn run(mut self) {
        info!("Started WM, entering event loop.");
        let event_loop_connection = self.connection.clone();
        let event_loop = event_loop_connection.get_event_loop();
        for event in event_loop {
            match event {
                Event::MapRequest(window_id) => self.on_map_request(window_id),
                Event::UnmapNotify(window_id) => self.on_unmap_notify(&window_id),
                Event::DestroyNotify(window_id) => self.on_destroy_notify(&window_id),
                Event::KeyPress(key) => self.on_key_press(key),
                Event::EnterNotify(window_id) => self.on_enter_notify(&window_id),
            }
        }
        info!("Event loop exiting");
    }

    fn on_map_request(&mut self, window_id: WindowId) {
        if !self.is_window_managed(&window_id) {
            // If the window isn't in any group, then add it to the current group.
            // (This will have the side-effect of mapping the window, as new windows are focused
            // and focused windows are mapped).
            self.manage_window(window_id);
        } else if self.group().contains(&window_id) {
            // Otherwise, if the window is in the active group, focus it. The application probably
            // wants us to make it prominent. Log as there may be misbehaving applications that
            // constantly re-map windows and cause focus issues.
            info!(
                "Window {} asked to be mapped but is already mapped: focusing.",
                window_id
            );
            self.group_mut().focus(&window_id);
        }
    }

    fn on_unmap_notify(&mut self, window_id: &WindowId) {
        // We only receive an unmap notify event when the window is actually
        // unmapped by its application. When our layouts unmap windows, they
        // (should) do it by disabling event tracking first.
        self.unmanage_window(window_id);
    }

    fn on_destroy_notify(&mut self, window_id: &WindowId) {
        self.unmanage_window(window_id);
    }

    fn on_key_press(&mut self, key: KeyCombo) {
        self.keys.get(&key).map(move |handler| {
            if let Err(error) = (handler)(self) {
                error!(
                    "Error running command for key command {:?}: {}",
                    key,
                    error.display_chain().to_string()
                );
            }
        });
    }

    fn on_enter_notify(&mut self, window_id: &WindowId) {
        self.group_mut().focus(window_id);
    }
}
<|endoftext|>


<commit_before>/*
 * Copyright 2017 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

//! # Rust shell - shell script written in rust.
//!
//! This is not an officially supported Google product
//!
//! Rust shell is a helper library for std::process::Command to write shell
//! script like tasks in rust. The library only works with unix-like operation
//! systems.
//!
//! ## Run command
//!
//! `run!` macro creates a ShellCommand instance which you can run by `run()`
//! method.
//!
//! ```
//! #[macro_use] extern crate shell;
//!
//! # fn main() {
//! // Run command by cmd! macro
//! cmd!("echo Hello rust shell!").run().unwrap();
//!
//! // Contain white space or non-alphabetical characters
//! cmd!("echo \"%$#\"").run().unwrap();
//!
//! // Pass an argument
//! let name = "shell";
//! cmd!("echo Hello rust {}!", name).run().unwrap();
//!
//! // Extract environment variable
//! cmd!("echo HOME is $HOME").run().unwrap();
//! # }
//! ```
//! ## ShellResult
//!
//! The return value of `ShellCommand#run()` is `ShellResult` which is `Ok(_)`
//! only when the command successfully runs and its execution code is 0, so you
//! can use `?` operator to check if the command successfully exits or not.
//!
//! ```
//! #[macro_use] extern crate shell;
//! use shell::ShellResult;
//!
//! # fn main() {
//! #   shell_function().unwrap();
//! # }
//! fn shell_function() -> ShellResult {
//!   cmd!("echo Command A").run()?;
//!   cmd!("echo Command B").run()
//! }
//! ```
//!
//! ## Output string
//!
//! ShellCommand has a shorthand to obtain stdout as UTF8 string.
//!
//! ```
//! #[macro_use] extern crate shell;
//!
//! # fn main() {
//! assert_eq!(cmd!("echo OK").stdout_utf8().unwrap(), "OK\n");
//! # }
//! ```
//!
//! ## Spawn
//!
//! ShellCommand has `spawn()` method which runs the command asynchronously and
//! returns `ShellChild`.
//!
//! ```
//! #[macro_use] extern crate shell;
//! extern crate libc;
//! use shell::ShellResultExt;
//!
//! # fn main() {
//! // Wait
//! let child = cmd!("sleep 2").spawn().unwrap();
//! child.wait().unwrap();
//!
//! // Signal
//! let child = cmd!("sleep 2").spawn().unwrap();
//! child.signal(libc::SIGINT);
//! let result = child.wait();
//! assert!(result.is_err(), "Should be error as it exits with a signal");
//! assert!(result.status().is_ok(), "Still able to obtain status");
//! # }
//! ```
//!
//! ## Thread
//!
//! If you would like to run a sequence of commands asynchronously,
//! `shell::spawn` creates a thread as well as `std::thread::spawn` but it
//! returns `ShellHandle` wrapping `std::thread::JoinHandle`.
//!
//! `ShellHandle#signal()` is used to send a signal to processes running on the
//! thread.  It also stops launching a new process by `ShellComamnd::run()` on
//! that thread.
//!
//! ```
//! #[macro_use] extern crate shell;
//! extern crate libc;
//! use shell::ShellResult;
//! use shell::ShellResultExt;
//!
//! # fn main() {
//! let handle = shell::spawn(|| -> ShellResult {
//!   cmd!("sleep 3").run()
//! });
//! handle.signal(libc::SIGINT);
//! let result = handle.join().unwrap();
//! assert!(result.is_err(), "Should be error as it exits with a signal");
//! assert!(result.status().is_ok(), "Still able to obtain status");
//! # }
//! ```
//!
//! ## Signal handling
//!
//! `trap_signal_and_wait_children()` starts watching SIGINT and SIGTERM, and
//! waits all child processes before exiting the process when receiving these
//! signals. The function needs to be called before launching any new thread.
//!
//! ```
//! extern crate shell;
//! shell::trap_signal_and_wait_children().unwrap();
//! ```
//!
//! ## Access underlaying objects
//!
//! `ShellComamnd` wraps `std::process::Command` and `ShellChild` wraps
//! `std::process::Child`. Both underlaying objects are accessible via public
//! fields.
//!
//! ```
//! #[macro_use] extern crate shell;
//! use std::process::Stdio;
//! use std::io::Read;
//!
//! # fn main() {
//! // Access std::process::Command.
//! let mut shell_command = cmd!("echo OK");
//! {
//!   let mut command = &mut shell_command.command;
//!   command.stdout(Stdio::piped());
//! }
//!
//! // Access std::process::Child.
//! let shell_child = shell_command.spawn().unwrap();
//! {
//!   let mut lock = shell_child.0.write().unwrap();
//!   let mut child = &mut lock.as_mut().unwrap().child;
//!   let mut str = String::new();
//!   child.stdout.as_mut().unwrap().read_to_string(&mut str);
//! }
//! shell_child.wait().unwrap();
//! # }
//! ```
//!
//! ## License
//! Apatch 2 License

#[macro_use] extern crate lazy_static;
#[macro_use] extern crate log;
#[macro_use] extern crate nom;
extern crate errno;
extern crate libc;
extern crate regex;
extern crate env_logger;

#[macro_use] mod command;
mod shell_child;
mod shell_command;
mod process_manager;
mod local_shell;
mod result;

pub use command::new_command;
pub use local_shell::ShellHandle;
pub use local_shell::spawn;
pub use process_manager::trap_signal_and_wait_children;
pub use result::ShellError;
pub use result::ShellResult;
pub use result::ShellResultExt;
pub use shell_child::ShellChild;
pub use shell_child::ShellChildArc;
pub use shell_child::ShellChildCore;
pub use shell_command::ShellCommand;
<commit_msg>Change licence header format<commit_after>// Copyright 2017 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//! # Rust shell - shell script written in rust.
//!
//! This is not an officially supported Google product
//!
//! Rust shell is a helper library for std::process::Command to write shell
//! script like tasks in rust. The library only works with unix-like operation
//! systems.
//!
//! ## Run command
//!
//! `run!` macro creates a ShellCommand instance which you can run by `run()`
//! method.
//!
//! ```
//! #[macro_use] extern crate shell;
//!
//! # fn main() {
//! // Run command by cmd! macro
//! cmd!("echo Hello rust shell!").run().unwrap();
//!
//! // Contain white space or non-alphabetical characters
//! cmd!("echo \"%$#\"").run().unwrap();
//!
//! // Pass an argument
//! let name = "shell";
//! cmd!("echo Hello rust {}!", name).run().unwrap();
//!
//! // Extract environment variable
//! cmd!("echo HOME is $HOME").run().unwrap();
//! # }
//! ```
//! ## ShellResult
//!
//! The return value of `ShellCommand#run()` is `ShellResult` which is `Ok(_)`
//! only when the command successfully runs and its execution code is 0, so you
//! can use `?` operator to check if the command successfully exits or not.
//!
//! ```
//! #[macro_use] extern crate shell;
//! use shell::ShellResult;
//!
//! # fn main() {
//! #   shell_function().unwrap();
//! # }
//! fn shell_function() -> ShellResult {
//!   cmd!("echo Command A").run()?;
//!   cmd!("echo Command B").run()
//! }
//! ```
//!
//! ## Output string
//!
//! ShellCommand has a shorthand to obtain stdout as UTF8 string.
//!
//! ```
//! #[macro_use] extern crate shell;
//!
//! # fn main() {
//! assert_eq!(cmd!("echo OK").stdout_utf8().unwrap(), "OK\n");
//! # }
//! ```
//!
//! ## Spawn
//!
//! ShellCommand has `spawn()` method which runs the command asynchronously and
//! returns `ShellChild`.
//!
//! ```
//! #[macro_use] extern crate shell;
//! extern crate libc;
//! use shell::ShellResultExt;
//!
//! # fn main() {
//! // Wait
//! let child = cmd!("sleep 2").spawn().unwrap();
//! child.wait().unwrap();
//!
//! // Signal
//! let child = cmd!("sleep 2").spawn().unwrap();
//! child.signal(libc::SIGINT);
//! let result = child.wait();
//! assert!(result.is_err(), "Should be error as it exits with a signal");
//! assert!(result.status().is_ok(), "Still able to obtain status");
//! # }
//! ```
//!
//! ## Thread
//!
//! If you would like to run a sequence of commands asynchronously,
//! `shell::spawn` creates a thread as well as `std::thread::spawn` but it
//! returns `ShellHandle` wrapping `std::thread::JoinHandle`.
//!
//! `ShellHandle#signal()` is used to send a signal to processes running on the
//! thread.  It also stops launching a new process by `ShellComamnd::run()` on
//! that thread.
//!
//! ```
//! #[macro_use] extern crate shell;
//! extern crate libc;
//! use shell::ShellResult;
//! use shell::ShellResultExt;
//!
//! # fn main() {
//! let handle = shell::spawn(|| -> ShellResult {
//!   cmd!("sleep 3").run()
//! });
//! handle.signal(libc::SIGINT);
//! let result = handle.join().unwrap();
//! assert!(result.is_err(), "Should be error as it exits with a signal");
//! assert!(result.status().is_ok(), "Still able to obtain status");
//! # }
//! ```
//!
//! ## Signal handling
//!
//! `trap_signal_and_wait_children()` starts watching SIGINT and SIGTERM, and
//! waits all child processes before exiting the process when receiving these
//! signals. The function needs to be called before launching any new thread.
//!
//! ```
//! extern crate shell;
//! shell::trap_signal_and_wait_children().unwrap();
//! ```
//!
//! ## Access underlaying objects
//!
//! `ShellComamnd` wraps `std::process::Command` and `ShellChild` wraps
//! `std::process::Child`. Both underlaying objects are accessible via public
//! fields.
//!
//! ```
//! #[macro_use] extern crate shell;
//! use std::process::Stdio;
//! use std::io::Read;
//!
//! # fn main() {
//! // Access std::process::Command.
//! let mut shell_command = cmd!("echo OK");
//! {
//!   let mut command = &mut shell_command.command;
//!   command.stdout(Stdio::piped());
//! }
//!
//! // Access std::process::Child.
//! let shell_child = shell_command.spawn().unwrap();
//! {
//!   let mut lock = shell_child.0.write().unwrap();
//!   let mut child = &mut lock.as_mut().unwrap().child;
//!   let mut str = String::new();
//!   child.stdout.as_mut().unwrap().read_to_string(&mut str);
//! }
//! shell_child.wait().unwrap();
//! # }
//! ```
//!
//! ## License
//! Apatch 2 License

#[macro_use] extern crate lazy_static;
#[macro_use] extern crate log;
#[macro_use] extern crate nom;
extern crate errno;
extern crate libc;
extern crate regex;
extern crate env_logger;

#[macro_use] mod command;
mod shell_child;
mod shell_command;
mod process_manager;
mod local_shell;
mod result;

pub use command::new_command;
pub use local_shell::ShellHandle;
pub use local_shell::spawn;
pub use process_manager::trap_signal_and_wait_children;
pub use result::ShellError;
pub use result::ShellResult;
pub use result::ShellResultExt;
pub use shell_child::ShellChild;
pub use shell_child::ShellChildArc;
pub use shell_child::ShellChildCore;
pub use shell_command::ShellCommand;
<|endoftext|>