<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) }
    }

    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) }
    }

    /// 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 derwin() and subwin()<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(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() }
}
<|endoftext|>


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

use std::collections::HashMap;
use futures::Stream;

struct Statement(Node, Entity, Node, Entity);

struct Rule(Node, Entity, Node);

enum Node {
    Entity(Entity),
    Literal(Literal)
}

struct Entity {
    identifier: String
}

impl Entity {
    fn a() -> Entity {
        Entity { identifier: String::from("_a") }
    }
    fn default() -> Entity {
        Entity { identifier: String::from("_") }
    }
}

enum Literal {
    StringLiteral(String),
    LangLiteral(LangLiteral),
    BooleanLiteral(bool),
    LongLiteral(i64),
    DoubleLiteral(f64),
    ListLiteral,
    BagLiteral,
    AltLiteral
}

enum Range {
    LangLiteralRange(LangLiteral, LangLiteral),
    StringLiteralRange(String, String),
    LongLiteralRange(u64, u64),
    DoubleLiteralRange(f64, f64)
}

struct LangLiteral {
    value: String,
    tag: String
}

impl LangLiteral {
    fn is_valid(&self) -> bool {
        //return "[a-zA-Z]+(-[a-zA-Z0-9]+)*".toRegex().matches(langTag)
        unimplemented!()
    }
}

impl Entity {
    fn is_valid(&self) -> bool {
        //return "[a-zA-Z_][^\\s\\(\\)\\[\\]\\{\\}'\"`<>\\\\]*".toRegex().matches(identifier)
        unimplemented!()
    }
}

trait LigatureStore {
    /**
     * Returns a collection based on the name passed.
     * Calling this function will not create a new collection, it just binds a Store and Collection name.
     */
    fn collection(&self, collection_name: Entity) -> dyn LigatureCollection;

    /**
     * Creates a new collection or does nothing if collection already exists.
     * Regardless the collection is returned.
     */
    fn create_collection(&self, collection_name: Entity) -> dyn LigatureCollection;

    /**
     * Deletes the collection of the name given and does nothing if the collection doesn't exist.
     */
    fn delete_collection(&self, collection_name: Entity);

    /**
     * Returns a Iterator of all existing collections.
     */
    fn all_collections(&self) -> dyn Stream<Item = dyn LigatureCollection>;

    /**
     * Close connection with the Store.
     */
    fn close(&self);

    /**
     * Returns an implementation specific map of details about this Store useful for debugging.
     */
    fn details(&self) -> HashMap<String, String>;
}

/**
 * Manages a collection of Statements and Rules, supports ontologies, and querying.
 */
trait LigatureCollection {
    fn collection_name(&self) -> Entity;
    fn read_tx(&self) -> dyn ReadTx;
    fn write_tx(&self) -> dyn WriteTx;
}

trait ReadTx {
    /**
     * Accepts nothing but returns a Stream of all Statements in the Collection.
     */
    fn all_statements(&self) -> dyn Stream<Item = Statement>;

    /**
     * Is passed a pattern and returns a seq with all matching Statements.
     */
    //fn matchStatements(subject: Node? = null, predicate: Entity? = null, `object`: Node? = null, graph: Entity? = null) -> Stream<Statement>

    /**
     * Is passed a pattern and returns a seq with all matching Statements.
     */
    //fn matchStatements(subject: Node? = null, predicate: Entity? = null, range: Range<*>, graph: Entity? = null) -> Stream<Statement>

    /**
     * Accepts nothing but returns a seq of all Rules in the Collection.
     */
    fn all_rules(&self) -> dyn Stream<Item = Rule>;

    /**
     * Is passed a pattern and returns a seq with all matching rules.
     */
    //fn matchRules(subject: Node? = null, predicate: Entity? = null, `object`: Node? = null) -> Stream<Rule>

    /**
     * Cancels this transaction.
     */
    fn cancel(&self);
}

trait WriteTx: ReadTx {
    /**
     * Returns a new, unique to this collection identifier in the form _:NUMBER"
     */
    fn new_entity(&self) -> Entity;
    fn add_statement(&self, statement: Statement);
    fn remove_statement(&self, statement: Statement);
    fn add_rule(&self, rule: Rule);
    fn remove_rule(&self, rule: Rule);
    fn commit(&self);
}
<commit_msg>adding match methods back to trait<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use std::collections::HashMap;
use futures::Stream;

struct Statement(Node, Entity, Node, Entity);

struct Rule(Node, Entity, Node);

enum Node {
    Entity(Entity),
    Literal(Literal)
}

struct Entity {
    identifier: String
}

impl Entity {
    fn a() -> Entity {
        Entity { identifier: String::from("_a") }
    }
    fn default() -> Entity {
        Entity { identifier: String::from("_") }
    }
}

enum Literal {
    StringLiteral(String),
    LangLiteral(LangLiteral),
    BooleanLiteral(bool),
    LongLiteral(i64),
    DoubleLiteral(f64),
    ListLiteral,
    BagLiteral,
    AltLiteral
}

enum Range {
    LangLiteralRange(LangLiteral, LangLiteral),
    StringLiteralRange(String, String),
    LongLiteralRange(u64, u64),
    DoubleLiteralRange(f64, f64)
}

struct LangLiteral {
    value: String,
    tag: String
}

impl LangLiteral {
    fn is_valid(&self) -> bool {
        //return "[a-zA-Z]+(-[a-zA-Z0-9]+)*".toRegex().matches(langTag)
        unimplemented!()
    }
}

impl Entity {
    fn is_valid(&self) -> bool {
        //return "[a-zA-Z_][^\\s\\(\\)\\[\\]\\{\\}'\"`<>\\\\]*".toRegex().matches(identifier)
        unimplemented!()
    }
}

trait LigatureStore {
    /**
     * Returns a collection based on the name passed.
     * Calling this function will not create a new collection, it just binds a Store and Collection name.
     */
    fn collection(&self, collection_name: Entity) -> dyn LigatureCollection;

    /**
     * Creates a new collection or does nothing if collection already exists.
     * Regardless the collection is returned.
     */
    fn create_collection(&self, collection_name: Entity) -> dyn LigatureCollection;

    /**
     * Deletes the collection of the name given and does nothing if the collection doesn't exist.
     */
    fn delete_collection(&self, collection_name: Entity);

    /**
     * Returns a Iterator of all existing collections.
     */
    fn all_collections(&self) -> dyn Stream<Item = dyn LigatureCollection>;

    /**
     * Close connection with the Store.
     */
    fn close(&self);

    /**
     * Returns an implementation specific map of details about this Store useful for debugging.
     */
    fn details(&self) -> HashMap<String, String>;
}

/**
 * Manages a collection of Statements and Rules, supports ontologies, and querying.
 */
trait LigatureCollection {
    fn collection_name(&self) -> Entity;
    fn read_tx(&self) -> dyn ReadTx;
    fn write_tx(&self) -> dyn WriteTx;
}

trait ReadTx {
    /**
     * Accepts nothing but returns a Stream of all Statements in the Collection.
     */
    fn all_statements(&self) -> dyn Stream<Item = Statement>;

    /**
     * Is passed a pattern and returns a seq with all matching Statements.
     */
    fn match_statements(subject: Option<Node>, predicate: Option<Entity>, object: Option<Node>, graph: Optional<Entity>) -> Stream<Item = Statement>;

    /**
     * Is passed a pattern and returns a seq with all matching Statements.
     */
    fn match_range(subject: Option<Node>, predicate: Option<Entity>, range: Range, graph: Option<Entity>) -> Stream<Item = Statement>;

    /**
     * Accepts nothing but returns a seq of all Rules in the Collection.
     */
    fn all_rules(&self) -> dyn Stream<Item = Rule>;

    /**
     * Is passed a pattern and returns a seq with all matching rules.
     */
    fn match_rules(subject: Option<Node>, predicate: Option<Entity>, object: Option<Node>) -> Stream<Item = Rule>;

    /**
     * Cancels this transaction.
     */
    fn cancel(&self);
}

trait WriteTx: ReadTx {
    /**
     * Returns a new, unique to this collection identifier in the form _:NUMBER"
     */
    fn new_entity(&self) -> Entity;
    fn add_statement(&self, statement: Statement);
    fn remove_statement(&self, statement: Statement);
    fn add_rule(&self, rule: Rule);
    fn remove_rule(&self, rule: Rule);
    fn commit(&self);
}
<|endoftext|>


<commit_before>#![feature(libc)]
#![feature(std_misc)]

extern crate libc;

use libc::c_void;
use std::ffi::CString;
use std::ptr;

pub mod ffi;
pub use ffi::{IPaddress, _TCPsocket, _SDLNet_SocketSet, _SDLNet_GenericSocket};

pub struct TCPsocket {
    opaque_ptr: *const _TCPsocket,
}
pub struct SocketSet {
    opaque_ptr: *const _SDLNet_SocketSet,
}
#[repr(C)]
pub struct CustTCPSocket {
    ready: i32,
}

static EMPTY_SOCKET_SET: SocketSet = SocketSet {opaque_ptr: 0 as *const _SDLNet_SocketSet};
static EMPTY_TCPSOCKET: TCPsocket = TCPsocket {opaque_ptr: 0 as *const _TCPsocket};

pub fn init() -> bool { unsafe { ffi::SDLNet_Init() == 0 } }
pub fn quit() -> () { unsafe { ffi::SDLNet_Quit(); } }
pub fn get_error() -> String {
    unsafe {
        let raw = &ffi::SDLNet_GetError();
        std::str::from_utf8(std::ffi::c_str_to_bytes(raw)).unwrap().to_string()
    }
}

// Returns an IPaddress struct if successful in becoming a host on the given port
pub fn become_host(port: u16) -> Option<ffi::IPaddress> {
    let mut address = ffi::IPaddress { host: 0, port: 0};
    let result = unsafe { ffi::SDLNet_ResolveHost(&mut address, ptr::null() , port) };
    if result == 0 {
        Some(address)
    } else {
        None
    }
}

// Resolves the host from the given IP and port
pub fn resolve_host(host: &str, port: u16) -> Option<ffi::IPaddress> {
    let mut address = ffi::IPaddress { host: 0, port: 0 };
    let result = unsafe { ffi::SDLNet_ResolveHost(&mut address, CString::from_slice(host.as_bytes()).as_ptr(), port) };
    if result == 0 {
        Some(address)
    } else {
        None
    }
}

// Resolves the given IP from the given IPaddress struct
pub fn resolve_ip(mut address: IPaddress) -> String {
    unsafe {
        let raw = &ffi::SDLNet_ResolveIP(&mut address);
        std::str::from_utf8(std::ffi::c_str_to_bytes(raw)).unwrap().to_string()
    }
}

// Returns a TCPsocket from the data given in the IPaddress object
pub fn tcp_open(address: &mut IPaddress) -> Option<TCPsocket> {
    unsafe {
        let socket = ffi::SDLNet_TCP_Open(address);
        if socket as *const _TCPsocket != ptr::null() {
            Some(TCPsocket { opaque_ptr: socket })
        } else {
            None
        }
    }
}

// Closes the given TCPsocket
pub fn tcp_close(sock: &TCPsocket) -> () {
    unsafe {
        ffi::SDLNet_TCP_Close(sock.opaque_ptr)
    }
}

// Accepts a TCP connection on the given socket, returning the new socket of the connection
pub fn tcp_accept(server: &TCPsocket) -> Option<TCPsocket> {
    unsafe {
        let socket =  ffi::SDLNet_TCP_Accept(server.opaque_ptr);
        if socket as *const _TCPsocket != ptr::null() {
            Some(TCPsocket { opaque_ptr: socket})
        } else {
            None
        }
    }
}

pub fn tcp_get_peer_address(sock: &TCPsocket) -> Option<IPaddress> {
    unsafe {
        let possible_addr = ffi::SDLNet_TCP_GetPeerAddress(sock.opaque_ptr);
        if possible_addr as *const IPaddress == ptr::null() {
            None
        } else {
            Some(ptr::read(possible_addr))
        }
    }
}

// Writes the data out
pub fn tcp_send(sock: &TCPsocket, data: &mut [u8]) -> i32 {
    unsafe {
        ffi::SDLNet_TCP_Send(sock.opaque_ptr, &data[0] as *const u8 as *const c_void, data.len() as i32)
    }
}

// Receives incoming data
pub fn tcp_recv(sock: &TCPsocket, data: &mut [u8]) -> i32 {
    unsafe {
        ffi::SDLNet_TCP_Recv(sock.opaque_ptr, &mut data[0] as *mut u8 as *mut c_void, data.len() as i32)
    }
}

// Allocates a socket set to hold the given number of sockets
pub fn alloc_socket_set(maxsockets: i32) -> SocketSet {
    unsafe {
        SocketSet { opaque_ptr: ffi::SDLNet_AllocSocketSet(maxsockets) }
    }
}

// Frees the given socket set
pub fn free_socket_set(set: &SocketSet) -> () {
    unsafe {
        ffi::SDLNet_FreeSocketSet(set.opaque_ptr);
    }
}

// Adds a given socket to the given socket set
pub fn add_socket(set: &SocketSet, sock: &TCPsocket) -> i32 {
    unsafe {
        ffi::SDLNet_AddSocket(set.opaque_ptr, sock.opaque_ptr)
    }
}

// Maybe should take in the box here as it may get deleted.... not sure
pub fn del_socket(set: &SocketSet, sock: &TCPsocket) -> i32 {
    unsafe {
        ffi::SDLNet_DelSocket(set.opaque_ptr, sock.opaque_ptr)
    }
}

// Returns the incoming data amount  on the given socketset
pub fn check_sockets(set: &SocketSet, timeout: u32) -> i32 {
    unsafe {
        ffi::SDLNet_CheckSockets(set.opaque_ptr, timeout)
    }
}

pub fn socket_ready(sock: &TCPsocket) -> bool {
    unsafe {
        (std::mem::transmute::<*const ffi::_TCPsocket,&CustTCPSocket>(sock.opaque_ptr)).ready != 0
    }
}
<commit_msg>Made the empty socket set and socket public.<commit_after>#![feature(libc)]
#![feature(std_misc)]

extern crate libc;

use libc::c_void;
use std::ffi::CString;
use std::ptr;

pub mod ffi;
pub use ffi::{IPaddress, _TCPsocket, _SDLNet_SocketSet, _SDLNet_GenericSocket};

pub struct TCPsocket {
    opaque_ptr: *const _TCPsocket,
}
pub struct SocketSet {
    opaque_ptr: *const _SDLNet_SocketSet,
}
#[repr(C)]
pub struct CustTCPSocket {
    ready: i32,
}

pub static EMPTY_SOCKET_SET: SocketSet = SocketSet {opaque_ptr: 0 as *const _SDLNet_SocketSet};
pub static EMPTY_TCPSOCKET: TCPsocket = TCPsocket {opaque_ptr: 0 as *const _TCPsocket};

pub fn init() -> bool { unsafe { ffi::SDLNet_Init() == 0 } }
pub fn quit() -> () { unsafe { ffi::SDLNet_Quit(); } }
pub fn get_error() -> String {
    unsafe {
        let raw = &ffi::SDLNet_GetError();
        std::str::from_utf8(std::ffi::c_str_to_bytes(raw)).unwrap().to_string()
    }
}

// Returns an IPaddress struct if successful in becoming a host on the given port
pub fn become_host(port: u16) -> Option<ffi::IPaddress> {
    let mut address = ffi::IPaddress { host: 0, port: 0};
    let result = unsafe { ffi::SDLNet_ResolveHost(&mut address, ptr::null() , port) };
    if result == 0 {
        Some(address)
    } else {
        None
    }
}

// Resolves the host from the given IP and port
pub fn resolve_host(host: &str, port: u16) -> Option<ffi::IPaddress> {
    let mut address = ffi::IPaddress { host: 0, port: 0 };
    let result = unsafe { ffi::SDLNet_ResolveHost(&mut address, CString::from_slice(host.as_bytes()).as_ptr(), port) };
    if result == 0 {
        Some(address)
    } else {
        None
    }
}

// Resolves the given IP from the given IPaddress struct
pub fn resolve_ip(mut address: IPaddress) -> String {
    unsafe {
        let raw = &ffi::SDLNet_ResolveIP(&mut address);
        std::str::from_utf8(std::ffi::c_str_to_bytes(raw)).unwrap().to_string()
    }
}

// Returns a TCPsocket from the data given in the IPaddress object
pub fn tcp_open(address: &mut IPaddress) -> Option<TCPsocket> {
    unsafe {
        let socket = ffi::SDLNet_TCP_Open(address);
        if socket as *const _TCPsocket != ptr::null() {
            Some(TCPsocket { opaque_ptr: socket })
        } else {
            None
        }
    }
}

// Closes the given TCPsocket
pub fn tcp_close(sock: &TCPsocket) -> () {
    unsafe {
        ffi::SDLNet_TCP_Close(sock.opaque_ptr)
    }
}

// Accepts a TCP connection on the given socket, returning the new socket of the connection
pub fn tcp_accept(server: &TCPsocket) -> Option<TCPsocket> {
    unsafe {
        let socket =  ffi::SDLNet_TCP_Accept(server.opaque_ptr);
        if socket as *const _TCPsocket != ptr::null() {
            Some(TCPsocket { opaque_ptr: socket})
        } else {
            None
        }
    }
}

pub fn tcp_get_peer_address(sock: &TCPsocket) -> Option<IPaddress> {
    unsafe {
        let possible_addr = ffi::SDLNet_TCP_GetPeerAddress(sock.opaque_ptr);
        if possible_addr as *const IPaddress == ptr::null() {
            None
        } else {
            Some(ptr::read(possible_addr))
        }
    }
}

// Writes the data out
pub fn tcp_send(sock: &TCPsocket, data: &mut [u8]) -> i32 {
    unsafe {
        ffi::SDLNet_TCP_Send(sock.opaque_ptr, &data[0] as *const u8 as *const c_void, data.len() as i32)
    }
}

// Receives incoming data
pub fn tcp_recv(sock: &TCPsocket, data: &mut [u8]) -> i32 {
    unsafe {
        ffi::SDLNet_TCP_Recv(sock.opaque_ptr, &mut data[0] as *mut u8 as *mut c_void, data.len() as i32)
    }
}

// Allocates a socket set to hold the given number of sockets
pub fn alloc_socket_set(maxsockets: i32) -> SocketSet {
    unsafe {
        SocketSet { opaque_ptr: ffi::SDLNet_AllocSocketSet(maxsockets) }
    }
}

// Frees the given socket set
pub fn free_socket_set(set: &SocketSet) -> () {
    unsafe {
        ffi::SDLNet_FreeSocketSet(set.opaque_ptr);
    }
}

// Adds a given socket to the given socket set
pub fn add_socket(set: &SocketSet, sock: &TCPsocket) -> i32 {
    unsafe {
        ffi::SDLNet_AddSocket(set.opaque_ptr, sock.opaque_ptr)
    }
}

// Maybe should take in the box here as it may get deleted.... not sure
pub fn del_socket(set: &SocketSet, sock: &TCPsocket) -> i32 {
    unsafe {
        ffi::SDLNet_DelSocket(set.opaque_ptr, sock.opaque_ptr)
    }
}

// Returns the incoming data amount  on the given socketset
pub fn check_sockets(set: &SocketSet, timeout: u32) -> i32 {
    unsafe {
        ffi::SDLNet_CheckSockets(set.opaque_ptr, timeout)
    }
}

pub fn socket_ready(sock: &TCPsocket) -> bool {
    unsafe {
        (std::mem::transmute::<*const ffi::_TCPsocket,&CustTCPSocket>(sock.opaque_ptr)).ready != 0
    }
}
<|endoftext|>


<commit_before>extern crate xml;

use std::io::prelude::*;
use std::str::FromStr;

use xml::reader::{EventReader, XmlEvent};
use xml::attribute::OwnedAttribute;

mod error;
use error::{Error, ErrorReason};

mod elements;
pub use elements::{Coordinate, Id, Role, Bounds, Node, Way, Relation, Member, Tag, Reference,
                   UnresolvedReference};
mod polygon;

#[derive(Debug)]
pub struct OSM {
    pub bounds: Option<Bounds>,
    pub nodes: Vec<Node>,
    pub ways: Vec<Way>,
    pub relations: Vec<Relation>,
}

impl OSM {
    fn empty() -> OSM {
        OSM {
            bounds: None,
            nodes: Vec::new(),
            ways: Vec::new(),
            relations: Vec::new(),
        }
    }

    pub fn parse<R: Read>(source: R) -> Result<OSM, Error> {

        let mut osm = OSM::empty();

        let mut parser = EventReader::new(source);

        loop {
            match parse_element_data(&mut parser) {
                Err(Error::XmlParseError(err)) => return Err(Error::XmlParseError(err)),
                Err(Error::BoundsMissing(_)) => osm.bounds = None,
                Err(Error::MalformedTag(_)) |
                Err(Error::MalformedNode(_)) |
                Err(Error::MalformedWay(_)) |
                Err(Error::MalformedRelation(_)) |
                Err(Error::UnknownElement) => continue,
                Ok(data) => {
                    match data {
                        ElementData::EndOfDocument => return Ok(osm),
                        ElementData::Ignored => continue,
                        ElementData::Bounds(minlat, minlon, maxlat, maxlon) => {
                            osm.bounds = Some(Bounds {
                                minlat: minlat,
                                minlon: minlon,
                                maxlat: maxlat,
                                maxlon: maxlon,
                            });
                        }
                        ElementData::Node(id, lat, lon, tags) => {
                            osm.nodes.push(Node {
                                id: id,
                                lat: lat,
                                lon: lon,
                                tags: tags,
                            });
                        }
                        ElementData::Way(id, node_refs, tags) => {
                            osm.ways.push(Way {
                                id: id,
                                nodes: node_refs,
                                tags: tags,
                            });
                        }
                        ElementData::Relation(relation) => {
                            osm.relations.push(relation);
                        }
                    }
                }
            }
        }
    }

    pub fn resolve_reference<'a>(&self, reference: &UnresolvedReference) -> Reference {
        match *reference {
            UnresolvedReference::Node(id) => {
                match self.nodes.iter().find(|node| node.id == id) {
                    Some(node) => Reference::Node(&node),
                    None => Reference::Unresolved,
                }
            }
            UnresolvedReference::Way(id) => {
                match self.ways.iter().find(|way| way.id == id) {
                    Some(way) => Reference::Way(&way),
                    None => Reference::Unresolved,
                }
            }
            UnresolvedReference::Relation(id) => {
                match self.relations.iter().find(|relation| relation.id == id) {
                    Some(relation) => Reference::Relation(&relation),
                    None => Reference::Unresolved,
                }
            }
        }
    }
}

enum ElementType {
    Bounds,
    Node,
    Way,
    Relation,
    Tag,
    NodeRef,
    Member,
}

enum ElementData {
    Bounds(Coordinate, Coordinate, Coordinate, Coordinate),
    Node(Id, Coordinate, Coordinate, Vec<Tag>),
    Way(Id, Vec<UnresolvedReference>, Vec<Tag>),
    Relation(Relation),
    // These two are here so we can terminate and skip uninteresting data without
    // using error handling.
    EndOfDocument,
    Ignored,
}

impl FromStr for ElementType {
    type Err = Error;

    fn from_str(s: &str) -> Result<ElementType, Error> {
        match s.to_lowercase().as_ref() {
            "bounds" => Ok(ElementType::Bounds),
            "node" => Ok(ElementType::Node),
            "way" => Ok(ElementType::Way),
            "relation" => Ok(ElementType::Relation),
            "tag" => Ok(ElementType::Tag),
            "nd" => Ok(ElementType::NodeRef),
            "member" => Ok(ElementType::Member),
            _ => Err(Error::UnknownElement),
        }
    }
}

fn parse_element_data<R: Read>(parser: &mut EventReader<R>) -> Result<ElementData, Error> {
    let element = try!(parser.next());
    match element {
        XmlEvent::EndDocument => Ok(ElementData::EndOfDocument),
        XmlEvent::StartElement { name, attributes, .. } => {
            let element_type = try!(ElementType::from_str(&name.local_name));

            match element_type {
                ElementType::Bounds => parse_bounds(&attributes),
                ElementType::Node => parse_node(parser, &attributes),
                ElementType::Way => parse_way(parser, &attributes),
                ElementType::Relation => parse_relation(parser, &attributes),
                _ => Err(Error::UnknownElement),
            }
        }
        _ => Ok(ElementData::Ignored),
    }
}

fn parse_relation<R: Read>(parser: &mut EventReader<R>,
                  attrs: &Vec<OwnedAttribute>)
                  -> Result<ElementData, Error> {
    let id = try!(find_attribute("id", attrs).map_err(Error::MalformedRelation));

    let mut members = Vec::new();
    let mut tags = Vec::new();

    loop {
        match try!(parser.next()) {
            XmlEvent::EndElement { name } => {
                let element_type = try!(ElementType::from_str(&name.local_name));

                match element_type {
                    ElementType::Relation => {
                        return Ok(ElementData::Relation(Relation {
                            id: id,
                            members: members,
                            tags: tags,
                        }))
                    }
                    _ => continue,
                }
            }
            XmlEvent::StartElement { name, attributes, .. } => {
                let element_type = try!(ElementType::from_str(&name.local_name));

                match element_type {
                    ElementType::Tag => {
                        if let Ok(tag) = parse_tag(&attributes) {
                            tags.push(tag);
                        }
                    }
                    ElementType::Member => {
                        let el_type = try!(find_attribute_uncasted("type", &attributes)
                            .map_err(Error::MalformedRelation));
                        let el_ref = try!(find_attribute("ref", &attributes)
                            .map_err(Error::MalformedRelation));
                        let el_role = try!(find_attribute_uncasted("role", &attributes)
                            .map_err(Error::MalformedRelation));

                        let el = match el_type.to_lowercase().as_ref() {
                            "node" => Member::Node(UnresolvedReference::Node(el_ref), el_role),
                            "way" => Member::Way(UnresolvedReference::Way(el_ref), el_role),
                            "relation" => {
                                Member::Relation(UnresolvedReference::Relation(el_ref), el_role)
                            }
                            _ => return Err(Error::MalformedRelation(ErrorReason::Missing)),
                        };

                        members.push(el);
                    }
                    ElementType::Bounds | ElementType::Node | ElementType::Relation |
                    ElementType::Way | ElementType::NodeRef => {
                        return Err(Error::MalformedRelation(ErrorReason::IllegalNesting))
                    }
                }
            }
            _ => continue,
        }
    }
}

fn parse_way<R: Read>(parser: &mut EventReader<R>,
             attrs: &Vec<OwnedAttribute>)
             -> Result<ElementData, Error> {
    let id = try!(find_attribute("id", attrs).map_err(Error::MalformedWay));

    let mut node_refs = Vec::new();
    let mut tags = Vec::new();

    loop {
        match try!(parser.next()) {
            XmlEvent::EndElement { name } => {
                let element_type = try!(ElementType::from_str(&name.local_name));

                match element_type {
                    ElementType::Way => return Ok(ElementData::Way(id, node_refs, tags)),
                    _ => continue,
                }
            }
            XmlEvent::StartElement { name, attributes, .. } => {
                let element_type = try!(ElementType::from_str(&name.local_name));

                match element_type {
                    ElementType::Tag => {
                        if let Ok(tag) = parse_tag(&attributes) {
                            tags.push(tag);
                        }
                    }
                    ElementType::NodeRef => {
                        let node_ref = try!(find_attribute("ref", &attributes)
                            .map_err(Error::MalformedWay));
                        node_refs.push(UnresolvedReference::Node(node_ref));
                    }
                    ElementType::Bounds | ElementType::Node | ElementType::Relation |
                    ElementType::Way | ElementType::Member => {
                        return Err(Error::MalformedWay(ErrorReason::IllegalNesting))
                    }
                }
            }
            _ => continue,
        }
    }

}

fn parse_node<R: Read>(parser: &mut EventReader<R>,
              attrs: &Vec<OwnedAttribute>)
              -> Result<ElementData, Error> {
    let id = try!(find_attribute("id", attrs).map_err(Error::MalformedNode));
    let lat = try!(find_attribute("lat", attrs).map_err(Error::MalformedNode));
    let lon = try!(find_attribute("lon", attrs).map_err(Error::MalformedNode));

    let mut tags = Vec::new();

    loop {
        match try!(parser.next()) {
            XmlEvent::EndElement { name } => {
                let element_type = try!(ElementType::from_str(&name.local_name));

                match element_type {
                    ElementType::Node => return Ok(ElementData::Node(id, lat, lon, tags)),
                    _ => continue,
                }
            }
            XmlEvent::StartElement { name, attributes, .. } => {
                let element_type = try!(ElementType::from_str(&name.local_name));

                match element_type {
                    ElementType::Tag => {
                        if let Ok(tag) = parse_tag(&attributes) {
                            tags.push(tag);
                        }
                    }
                    ElementType::Bounds | ElementType::Node | ElementType::Relation |
                    ElementType::Way | ElementType::NodeRef | ElementType::Member => {
                        return Err(Error::MalformedNode(ErrorReason::IllegalNesting))
                    }
                }
            }
            _ => continue,
        }
    }
}

fn parse_tag(attributes: &Vec<OwnedAttribute>) -> Result<Tag, Error> {
    let key = try!(find_attribute_uncasted("k", attributes).map_err(Error::MalformedTag));
    let val = try!(find_attribute_uncasted("v", attributes).map_err(Error::MalformedTag));
    Ok(Tag {
        key: key,
        val: val,
    })
}

fn parse_bounds(attrs: &Vec<OwnedAttribute>) -> Result<ElementData, Error> {
    let minlat = try!(find_attribute("minlat", attrs).map_err(Error::BoundsMissing));
    let minlon = try!(find_attribute("minlon", attrs).map_err(Error::BoundsMissing));
    let maxlat = try!(find_attribute("maxlat", attrs).map_err(Error::BoundsMissing));
    let maxlon = try!(find_attribute("maxlon", attrs).map_err(Error::BoundsMissing));

    Ok(ElementData::Bounds(minlat, minlon, maxlat, maxlon))
}

fn find_attribute<T>(name: &str, attrs: &Vec<OwnedAttribute>) -> Result<T, ErrorReason>
    where ErrorReason: std::convert::From<<T as std::str::FromStr>::Err>,
          T: FromStr
{
    let val_raw = try!(find_attribute_uncasted(name, attrs));
    let val = try!(val_raw.parse::<T>());
    Ok(val)
}

fn find_attribute_uncasted(name: &str, attrs: &Vec<OwnedAttribute>) -> Result<String, ErrorReason> {
    let attr = attrs.iter().find(|attr| attr.name.local_name == name);
    match attr {
        Some(a) => Ok(a.value.clone()),
        None => Err(ErrorReason::Missing),
    }
}
<commit_msg>Make error module public<commit_after>extern crate xml;

use std::io::prelude::*;
use std::str::FromStr;

use xml::reader::{EventReader, XmlEvent};
use xml::attribute::OwnedAttribute;

pub mod error;
use error::{Error, ErrorReason};

mod elements;
pub use elements::{Coordinate, Id, Role, Bounds, Node, Way, Relation, Member, Tag, Reference,
                   UnresolvedReference};
mod polygon;

#[derive(Debug)]
pub struct OSM {
    pub bounds: Option<Bounds>,
    pub nodes: Vec<Node>,
    pub ways: Vec<Way>,
    pub relations: Vec<Relation>,
}

impl OSM {
    fn empty() -> OSM {
        OSM {
            bounds: None,
            nodes: Vec::new(),
            ways: Vec::new(),
            relations: Vec::new(),
        }
    }

    pub fn parse<R: Read>(source: R) -> Result<OSM, Error> {

        let mut osm = OSM::empty();

        let mut parser = EventReader::new(source);

        loop {
            match parse_element_data(&mut parser) {
                Err(Error::XmlParseError(err)) => return Err(Error::XmlParseError(err)),
                Err(Error::BoundsMissing(_)) => osm.bounds = None,
                Err(Error::MalformedTag(_)) |
                Err(Error::MalformedNode(_)) |
                Err(Error::MalformedWay(_)) |
                Err(Error::MalformedRelation(_)) |
                Err(Error::UnknownElement) => continue,
                Ok(data) => {
                    match data {
                        ElementData::EndOfDocument => return Ok(osm),
                        ElementData::Ignored => continue,
                        ElementData::Bounds(minlat, minlon, maxlat, maxlon) => {
                            osm.bounds = Some(Bounds {
                                minlat: minlat,
                                minlon: minlon,
                                maxlat: maxlat,
                                maxlon: maxlon,
                            });
                        }
                        ElementData::Node(id, lat, lon, tags) => {
                            osm.nodes.push(Node {
                                id: id,
                                lat: lat,
                                lon: lon,
                                tags: tags,
                            });
                        }
                        ElementData::Way(id, node_refs, tags) => {
                            osm.ways.push(Way {
                                id: id,
                                nodes: node_refs,
                                tags: tags,
                            });
                        }
                        ElementData::Relation(relation) => {
                            osm.relations.push(relation);
                        }
                    }
                }
            }
        }
    }

    pub fn resolve_reference<'a>(&self, reference: &UnresolvedReference) -> Reference {
        match *reference {
            UnresolvedReference::Node(id) => {
                match self.nodes.iter().find(|node| node.id == id) {
                    Some(node) => Reference::Node(&node),
                    None => Reference::Unresolved,
                }
            }
            UnresolvedReference::Way(id) => {
                match self.ways.iter().find(|way| way.id == id) {
                    Some(way) => Reference::Way(&way),
                    None => Reference::Unresolved,
                }
            }
            UnresolvedReference::Relation(id) => {
                match self.relations.iter().find(|relation| relation.id == id) {
                    Some(relation) => Reference::Relation(&relation),
                    None => Reference::Unresolved,
                }
            }
        }
    }
}

enum ElementType {
    Bounds,
    Node,
    Way,
    Relation,
    Tag,
    NodeRef,
    Member,
}

enum ElementData {
    Bounds(Coordinate, Coordinate, Coordinate, Coordinate),
    Node(Id, Coordinate, Coordinate, Vec<Tag>),
    Way(Id, Vec<UnresolvedReference>, Vec<Tag>),
    Relation(Relation),
    // These two are here so we can terminate and skip uninteresting data without
    // using error handling.
    EndOfDocument,
    Ignored,
}

impl FromStr for ElementType {
    type Err = Error;

    fn from_str(s: &str) -> Result<ElementType, Error> {
        match s.to_lowercase().as_ref() {
            "bounds" => Ok(ElementType::Bounds),
            "node" => Ok(ElementType::Node),
            "way" => Ok(ElementType::Way),
            "relation" => Ok(ElementType::Relation),
            "tag" => Ok(ElementType::Tag),
            "nd" => Ok(ElementType::NodeRef),
            "member" => Ok(ElementType::Member),
            _ => Err(Error::UnknownElement),
        }
    }
}

fn parse_element_data<R: Read>(parser: &mut EventReader<R>) -> Result<ElementData, Error> {
    let element = try!(parser.next());
    match element {
        XmlEvent::EndDocument => Ok(ElementData::EndOfDocument),
        XmlEvent::StartElement { name, attributes, .. } => {
            let element_type = try!(ElementType::from_str(&name.local_name));

            match element_type {
                ElementType::Bounds => parse_bounds(&attributes),
                ElementType::Node => parse_node(parser, &attributes),
                ElementType::Way => parse_way(parser, &attributes),
                ElementType::Relation => parse_relation(parser, &attributes),
                _ => Err(Error::UnknownElement),
            }
        }
        _ => Ok(ElementData::Ignored),
    }
}

fn parse_relation<R: Read>(parser: &mut EventReader<R>,
                  attrs: &Vec<OwnedAttribute>)
                  -> Result<ElementData, Error> {
    let id = try!(find_attribute("id", attrs).map_err(Error::MalformedRelation));

    let mut members = Vec::new();
    let mut tags = Vec::new();

    loop {
        match try!(parser.next()) {
            XmlEvent::EndElement { name } => {
                let element_type = try!(ElementType::from_str(&name.local_name));

                match element_type {
                    ElementType::Relation => {
                        return Ok(ElementData::Relation(Relation {
                            id: id,
                            members: members,
                            tags: tags,
                        }))
                    }
                    _ => continue,
                }
            }
            XmlEvent::StartElement { name, attributes, .. } => {
                let element_type = try!(ElementType::from_str(&name.local_name));

                match element_type {
                    ElementType::Tag => {
                        if let Ok(tag) = parse_tag(&attributes) {
                            tags.push(tag);
                        }
                    }
                    ElementType::Member => {
                        let el_type = try!(find_attribute_uncasted("type", &attributes)
                            .map_err(Error::MalformedRelation));
                        let el_ref = try!(find_attribute("ref", &attributes)
                            .map_err(Error::MalformedRelation));
                        let el_role = try!(find_attribute_uncasted("role", &attributes)
                            .map_err(Error::MalformedRelation));

                        let el = match el_type.to_lowercase().as_ref() {
                            "node" => Member::Node(UnresolvedReference::Node(el_ref), el_role),
                            "way" => Member::Way(UnresolvedReference::Way(el_ref), el_role),
                            "relation" => {
                                Member::Relation(UnresolvedReference::Relation(el_ref), el_role)
                            }
                            _ => return Err(Error::MalformedRelation(ErrorReason::Missing)),
                        };

                        members.push(el);
                    }
                    ElementType::Bounds | ElementType::Node | ElementType::Relation |
                    ElementType::Way | ElementType::NodeRef => {
                        return Err(Error::MalformedRelation(ErrorReason::IllegalNesting))
                    }
                }
            }
            _ => continue,
        }
    }
}

fn parse_way<R: Read>(parser: &mut EventReader<R>,
             attrs: &Vec<OwnedAttribute>)
             -> Result<ElementData, Error> {
    let id = try!(find_attribute("id", attrs).map_err(Error::MalformedWay));

    let mut node_refs = Vec::new();
    let mut tags = Vec::new();

    loop {
        match try!(parser.next()) {
            XmlEvent::EndElement { name } => {
                let element_type = try!(ElementType::from_str(&name.local_name));

                match element_type {
                    ElementType::Way => return Ok(ElementData::Way(id, node_refs, tags)),
                    _ => continue,
                }
            }
            XmlEvent::StartElement { name, attributes, .. } => {
                let element_type = try!(ElementType::from_str(&name.local_name));

                match element_type {
                    ElementType::Tag => {
                        if let Ok(tag) = parse_tag(&attributes) {
                            tags.push(tag);
                        }
                    }
                    ElementType::NodeRef => {
                        let node_ref = try!(find_attribute("ref", &attributes)
                            .map_err(Error::MalformedWay));
                        node_refs.push(UnresolvedReference::Node(node_ref));
                    }
                    ElementType::Bounds | ElementType::Node | ElementType::Relation |
                    ElementType::Way | ElementType::Member => {
                        return Err(Error::MalformedWay(ErrorReason::IllegalNesting))
                    }
                }
            }
            _ => continue,
        }
    }

}

fn parse_node<R: Read>(parser: &mut EventReader<R>,
              attrs: &Vec<OwnedAttribute>)
              -> Result<ElementData, Error> {
    let id = try!(find_attribute("id", attrs).map_err(Error::MalformedNode));
    let lat = try!(find_attribute("lat", attrs).map_err(Error::MalformedNode));
    let lon = try!(find_attribute("lon", attrs).map_err(Error::MalformedNode));

    let mut tags = Vec::new();

    loop {
        match try!(parser.next()) {
            XmlEvent::EndElement { name } => {
                let element_type = try!(ElementType::from_str(&name.local_name));

                match element_type {
                    ElementType::Node => return Ok(ElementData::Node(id, lat, lon, tags)),
                    _ => continue,
                }
            }
            XmlEvent::StartElement { name, attributes, .. } => {
                let element_type = try!(ElementType::from_str(&name.local_name));

                match element_type {
                    ElementType::Tag => {
                        if let Ok(tag) = parse_tag(&attributes) {
                            tags.push(tag);
                        }
                    }
                    ElementType::Bounds | ElementType::Node | ElementType::Relation |
                    ElementType::Way | ElementType::NodeRef | ElementType::Member => {
                        return Err(Error::MalformedNode(ErrorReason::IllegalNesting))
                    }
                }
            }
            _ => continue,
        }
    }
}

fn parse_tag(attributes: &Vec<OwnedAttribute>) -> Result<Tag, Error> {
    let key = try!(find_attribute_uncasted("k", attributes).map_err(Error::MalformedTag));
    let val = try!(find_attribute_uncasted("v", attributes).map_err(Error::MalformedTag));
    Ok(Tag {
        key: key,
        val: val,
    })
}

fn parse_bounds(attrs: &Vec<OwnedAttribute>) -> Result<ElementData, Error> {
    let minlat = try!(find_attribute("minlat", attrs).map_err(Error::BoundsMissing));
    let minlon = try!(find_attribute("minlon", attrs).map_err(Error::BoundsMissing));
    let maxlat = try!(find_attribute("maxlat", attrs).map_err(Error::BoundsMissing));
    let maxlon = try!(find_attribute("maxlon", attrs).map_err(Error::BoundsMissing));

    Ok(ElementData::Bounds(minlat, minlon, maxlat, maxlon))
}

fn find_attribute<T>(name: &str, attrs: &Vec<OwnedAttribute>) -> Result<T, ErrorReason>
    where ErrorReason: std::convert::From<<T as std::str::FromStr>::Err>,
          T: FromStr
{
    let val_raw = try!(find_attribute_uncasted(name, attrs));
    let val = try!(val_raw.parse::<T>());
    Ok(val)
}

fn find_attribute_uncasted(name: &str, attrs: &Vec<OwnedAttribute>) -> Result<String, ErrorReason> {
    let attr = attrs.iter().find(|attr| attr.name.local_name == name);
    match attr {
        Some(a) => Ok(a.value.clone()),
        None => Err(ErrorReason::Missing),
    }
}
<|endoftext|>


<commit_before>// Copyright 2015  Emmanuele Bassi. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

#![crate_name = "taglib"]
#![crate_type = "lib"]

extern crate libc;
extern crate taglib_sys as sys;

use libc::c_char;
use std::ffi::{CString, CStr};
use std::path::Path;

use sys as ll;

fn c_str_to_str(c_str: *const c_char) -> Option<String> {
    if c_str.is_null() {
        None
    } else {
        let bytes = unsafe { CStr::from_ptr(c_str).to_bytes() };

        if bytes.is_empty() { None } else { Some(String::from_utf8_lossy(bytes).to_string()) }
    }
}

fn u32_to_option(n: u32) -> Option<u32> {
    if n == 0 { None } else { Some(n) }
}

/// A representation of an audio file, with meta-data and properties.
pub struct File {
    raw: *mut ll::TagLib_File,
}

/// The abstract meta-data container for audio files
///
/// Each `Tag` instance can only be created by the `taglib::File::tag()`
/// method.
#[allow(dead_code)]
pub struct Tag<'a> {
    raw: *mut ll::TagLib_Tag,
    file: &'a File,
}

/// Common audio file properties.
///
/// Instances of `AudioProperties` can only be created through the
/// `taglib::File::audioproperties()` method.
#[allow(dead_code)]
pub struct AudioProperties<'a> {
    raw: *const ll::TagLib_AudioProperties,
    file: &'a File,
}

impl<'a> Tag<'a> {
    /// Returns the track name, if any.
    pub fn title(&self) -> Option<String> {
        let res = unsafe { ll::taglib_tag_title(self.raw) };
        c_str_to_str(res)
    }

    /// Sets the track name.
    pub fn set_title(&mut self, title: &str) {
        let cs = CString::new(title).unwrap();
        let s = cs.as_ptr();
        unsafe {
            ll::taglib_tag_set_title(self.raw, s);
        }
    }

    /// Returns the artist name, if any.
    pub fn artist(&self) -> Option<String> {
        let res = unsafe { ll::taglib_tag_artist(self.raw) };
        c_str_to_str(res)
    }

    /// Sets the artist name.
    pub fn set_artist(&mut self, artist: &str) {
        let cs = CString::new(artist).unwrap();
        let s = cs.as_ptr();
        unsafe {
            ll::taglib_tag_set_artist(self.raw, s);
        }
    }

    /// Returns the album name, if any.
    pub fn album(&self) -> Option<String> {
        let res = unsafe { ll::taglib_tag_album(self.raw) };
        c_str_to_str(res)
    }

    /// Sets the album name.
    pub fn set_album(&mut self, album: &str) {
        let cs = CString::new(album).unwrap();
        let s = cs.as_ptr();
        unsafe {
            ll::taglib_tag_set_album(self.raw, s);
        }
    }

    /// Returns the track comment, if any.
    pub fn comment(&self) -> Option<String> {
        let res = unsafe { ll::taglib_tag_comment(self.raw) };
        c_str_to_str(res)
    }

    /// Sets the track comment.
    pub fn set_comment(&mut self, comment: &str) {
        let cs = CString::new(comment).unwrap();
        let s = cs.as_ptr();
        unsafe {
            ll::taglib_tag_set_comment(self.raw, s);
        }
    }

    /// Returns the genre name, if any.
    pub fn genre(&self) -> Option<String> {
        let res = unsafe { ll::taglib_tag_genre(self.raw) };
        c_str_to_str(res)
    }

    /// Sets the genre name.
    pub fn set_genre(&mut self, genre: &str) {
        let cs = CString::new(genre).unwrap();
        let s = cs.as_ptr();
        unsafe {
            ll::taglib_tag_set_genre(self.raw, s);
        }
    }

    /// Returns the year, if any.
    pub fn year(&self) -> Option<u32> {
        u32_to_option(unsafe { ll::taglib_tag_year(self.raw) as u32 })
    }

    /// Sets the year.
    pub fn set_year(&mut self, year: u32) {
        unsafe {
            ll::taglib_tag_set_year(self.raw, year);
        }
    }

    /// Returns the track number, if any.
    pub fn track(&self) -> Option<u32> {
        u32_to_option(unsafe { ll::taglib_tag_track(self.raw) as u32 })
    }

    /// Sets the track number.
    pub fn set_track(&mut self, track: u32) {
        unsafe {
            ll::taglib_tag_set_track(self.raw, track);
        }
    }
}

impl<'a> AudioProperties<'a> {
    /// Returns the length, in seconds, of the track.
    pub fn length(&self) -> u32 {
        unsafe { ll::taglib_audioproperties_length(self.raw) as u32 }
    }

    /// Returns the most appropriate bit rate for the track, in kB/s.
    /// For constant bit rate formats, the returned value is the bit
    /// rate of the file; for variable bit rate formats this is either
    /// the average or the nominal bit rate.
    pub fn bitrate(&self) -> u32 {
        unsafe { ll::taglib_audioproperties_bitrate(self.raw) as u32 }
    }

    /// Returns the sample rate, in Hz.
    pub fn samplerate(&self) -> u32 {
        unsafe { ll::taglib_audioproperties_samplerate(self.raw) as u32 }
    }

    /// Returns the number of audio channels.
    pub fn channels(&self) -> u32 {
        unsafe { ll::taglib_audioproperties_channels(self.raw) as u32 }
    }
}

#[derive(Copy, Clone, PartialEq)]
pub enum FileType {
    /// MPEG file
    MPEG = ll::TAGLIB_FILE_MPEG as isize,
    /// Ogg/Vorbis file
    OggVorbis = ll::TAGLIB_FILE_OGG_VORBIS as isize,
    /// FLAC file
    FLAC = ll::TAGLIB_FILE_FLAC as isize,
    /// MPC file
    MPC = ll::TAGLIB_FILE_MPC as isize,
    /// Ogg/FLAC file
    OggFlac = ll::TAGLIB_FILE_OGG_FLAC as isize,
    /// WavPack file
    WavPack = ll::TAGLIB_FILE_WAV_PACK as isize,
    /// Ogg/Speex file
    Speex = ll::TAGLIB_FILE_SPEEX as isize,
    /// TrueAudio file
    TrueAudio = ll::TAGLIB_FILE_TRUE_AUDIO as isize,
    /// MP4 file
    MP4 = ll::TAGLIB_FILE_MP4 as isize,
    /// ASF file
    ASF = ll::TAGLIB_FILE_ASF as isize,
}

#[derive(Debug)]
pub enum FileError {
    /// The file is an invalid or an unrecognized audio container
    InvalidFile,
    /// The file name is invalid
    InvalidFileName,
    /// No meta-data is available
    NoAvailableTag,
    /// No audio properties are available
    NoAvailableAudioProperties,
}

impl Drop for File {
    fn drop(&mut self) {
        unsafe {
            ll::taglib_file_free(self.raw);
        }
    }
}

impl File {
    /// Creates a new `taglib::File` for the given `filename`.
    pub fn new<P: AsRef<Path>>(path: P) -> Result<File, FileError> {
        let filename = path.as_ref().to_str().ok_or(FileError::InvalidFileName)?;
        let filename_c = CString::new(filename).ok().ok_or(FileError::InvalidFileName)?;
        let filename_c_ptr = filename_c.as_ptr();

        let f = unsafe { ll::taglib_file_new(filename_c_ptr) };
        if f.is_null() {
            return Err(FileError::InvalidFile);
        }

        Ok(File { raw: f })
    }

    /// Creates a new `taglib::File` for the given `filename` and type of file.
    pub fn new_type(filename: &str, filetype: FileType) -> Result<File, FileError> {
        let filename_c = match CString::new(filename) {
            Ok(s) => s,
            _ => return Err(FileError::InvalidFileName),
        };

        let filename_c_ptr = filename_c.as_ptr();
        let f = unsafe { ll::taglib_file_new_type(filename_c_ptr, filetype as u32) };
        if f.is_null() {
            return Err(FileError::InvalidFile);
        }

        Ok(File { raw: f })
    }

    /// Returns the `taglib::Tag` instance for the given file.
    pub fn tag(&self) -> Result<Tag, FileError> {
        let res = unsafe { ll::taglib_file_tag(self.raw) };

        if res.is_null() {
            Err(FileError::NoAvailableTag)
        } else {
            Ok(Tag {
                raw: res,
                file: self,
            })
        }
    }

    /// Returns whether the file is valid.
    pub fn is_valid(&self) -> bool {
        unsafe { ll::taglib_file_is_valid(self.raw) != 0 }
    }

    /// Returns the `taglib::AudioProperties` instance for the given file.
    pub fn audioproperties(&self) -> Result<AudioProperties, FileError> {
        let res = unsafe { ll::taglib_file_audioproperties(self.raw) };

        if res.is_null() {
            Err(FileError::NoAvailableAudioProperties)
        } else {
            Ok(AudioProperties {
                raw: res,
                file: self,
            })
        }
    }

    /// Updates the meta-data of the file.
    pub fn save(&self) -> bool {
        unsafe { ll::taglib_file_save(self.raw) != 0 }
    }
}

/// Fixture creation:
/// ffmpeg -t 0.01 -f s16le -i /dev/zero test.mp3
/// kid3-cli -c 'set artist "Artist"' test.mp3
#[cfg(test)]
mod test {
    const TEST_MP3: &'static str = "fixtures/test.mp3";

    use super::*;
    use std::fs;
    use std::path::PathBuf;

    #[test]
    fn test_get_tag() {
        let file = File::new(TEST_MP3).unwrap();
        let tag = file.tag().unwrap();
        assert_eq!(tag.artist().unwrap(), "Artist");
    }

    #[test]
    fn test_get_pathbuf() {
        let file = File::new(PathBuf::from(TEST_MP3)).unwrap();
        let tag = file.tag().unwrap();
        assert_eq!(tag.artist().unwrap(), "Artist");
    }

    #[test]
    fn test_get_tag_new_type() {
        let file = File::new_type(TEST_MP3, FileType::MPEG).unwrap();
        let tag = file.tag().unwrap();
        assert_eq!(tag.artist().unwrap(), "Artist");
    }

    #[test]
    fn test_get_audioproperties() {
        let file = File::new(TEST_MP3).unwrap();
        let ap = file.audioproperties().unwrap();
        assert_eq!(ap.length(), 0);
    }

    #[test]
    fn test_set_tag() {
        let temp_fn = "fixtures/temp.mp3";
        fs::copy(TEST_MP3, temp_fn).unwrap();
        let file = File::new(temp_fn).unwrap();
        let mut tag = file.tag().unwrap();

        tag.set_artist("Not Artist");
        file.save();

        assert_eq!(tag.artist().unwrap(), "Not Artist");

        fs::remove_file(temp_fn).unwrap();
    }
}
<commit_msg>Add test for None tag values<commit_after>// Copyright 2015  Emmanuele Bassi. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

#![crate_name = "taglib"]
#![crate_type = "lib"]

extern crate libc;
extern crate taglib_sys as sys;

use libc::c_char;
use std::ffi::{CString, CStr};
use std::path::Path;

use sys as ll;

fn c_str_to_str(c_str: *const c_char) -> Option<String> {
    if c_str.is_null() {
        None
    } else {
        let bytes = unsafe { CStr::from_ptr(c_str).to_bytes() };

        if bytes.is_empty() { None } else { Some(String::from_utf8_lossy(bytes).to_string()) }
    }
}

fn u32_to_option(n: u32) -> Option<u32> {
    if n == 0 { None } else { Some(n) }
}

/// A representation of an audio file, with meta-data and properties.
pub struct File {
    raw: *mut ll::TagLib_File,
}

/// The abstract meta-data container for audio files
///
/// Each `Tag` instance can only be created by the `taglib::File::tag()`
/// method.
#[allow(dead_code)]
pub struct Tag<'a> {
    raw: *mut ll::TagLib_Tag,
    file: &'a File,
}

/// Common audio file properties.
///
/// Instances of `AudioProperties` can only be created through the
/// `taglib::File::audioproperties()` method.
#[allow(dead_code)]
pub struct AudioProperties<'a> {
    raw: *const ll::TagLib_AudioProperties,
    file: &'a File,
}

impl<'a> Tag<'a> {
    /// Returns the track name, if any.
    pub fn title(&self) -> Option<String> {
        let res = unsafe { ll::taglib_tag_title(self.raw) };
        c_str_to_str(res)
    }

    /// Sets the track name.
    pub fn set_title(&mut self, title: &str) {
        let cs = CString::new(title).unwrap();
        let s = cs.as_ptr();
        unsafe {
            ll::taglib_tag_set_title(self.raw, s);
        }
    }

    /// Returns the artist name, if any.
    pub fn artist(&self) -> Option<String> {
        let res = unsafe { ll::taglib_tag_artist(self.raw) };
        c_str_to_str(res)
    }

    /// Sets the artist name.
    pub fn set_artist(&mut self, artist: &str) {
        let cs = CString::new(artist).unwrap();
        let s = cs.as_ptr();
        unsafe {
            ll::taglib_tag_set_artist(self.raw, s);
        }
    }

    /// Returns the album name, if any.
    pub fn album(&self) -> Option<String> {
        let res = unsafe { ll::taglib_tag_album(self.raw) };
        c_str_to_str(res)
    }

    /// Sets the album name.
    pub fn set_album(&mut self, album: &str) {
        let cs = CString::new(album).unwrap();
        let s = cs.as_ptr();
        unsafe {
            ll::taglib_tag_set_album(self.raw, s);
        }
    }

    /// Returns the track comment, if any.
    pub fn comment(&self) -> Option<String> {
        let res = unsafe { ll::taglib_tag_comment(self.raw) };
        c_str_to_str(res)
    }

    /// Sets the track comment.
    pub fn set_comment(&mut self, comment: &str) {
        let cs = CString::new(comment).unwrap();
        let s = cs.as_ptr();
        unsafe {
            ll::taglib_tag_set_comment(self.raw, s);
        }
    }

    /// Returns the genre name, if any.
    pub fn genre(&self) -> Option<String> {
        let res = unsafe { ll::taglib_tag_genre(self.raw) };
        c_str_to_str(res)
    }

    /// Sets the genre name.
    pub fn set_genre(&mut self, genre: &str) {
        let cs = CString::new(genre).unwrap();
        let s = cs.as_ptr();
        unsafe {
            ll::taglib_tag_set_genre(self.raw, s);
        }
    }

    /// Returns the year, if any.
    pub fn year(&self) -> Option<u32> {
        u32_to_option(unsafe { ll::taglib_tag_year(self.raw) as u32 })
    }

    /// Sets the year.
    pub fn set_year(&mut self, year: u32) {
        unsafe {
            ll::taglib_tag_set_year(self.raw, year);
        }
    }

    /// Returns the track number, if any.
    pub fn track(&self) -> Option<u32> {
        u32_to_option(unsafe { ll::taglib_tag_track(self.raw) as u32 })
    }

    /// Sets the track number.
    pub fn set_track(&mut self, track: u32) {
        unsafe {
            ll::taglib_tag_set_track(self.raw, track);
        }
    }
}

impl<'a> AudioProperties<'a> {
    /// Returns the length, in seconds, of the track.
    pub fn length(&self) -> u32 {
        unsafe { ll::taglib_audioproperties_length(self.raw) as u32 }
    }

    /// Returns the most appropriate bit rate for the track, in kB/s.
    /// For constant bit rate formats, the returned value is the bit
    /// rate of the file; for variable bit rate formats this is either
    /// the average or the nominal bit rate.
    pub fn bitrate(&self) -> u32 {
        unsafe { ll::taglib_audioproperties_bitrate(self.raw) as u32 }
    }

    /// Returns the sample rate, in Hz.
    pub fn samplerate(&self) -> u32 {
        unsafe { ll::taglib_audioproperties_samplerate(self.raw) as u32 }
    }

    /// Returns the number of audio channels.
    pub fn channels(&self) -> u32 {
        unsafe { ll::taglib_audioproperties_channels(self.raw) as u32 }
    }
}

#[derive(Copy, Clone, PartialEq)]
pub enum FileType {
    /// MPEG file
    MPEG = ll::TAGLIB_FILE_MPEG as isize,
    /// Ogg/Vorbis file
    OggVorbis = ll::TAGLIB_FILE_OGG_VORBIS as isize,
    /// FLAC file
    FLAC = ll::TAGLIB_FILE_FLAC as isize,
    /// MPC file
    MPC = ll::TAGLIB_FILE_MPC as isize,
    /// Ogg/FLAC file
    OggFlac = ll::TAGLIB_FILE_OGG_FLAC as isize,
    /// WavPack file
    WavPack = ll::TAGLIB_FILE_WAV_PACK as isize,
    /// Ogg/Speex file
    Speex = ll::TAGLIB_FILE_SPEEX as isize,
    /// TrueAudio file
    TrueAudio = ll::TAGLIB_FILE_TRUE_AUDIO as isize,
    /// MP4 file
    MP4 = ll::TAGLIB_FILE_MP4 as isize,
    /// ASF file
    ASF = ll::TAGLIB_FILE_ASF as isize,
}

#[derive(Debug)]
pub enum FileError {
    /// The file is an invalid or an unrecognized audio container
    InvalidFile,
    /// The file name is invalid
    InvalidFileName,
    /// No meta-data is available
    NoAvailableTag,
    /// No audio properties are available
    NoAvailableAudioProperties,
}

impl Drop for File {
    fn drop(&mut self) {
        unsafe {
            ll::taglib_file_free(self.raw);
        }
    }
}

impl File {
    /// Creates a new `taglib::File` for the given `filename`.
    pub fn new<P: AsRef<Path>>(path: P) -> Result<File, FileError> {
        let filename = path.as_ref().to_str().ok_or(FileError::InvalidFileName)?;
        let filename_c = CString::new(filename).ok().ok_or(FileError::InvalidFileName)?;
        let filename_c_ptr = filename_c.as_ptr();

        let f = unsafe { ll::taglib_file_new(filename_c_ptr) };
        if f.is_null() {
            return Err(FileError::InvalidFile);
        }

        Ok(File { raw: f })
    }

    /// Creates a new `taglib::File` for the given `filename` and type of file.
    pub fn new_type(filename: &str, filetype: FileType) -> Result<File, FileError> {
        let filename_c = match CString::new(filename) {
            Ok(s) => s,
            _ => return Err(FileError::InvalidFileName),
        };

        let filename_c_ptr = filename_c.as_ptr();
        let f = unsafe { ll::taglib_file_new_type(filename_c_ptr, filetype as u32) };
        if f.is_null() {
            return Err(FileError::InvalidFile);
        }

        Ok(File { raw: f })
    }

    /// Returns the `taglib::Tag` instance for the given file.
    pub fn tag(&self) -> Result<Tag, FileError> {
        let res = unsafe { ll::taglib_file_tag(self.raw) };

        if res.is_null() {
            Err(FileError::NoAvailableTag)
        } else {
            Ok(Tag {
                raw: res,
                file: self,
            })
        }
    }

    /// Returns whether the file is valid.
    pub fn is_valid(&self) -> bool {
        unsafe { ll::taglib_file_is_valid(self.raw) != 0 }
    }

    /// Returns the `taglib::AudioProperties` instance for the given file.
    pub fn audioproperties(&self) -> Result<AudioProperties, FileError> {
        let res = unsafe { ll::taglib_file_audioproperties(self.raw) };

        if res.is_null() {
            Err(FileError::NoAvailableAudioProperties)
        } else {
            Ok(AudioProperties {
                raw: res,
                file: self,
            })
        }
    }

    /// Updates the meta-data of the file.
    pub fn save(&self) -> bool {
        unsafe { ll::taglib_file_save(self.raw) != 0 }
    }
}

/// Fixture creation:
/// ffmpeg -t 0.01 -f s16le -i /dev/zero test.mp3
/// kid3-cli -c 'set artist "Artist"' test.mp3
#[cfg(test)]
mod test {
    const TEST_MP3: &'static str = "fixtures/test.mp3";

    use super::*;
    use std::fs;
    use std::path::PathBuf;

    #[test]
    fn test_get_tag() {
        let file = File::new(TEST_MP3).unwrap();
        let tag = file.tag().unwrap();
        assert_eq!(tag.artist().unwrap(), "Artist");
    }

    #[test]
    fn test_get_pathbuf() {
        let file = File::new(PathBuf::from(TEST_MP3)).unwrap();
        let tag = file.tag().unwrap();
        assert_eq!(tag.artist().unwrap(), "Artist");
    }

    #[test]
    fn test_get_no_tag() {
        let file = File::new(TEST_MP3).unwrap();
        let tag = file.tag().unwrap();
        assert_eq!(tag.album(), None);
    }

    #[test]
    fn test_get_tag_new_type() {
        let file = File::new_type(TEST_MP3, FileType::MPEG).unwrap();
        let tag = file.tag().unwrap();
        assert_eq!(tag.artist().unwrap(), "Artist");
    }

    #[test]
    fn test_get_audioproperties() {
        let file = File::new(TEST_MP3).unwrap();
        let ap = file.audioproperties().unwrap();
        assert_eq!(ap.length(), 0);
    }

    #[test]
    fn test_set_tag() {
        let temp_fn = "fixtures/temp.mp3";
        fs::copy(TEST_MP3, temp_fn).unwrap();
        let file = File::new(temp_fn).unwrap();
        let mut tag = file.tag().unwrap();

        tag.set_artist("Not Artist");
        file.save();

        assert_eq!(tag.artist().unwrap(), "Not Artist");

        fs::remove_file(temp_fn).unwrap();
    }
}
<|endoftext|>


<commit_before>extern crate xml;

use xml::{Element, Xml};

fn elem_with_text(tag_name: &'static str, chars: &str) -> Element {
    let mut elem = Element::new(tag_name, None, &[]);
    elem.text(chars);
    elem
}

trait ViaXml {
    fn to_xml(&self) -> Element;
}

/// RSS version 2.0
pub struct Rss(pub Vec<Channel>);

impl ViaXml for Rss {
    fn to_xml(&self) -> Element {
        let mut rss = Element::new("rss", None, &[("version", None, "2.0")]);

        let &Rss(ref channels) = self;
        for channel in channels {
            rss.tag(channel.to_xml());
        }

        rss
    }
}

impl Rss {
    fn to_string(&self) -> String {
        let mut ret = format!("{}", Xml::PINode("xml version='1.0' encoding='UTF-8'".to_string()));
        ret.push_str(&format!("{}", self.to_xml()));
        ret
    }
}

pub struct Channel {
    pub title: String,
    pub link: String,
    pub description: String,
    pub items: Vec<Item>,
}

impl ViaXml for Channel {
    fn to_xml(&self) -> Element {
        let mut channel = Element::new("channel", None, &[]);

        channel.tag(elem_with_text("title", &self.title));
        channel.tag(elem_with_text("link", &self.link));
        channel.tag(elem_with_text("description", &self.description));

        for item in &self.items {
            channel.tag(item.to_xml());
        }

        channel
    }
}

pub struct Item {
    pub title: Option<String>,
    pub link: Option<String>,
    pub description: Option<String>,
}

impl ViaXml for Item {
    fn to_xml(&self) -> Element {
        let mut item = Element::new("item", None, &[]);

        if let Some(ref s) = self.title {
            item.tag(elem_with_text("title", s));
        }

        if let Some(ref s) = self.link {
            item.tag(elem_with_text("link", s));
        }

        if let Some(ref s) = self.description {
            item.tag(elem_with_text("description", s));
        }

        item
    }
}

#[test]
fn test_consruct() {
    #![feature(collections)]

    let item = Item {
        title: Some(String::from_str("My first post!")),
        link: Some(String::from_str("http://myblog.com/post1")),
        description: Some(String::from_str("This is my first post")),
    };

    let channel = Channel {
        title: String::from_str("My Blog"),
        link: String::from_str("http://myblog.com"),
        description: String::from_str("Where I write stuff"),
        items: vec![item],
    };

    let rss = Rss(vec![channel]);
    assert_eq!(rss.to_string(), "<?xml version=\'1.0\' encoding=\'UTF-8\'?><rss version=\'2.0\'><channel><title>My Blog</title><link>http://myblog.com</link><description>Where I write stuff</description><item><title>My first post!</title><link>http://myblog.com/post1</link><description>This is my first post</description></item></channel></rss>");
}
<commit_msg>Make structs derive Default<commit_after>extern crate xml;

use xml::{Element, Xml};

fn elem_with_text(tag_name: &'static str, chars: &str) -> Element {
    let mut elem = Element::new(tag_name, None, &[]);
    elem.text(chars);
    elem
}

trait ViaXml {
    fn to_xml(&self) -> Element;
}

/// RSS version 2.0
#[derive(Default)]
pub struct Rss(pub Vec<Channel>);

impl ViaXml for Rss {
    fn to_xml(&self) -> Element {
        let mut rss = Element::new("rss", None, &[("version", None, "2.0")]);

        let &Rss(ref channels) = self;
        for channel in channels {
            rss.tag(channel.to_xml());
        }

        rss
    }
}

impl Rss {
    fn to_string(&self) -> String {
        let mut ret = format!("{}", Xml::PINode("xml version='1.0' encoding='UTF-8'".to_string()));
        ret.push_str(&format!("{}", self.to_xml()));
        ret
    }
}

#[derive(Default)]
pub struct Channel {
    pub title: String,
    pub link: String,
    pub description: String,
    pub items: Vec<Item>,
}

impl ViaXml for Channel {
    fn to_xml(&self) -> Element {
        let mut channel = Element::new("channel", None, &[]);

        channel.tag(elem_with_text("title", &self.title));
        channel.tag(elem_with_text("link", &self.link));
        channel.tag(elem_with_text("description", &self.description));

        for item in &self.items {
            channel.tag(item.to_xml());
        }

        channel
    }
}

#[derive(Default)]
pub struct Item {
    pub title: Option<String>,
    pub link: Option<String>,
    pub description: Option<String>,
}

impl ViaXml for Item {
    fn to_xml(&self) -> Element {
        let mut item = Element::new("item", None, &[]);

        if let Some(ref s) = self.title {
            item.tag(elem_with_text("title", s));
        }

        if let Some(ref s) = self.link {
            item.tag(elem_with_text("link", s));
        }

        if let Some(ref s) = self.description {
            item.tag(elem_with_text("description", s));
        }

        item
    }
}

#[test]
fn test_consruct() {
    #![feature(collections)]

    let item = Item {
        title: Some(String::from_str("My first post!")),
        link: Some(String::from_str("http://myblog.com/post1")),
        description: Some(String::from_str("This is my first post")),
    };

    let channel = Channel {
        title: String::from_str("My Blog"),
        link: String::from_str("http://myblog.com"),
        description: String::from_str("Where I write stuff"),
        items: vec![item],
    };

    let rss = Rss(vec![channel]);
    assert_eq!(rss.to_string(), "<?xml version=\'1.0\' encoding=\'UTF-8\'?><rss version=\'2.0\'><channel><title>My Blog</title><link>http://myblog.com</link><description>Where I write stuff</description><item><title>My first post!</title><link>http://myblog.com/post1</link><description>This is my first post</description></item></channel></rss>");
}
<|endoftext|>