<commit_before>use board_logic::{Board, BoardMarker};

#[derive(Debug, PartialEq)]
pub enum Direction {
    Horizontal,
    Vertical,
    Diagonal, // ´\´
    AntiDiagonal, // ´/´
}

#[derive(PartialEq, Debug)]
pub enum EvalError {
    NoMatch,
    OutOfBounds,
}

pub fn is_five(board: &Board, marker: BoardMarker) -> Result<Direction, EvalError> {
    // OK this will be hard, let's do it.

    let mut n_line = 1;
    { // Horizontal
        debug!("Horiz right:");
        debug!("\tStart: {}", marker.point.x+1);
        'right: for i in marker.point.x+1..board.boardsize {
            match board.getxy(i, marker.point.y).ok_or(0) {
                Ok(other_marker) => {
                    if other_marker.color == marker.color {
                        n_line += 1;
                    } else {
                        debug!("\tEnd: {}", i);
                        break 'right;
                    }
                },
                Err(err) => return Err(EvalError::OutOfBounds)
            }
        }
        debug!("Horiz left:");
        debug!("\tStart: {}", marker.point.x-1);
        'left: for i in (0..marker.point.x).rev() {
            match board.getxy(i, marker.point.y).ok_or(0) {
                Ok(other_marker) => {
                    if other_marker.color == marker.color {
                        n_line += 1;
                    } else {
                        debug!("\tEnd: {}", i);
                        break 'left;
                    }
                },
                Err(err) => return Err(EvalError::OutOfBounds)
            }
        }

        if n_line >= 4 {
            debug!("Horizontal Line length: {}", n_line);
            return Ok(Direction::Horizontal);
        }
        n_line=0;
    }
    { // Vertical
        debug!("Vert down:");
        debug!("\tStart: {}", marker.point.y+1);
        'down: for i in marker.point.y+1..board.boardsize {
            match board.getxy(marker.point.x, i).ok_or(0) {
                Ok(other_marker) => {
                    if other_marker.color == marker.color {
                        n_line += 1;
                    } else {
                        debug!("\tEnd: {}", i);
                        break 'down;
                    }
                },
                Err(err) => return Err(EvalError::OutOfBounds)
            }
        }
        debug!("Vert up: ");
        debug!("\tStart: {}", marker.point.x-1);
        'up: for i in (0..marker.point.y).rev() { // if it is suppossed to be 0..y+1 or 0..y is not clear
            match board.getxy(marker.point.x, i).ok_or(0) {
                Ok(other_marker) => {
                    if other_marker.color == marker.color {
                        n_line += 1;
                    } else {
                        debug!("\tEnd: {}", i);
                        break 'up;
                    }
                },
                Err(err) => return Err(EvalError::OutOfBounds)
            }
        }

        debug!("Vertical line length: {}", n_line);
        if n_line >= 4 {
            return Ok(Direction::Vertical);
        }
        n_line = 0;
    }
    { // Diagonal '\'
        debug!("Diagonal down:");
        debug!("\tStart: {}_{}", marker.point.x+1, marker.point.y+1);
    }
    Err(EvalError::NoMatch)
}

#[cfg(test)]
mod tests {
    use super::*;
    use board_logic::{Board, BoardMarker, Stone, Point};

    #[test]
    #[ignore]
    fn check_if_illegal_move() {
        let mut board = Board::new(15);
        for pos in [7 * 15 + 7, 7 * 15 + 6, 6 * 15 + 6, 5 * 15 + 7].iter() {
            board.set_point(Point::from_1d(*pos, 15), Stone::Black);
        }

        let illegal = Point::from_1d(7 * 15 + 5, 15);
        println!("{:?}", illegal);
    }

    #[test]
    fn is_horizontal_five_in_a_row() {
        let mut board = Board::new(15);
        let y = 7u32;
        let p1 = BoardMarker { point: Point::new(4, y), color: Stone::Black };
        for x in 0..4 {
            board.set_point(Point::new(x, y), Stone::Black);
        }

        let p2 = BoardMarker { point: Point::new(8, y + 2), color: Stone::White };
        for x in (7..12).filter(|x| *x != 8) {
            board.set_point(Point::new(x, y + 2), Stone::White);
        }
        println!("\n{}\nChecks,{:?} and {:?}",
                 board.board, p1, p2);

        assert_eq!(is_five(&board, p1), Ok(Direction::Horizontal));
        // assert_eq!(is_five(&board, p2).unwrap(), Direction::Horizontal);
    }

    #[test]
    fn is_vertical_five_in_a_row() {
        let mut board = Board::new(15);
        let x = 7u32;
        let p1 = BoardMarker { point: Point::new(x, 4), color: Stone::Black };
        for y in 0..4 {
            board.set_point(Point::new(x, y), Stone::Black);
        }

        let p2 = BoardMarker { point: Point::new(x + 2, 8), color: Stone::White };
        for y in (7..12).filter(|y| *y != 8) {
            board.set_point(Point::new(x+2, y), Stone::White);
        }
        println!("\n{}\nChecks; {:?} and {:?}",
                 board.board, p1, p2);

        assert_eq!(is_five(&board, p1), Ok(Direction::Vertical));
        assert_eq!(is_five(&board, p2), Ok(Direction::Vertical));
    }
    #[test]
    fn is_diagonal_five_in_a_row() {
        let mut board = Board::new(15);
        // A diagonal is '\'
        for pos in [7*15 + 2u32, 8*15+3u32, 9*15+4u32, 10*15+5u32].iter() {
            board.set_point(Point::from_1d(*pos, 15), Stone::Black);
        }
        let p1 = BoardMarker { point: Point::from_1d(11*15+6, 15), color: Stone::Black };
        println!("\n{}\nChecks; {:?}",
                 board.board, p1);

        assert_eq!(is_five(&board, p1), Ok(Direction::Diagonal));
    }
}
<commit_msg>Added diagonal and antidiagonal to is_five.<commit_after>use board_logic::{Board, BoardMarker};

#[derive(Debug, PartialEq)]
pub enum Direction {
    Horizontal,
    Vertical,
    Diagonal, // ´\´
    AntiDiagonal, // ´/´
}

#[derive(PartialEq, Debug)]
pub enum EvalError {
    NoMatch,
    OutOfBounds,
}

pub fn is_five(board: &Board, marker: BoardMarker) -> Result<Direction, EvalError> {
    // OK this will be hard, let's do it.
    debug!("In is_five, checking {:?}", marker);
    let mut n_line = 1;
    { // Horizontal
        debug!("Horiz right:");
        debug!("\tStart: {}", marker.point.x+1);
        'right: for i in marker.point.x+1..board.boardsize {
            match board.getxy(i, marker.point.y).ok_or(0) {
                Ok(other_marker) => {
                    debug!("\t{:?}", other_marker);
                    if other_marker.color == marker.color {
                        n_line += 1;
                    } else {
                        debug!("\tEnd: {}", i);
                        break 'right;
                    }
                },
                Err(_) => return Err(EvalError::OutOfBounds)
            }
        }
        debug!("Horiz left:");
        debug!("\tStart: {}", marker.point.x-1);
        'left: for i in (0..marker.point.x).rev() {
            match board.getxy(i, marker.point.y).ok_or(0) {
                Ok(other_marker) => {
                    debug!("\t{:?}", other_marker);
                    if other_marker.color == marker.color {
                        n_line += 1;
                    } else {
                        break 'left;
                    }
                },
                Err(_) => return Err(EvalError::OutOfBounds)
            }
        }

        debug!("Horizontal Line length: {}", n_line);
        if n_line >= 4 {
            return Ok(Direction::Horizontal);
        }
        n_line=0;
    }
    { // Vertical
        debug!("Vert down:");
        debug!("\tStart: {}", marker.point.y+1);
        'down: for i in marker.point.y+1..board.boardsize {
            match board.getxy(marker.point.x, i).ok_or(0) {
                Ok(other_marker) => {
                    debug!("\t{:?}", other_marker);
                    if other_marker.color == marker.color {
                        n_line += 1;
                    } else {
                        break 'down;
                    }
                },
                Err(_) => return Err(EvalError::OutOfBounds)
            }
        }
        debug!("Vert up: ");
        debug!("\tStart: {}", marker.point.y.checked_sub(1).unwrap_or(marker.point.y));
        'up: for i in (0..marker.point.y).rev() { // if it is suppossed to be 0..y+1 or 0..y is not clear
            match board.getxy(marker.point.x, i).ok_or(0) {
                Ok(other_marker) => {
                    debug!("\t{:?}", other_marker);
                    if other_marker.color == marker.color {
                        n_line += 1;
                    } else {
                        break 'up;
                    }
                },
                Err(_) => return Err(EvalError::OutOfBounds) // Shouldn't happen
            }
        }

        debug!("Vertical line length: {}", n_line);
        if n_line >= 4 {
            return Ok(Direction::Vertical);
        }
        n_line = 0;
    }
    { // Diagonal '\'
        debug!("Diagonal down:");
        debug!("\tStart: {}_{}", marker.point.x+1, marker.point.y+1);
        'diag_down: for i in 1..board.boardsize+1 {
            match board.getxy(marker.point.x+i, marker.point.y+i).ok_or(0) {
                Ok(other_marker) => {
                    debug!("\t{:?}", other_marker);
                    if other_marker.color == marker.color {
                        n_line += 1;
                    } else {
                        break 'diag_down;
                    }
                },
                Err(_) => {
                    debug!("\tEnd: {}_{}", marker.point.x+i, marker.point.y+i);
                    break 'diag_down;
                }
            }
        }
        debug!("Diagonal up:");
        debug!("\tStart: {}_{}", marker.point.x-1, marker.point.y-1);
        'diag_up: for i in 1..board.boardsize+1 {
            match board.getxy(marker.point.x.checked_sub(i).unwrap_or(1024), marker.point.y.checked_sub(i).unwrap_or(1024)).ok_or(0) {
                Ok(other_marker) => {
                    debug!("\t{:?}", other_marker);
                    if other_marker.color == marker.color {
                        n_line += 1;
                    } else {
                        break 'diag_up;
                    }
                },
                Err(_) => {
                    debug!("\tEnd: {}_{}", marker.point.x+i, marker.point.y+i);
                    break 'diag_up;
                }
            }
        }

        debug!("Diagonal line length: {}", n_line);
        if n_line >= 4 {
            return Ok(Direction::Diagonal);
        }
        n_line = 0;
    }
    { // AntiDiagonal '/'
        debug!("Anti-Diagonal down:");
        debug!("\tStart: {}_{}", marker.point.x+1, marker.point.y+1);
        'anti_diag_down: for i in 1..board.boardsize+1 {
            match board.getxy(marker.point.x.checked_sub(i).unwrap_or(1024), marker.point.y+i).ok_or(0) {
                Ok(other_marker) => {
                    debug!("\t{:?}", other_marker);
                    if other_marker.color == marker.color {
                        n_line += 1;
                    } else {
                        debug!("\tEnd: {}_{}", marker.point.x+i, marker.point.y+i);
                        break 'anti_diag_down;
                    }
                },
                Err(_) => {
                    debug!("\tEnd: {}_{}", marker.point.x+i, marker.point.y+i);
                    break 'anti_diag_down;
                }
            }
        }
        debug!("Anti-Diagonal up:");
        debug!("\tStart: {}_{}", marker.point.x+1, marker.point.y.checked_sub(1).unwrap_or(marker.point.y));
        'anti_diag_up: for i in 1..board.boardsize+1 {
            match board.getxy(marker.point.x + i, marker.point.y.checked_sub(i).unwrap_or(1024)).ok_or(0) {
                Ok(other_marker) => {
                    debug!("\t{:?}", other_marker);
                    if other_marker.color == marker.color {
                        n_line += 1;
                    } else {
                        break 'anti_diag_up;
                    }
                },
                Err(_) => {
                    break 'anti_diag_up;
                }
            }
        }

        debug!("Anti-Diagonal line length: {}", n_line);
        if n_line >= 4 {
            return Ok(Direction::AntiDiagonal);
        }
        n_line = 0;
    }
    Err(EvalError::NoMatch)
}

#[cfg(test)]
mod tests {
    use super::*;
    use board_logic::{Board, BoardMarker, Stone, Point};

    #[test]
    #[ignore]
    fn check_if_illegal_move() {
        let mut board = Board::new(15);
        for pos in [7 * 15 + 7, 7 * 15 + 6, 6 * 15 + 6, 5 * 15 + 7].iter() {
            board.set_point(Point::from_1d(*pos, 15), Stone::Black);
        }

        let illegal = Point::from_1d(7 * 15 + 5, 15);
        println!("{:?}", illegal);
    }

    #[test]
    fn is_horizontal_five_in_a_row() {
        let mut board = Board::new(15);
        let y = 7u32;
        let p1 = BoardMarker { point: Point::new(4, y), color: Stone::Black };
        for x in 0..4 {
            board.set_point(Point::new(x, y), Stone::Black);
        }

        let p2 = BoardMarker { point: Point::new(8, y + 2), color: Stone::White };
        for x in (7..12).filter(|x| *x != 8) {
            board.set_point(Point::new(x, y + 2), Stone::White);
        }
        println!("\n{}\nChecks,{:?} and {:?}",
                 board.board, p1, p2);

        assert_eq!(is_five(&board, p1), Ok(Direction::Horizontal));
        // assert_eq!(is_five(&board, p2).unwrap(), Direction::Horizontal);
    }

    #[test]
    fn is_vertical_five_in_a_row() {
        let mut board = Board::new(15);
        let x = 7u32;
        let p1 = BoardMarker { point: Point::new(x, 4), color: Stone::Black };
        for y in 0..4 {
            board.set_point(Point::new(x, y), Stone::Black);
        }

        let p2 = BoardMarker { point: Point::new(x + 2, 8), color: Stone::White };
        for y in (7..12).filter(|y| *y != 8) {
            board.set_point(Point::new(x+2, y), Stone::White);
        }
        println!("\n{}\nChecks; {:?} and {:?}",
                 board.board, p1, p2);

        assert_eq!(is_five(&board, p1), Ok(Direction::Vertical));
        assert_eq!(is_five(&board, p2), Ok(Direction::Vertical));
    }
    #[test]
    fn is_diagonal_five_in_a_row() {
        let mut board = Board::new(15);
        // A diagonal is '\'
        for pos in [2u32 + 7*15, 3u32 + 8*15, 4u32 + 9*15, 5u32 + 10*15].iter() {
            board.set_point(Point::from_1d(*pos, 15), Stone::Black);
        }

        for pos in [9u32 + 0*15, 10u32 + 1*15, 11u32 + 2*15, 13u32 + 4*15].iter() {
            board.set_point(Point::from_1d(*pos, 15), Stone::White);
        }
        let p1 = BoardMarker { point: Point::from_1d(11*15+6, 15), color: Stone::Black };
        let p2 = BoardMarker { point: Point::from_1d(12+3*15, 15), color: Stone::White };

        println!("\n{}\nChecks; {:?} and {:?}",
                 board.board, p1, p2);

        assert_eq!(is_five(&board, p1), Ok(Direction::Diagonal));
        assert_eq!(is_five(&board, p2), Ok(Direction::Diagonal));
    }
    #[test]
    fn is_anti_diagonal_five_in_a_row() {
        let mut board = Board::new(15);
        for pos in [6u32+6*15,5u32+7*15, 4u32+8*15, 3u32+9*15].iter() {
            board.set_point(Point::from_1d(*pos, 15), Stone::Black);
        }

        let p1 = BoardMarker { point: Point::from_1d(2u32+10*15, 15), color: Stone::Black };

        println!("\n{}\nChecks; {:?}",
                 board.board, p1);

        assert_eq!(is_five(&board, p1), Ok(Direction::AntiDiagonal));
    }
}
<|endoftext|>


<commit_before>//! Readiness event types and utilities.

mod event;
mod events;
mod source;

pub use self::event::Event;
pub use self::events::{Events, Iter};
pub use self::source::Source;
<commit_msg>Allow module_inception in event::event<commit_after>//! Readiness event types and utilities.

#[allow(clippy::module_inception)]
mod event;
mod events;
mod source;

pub use self::event::Event;
pub use self::events::{Events, Iter};
pub use self::source::Source;
<|endoftext|>


<commit_before>// (C)opyleft 2013 Frank Denis

/*!
 * Bindings for the GeoIP library
 */
#![desc = "Bindings for the GeoIP library."]
#![license = "BSD"]
#![crate_id = "geoip#0.1"]
#![crate_type = "rlib"]

#![warn(non_camel_case_types,
        non_uppercase_statics,
        unnecessary_qualification,
        managed_heap_memory)]

extern crate libc;

use libc::{c_void, c_char, c_int, c_ulong};
use std::c_str::CString;
use std::fmt;
use std::io::net::ip::{IpAddr,Ipv4Addr,Ipv6Addr};

type GeoIP_ = *c_void;
type In6Addr = [u8, ..16];

struct GeoIPLookup {
    netmask: c_int
}

impl GeoIPLookup {
    fn new() -> GeoIPLookup {
        GeoIPLookup {
            netmask: 0
        }
    }
}

#[link(name = "GeoIP")]
extern {
    fn GeoIP_open(dbtype: *c_char, flags: c_int) -> GeoIP_;
    fn GeoIP_delete(db: GeoIP_);
    fn GeoIP_name_by_ipnum_gl(db: GeoIP_, ipnum: c_ulong, gl: &GeoIPLookup) -> *c_char;
    fn GeoIP_name_by_ipnum_v6_gl(db: GeoIP_, ipnum: In6Addr, gl: &GeoIPLookup) -> *c_char;
}

pub enum Options {
    Standard = 0,
    MemoryCache = 1,
    CheckCache = 2,
    IndexCache = 4,
    MmapCache = 8
}

pub enum DBType {
    CountryEdition = 1,
    RegionEditionRev0 = 7,
    CityEditionRev0 = 6,
    ORGEdition = 5,
    ISPEdition = 4,
    CityEditionRev1 = 2,
    RegionEditionRev1 = 3,
    ProxyEdition = 8,
    ASNUMEdition = 9,
    NetSpeedEdition = 10,
    DomainEdition = 11,
    CountryEditionV6 = 12,
    LocationAEdition = 13,
    AccuracyRadiusEdition = 14,
    LargeCountryEdition = 17,
    LargeCountryEditionV6 = 18,
    ASNumEditionV6 = 21,
    ISPEditionV6 = 22,
    ORGEditionV6 = 23,
    DomainEditionV6 = 24,
    LoctionAEditionV6 = 25,
    RegistrarEdition = 26,
    RegistrarEditionV6 = 27,
    UserTypeEdition = 28,
    UserTypeEditionV6 = 29,
    CityEditionRev1V6 = 30,
    CityEditionRev0V6 = 31,
    NetSpeedEditionRev1 = 32,
    NetSpeedEditionRev1V6 = 33,
    CountryConfEdition = 34,
    CityConfEdition = 35,
    RegionConfEdition = 36,
    PostalConfEdition = 37,
    AccuracyRadiusEditionV6 = 38
}

pub struct GeoIP {
    db: GeoIP_
}

pub struct ASInfo {
    pub asn: uint,
    pub name: ~str,
    pub netmask: uint
}

impl fmt::Show for ASInfo {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f.buf, "{}\t{}", self.asn, self.name)
    }
}

impl GeoIP {
    pub fn open(path: &Path, options: Options) -> Result<GeoIP, ~str> {
        let file = match path.as_str() {
            None => return Err(format!("Invalid path {}", path.display())),
            Some(file) => file
        };
        let db = unsafe {
            GeoIP_open(file.to_c_str().unwrap(), options as c_int)
        };
        match db.is_null() {
            true => Err(format!("Can't open {}", file)),
            false => Ok(GeoIP {
                    db: db
                })
        }
    }

    pub fn as_info_by_ip(&self, ip: IpAddr) -> Option<ASInfo> {
        let gl = GeoIPLookup::new();
        let cres = match ip {
            Ipv4Addr(a, b, c, d) => {
                let ipnum: c_ulong =
                    (a as c_ulong << 24) | (b as c_ulong << 16) |
                    (c as c_ulong << 8)  | (d as c_ulong);
                unsafe {
                    GeoIP_name_by_ipnum_gl(self.db, ipnum, &gl)
                }
            },
            Ipv6Addr(a, b, c, d, e, f, g, h) => {
                let in6_addr: In6Addr = [(a >> 8) as u8, a as u8,
                                         (b >> 8) as u8, b as u8,
                                         (c >> 8) as u8, c as u8,
                                         (d >> 8) as u8, d as u8,
                                         (e >> 8) as u8, e as u8,
                                         (f >> 8) as u8, f as u8,
                                         (g >> 8) as u8, g as u8,
                                         (h >> 8) as u8, h as u8];
                unsafe {
                    GeoIP_name_by_ipnum_v6_gl(self.db, in6_addr, &gl)
                }
            }
        };
        if cres.is_null() {
            return None;
        }
        let description_cstr = unsafe { CString::new(cres, true) };
        let description = match description_cstr.as_str() {
            None => return None,
            Some(description) => description
        };
        let mut di = description.splitn(' ', 1);
        let asn = match di.next() {
            None => return None,
            Some(asn) => {
                if ! asn.starts_with("AS") {
                    return None
                } else {
                    from_str::<uint>(asn.slice_from(2)).unwrap()
                }
            }
        };
        let name = di.next().unwrap_or("(none)");
        let asinfo = ASInfo {
            asn: asn,
            name: name.to_owned(),
            netmask: gl.netmask as uint
        };
        Some(asinfo)
    }
}

impl Drop for GeoIP {
    fn drop(&mut self) {
        unsafe {
            GeoIP_delete(self.db);
        }
    }
}

#[test]
fn geoip_test_basic() {
    let geoip = match GeoIP::open(&from_str("/opt/geoip/GeoIPASNum.dat").unwrap(), MemoryCache) {
        Err(err) => fail!(err),
        Ok(geoip) => geoip
    };
    let ip = from_str("91.203.184.192").unwrap();
    let res = geoip.as_info_by_ip(ip).unwrap();
    assert!(res.asn == 41064);
    assert!(res.name.contains("Telefun"));
    assert!(res.netmask == 22);
}
<commit_msg>`&mut core::fmt::FormatWriter` does not implement any method in named `write_fmt`<commit_after>// (C)opyleft 2013 Frank Denis

/*!
 * Bindings for the GeoIP library
 */
#![desc = "Bindings for the GeoIP library."]
#![license = "BSD"]
#![crate_id = "geoip#0.1"]
#![crate_type = "rlib"]

#![warn(non_camel_case_types,
        non_uppercase_statics,
        unnecessary_qualification,
        managed_heap_memory)]

extern crate libc;

use libc::{c_void, c_char, c_int, c_ulong};
use std::c_str::CString;
use std::fmt;
use std::io::net::ip::{IpAddr,Ipv4Addr,Ipv6Addr};

type GeoIP_ = *c_void;
type In6Addr = [u8, ..16];

struct GeoIPLookup {
    netmask: c_int
}

impl GeoIPLookup {
    fn new() -> GeoIPLookup {
        GeoIPLookup {
            netmask: 0
        }
    }
}

#[link(name = "GeoIP")]
extern {
    fn GeoIP_open(dbtype: *c_char, flags: c_int) -> GeoIP_;
    fn GeoIP_delete(db: GeoIP_);
    fn GeoIP_name_by_ipnum_gl(db: GeoIP_, ipnum: c_ulong, gl: &GeoIPLookup) -> *c_char;
    fn GeoIP_name_by_ipnum_v6_gl(db: GeoIP_, ipnum: In6Addr, gl: &GeoIPLookup) -> *c_char;
}

pub enum Options {
    Standard = 0,
    MemoryCache = 1,
    CheckCache = 2,
    IndexCache = 4,
    MmapCache = 8
}

pub enum DBType {
    CountryEdition = 1,
    RegionEditionRev0 = 7,
    CityEditionRev0 = 6,
    ORGEdition = 5,
    ISPEdition = 4,
    CityEditionRev1 = 2,
    RegionEditionRev1 = 3,
    ProxyEdition = 8,
    ASNUMEdition = 9,
    NetSpeedEdition = 10,
    DomainEdition = 11,
    CountryEditionV6 = 12,
    LocationAEdition = 13,
    AccuracyRadiusEdition = 14,
    LargeCountryEdition = 17,
    LargeCountryEditionV6 = 18,
    ASNumEditionV6 = 21,
    ISPEditionV6 = 22,
    ORGEditionV6 = 23,
    DomainEditionV6 = 24,
    LoctionAEditionV6 = 25,
    RegistrarEdition = 26,
    RegistrarEditionV6 = 27,
    UserTypeEdition = 28,
    UserTypeEditionV6 = 29,
    CityEditionRev1V6 = 30,
    CityEditionRev0V6 = 31,
    NetSpeedEditionRev1 = 32,
    NetSpeedEditionRev1V6 = 33,
    CountryConfEdition = 34,
    CityConfEdition = 35,
    RegionConfEdition = 36,
    PostalConfEdition = 37,
    AccuracyRadiusEditionV6 = 38
}

pub struct GeoIP {
    db: GeoIP_
}

pub struct ASInfo {
    pub asn: uint,
    pub name: ~str,
    pub netmask: uint
}

impl fmt::Show for ASInfo {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}\t{}", self.asn, self.name)
    }
}

impl GeoIP {
    pub fn open(path: &Path, options: Options) -> Result<GeoIP, ~str> {
        let file = match path.as_str() {
            None => return Err(format!("Invalid path {}", path.display())),
            Some(file) => file
        };
        let db = unsafe {
            GeoIP_open(file.to_c_str().unwrap(), options as c_int)
        };
        match db.is_null() {
            true => Err(format!("Can't open {}", file)),
            false => Ok(GeoIP {
                    db: db
                })
        }
    }

    pub fn as_info_by_ip(&self, ip: IpAddr) -> Option<ASInfo> {
        let gl = GeoIPLookup::new();
        let cres = match ip {
            Ipv4Addr(a, b, c, d) => {
                let ipnum: c_ulong =
                    (a as c_ulong << 24) | (b as c_ulong << 16) |
                    (c as c_ulong << 8)  | (d as c_ulong);
                unsafe {
                    GeoIP_name_by_ipnum_gl(self.db, ipnum, &gl)
                }
            },
            Ipv6Addr(a, b, c, d, e, f, g, h) => {
                let in6_addr: In6Addr = [(a >> 8) as u8, a as u8,
                                         (b >> 8) as u8, b as u8,
                                         (c >> 8) as u8, c as u8,
                                         (d >> 8) as u8, d as u8,
                                         (e >> 8) as u8, e as u8,
                                         (f >> 8) as u8, f as u8,
                                         (g >> 8) as u8, g as u8,
                                         (h >> 8) as u8, h as u8];
                unsafe {
                    GeoIP_name_by_ipnum_v6_gl(self.db, in6_addr, &gl)
                }
            }
        };
        if cres.is_null() {
            return None;
        }
        let description_cstr = unsafe { CString::new(cres, true) };
        let description = match description_cstr.as_str() {
            None => return None,
            Some(description) => description
        };
        let mut di = description.splitn(' ', 1);
        let asn = match di.next() {
            None => return None,
            Some(asn) => {
                if ! asn.starts_with("AS") {
                    return None
                } else {
                    from_str::<uint>(asn.slice_from(2)).unwrap()
                }
            }
        };
        let name = di.next().unwrap_or("(none)");
        let asinfo = ASInfo {
            asn: asn,
            name: name.to_owned(),
            netmask: gl.netmask as uint
        };
        Some(asinfo)
    }
}

impl Drop for GeoIP {
    fn drop(&mut self) {
        unsafe {
            GeoIP_delete(self.db);
        }
    }
}

#[test]
fn geoip_test_basic() {
    let geoip = match GeoIP::open(&from_str("/opt/geoip/GeoIPASNum.dat").unwrap(), MemoryCache) {
        Err(err) => fail!(err),
        Ok(geoip) => geoip
    };
    let ip = from_str("91.203.184.192").unwrap();
    let res = geoip.as_info_by_ip(ip).unwrap();
    assert!(res.asn == 41064);
    assert!(res.name.contains("Telefun"));
    assert!(res.netmask == 22);
}
<|endoftext|>


<commit_before>/* Copyright 2015 Jordan Miner
 *
 * Licensed under the MIT license <LICENSE or
 * http://opensource.org/licenses/MIT>. This file may not be copied,
 * modified, or distributed except according to those terms.
 */

use std::cell::{Cell, RefCell};
use std::collections::{HashMap, hash_map};
use std::ops::Deref;
use std::rc::{Rc, Weak};
use std::ptr;
use libc::c_int;
use iup_sys::*;
use callbacks::Token;

thread_local!(
    static LDESTROY_CALLBACKS: RefCell<HashMap<*mut Ihandle, Vec<(usize, Box<FnMut(*mut Ihandle)>)>>> =
        RefCell::new(HashMap::new())
);

// simplified version of CallbackRegistry::add_callback
// An ldestroy callback should hold a reference to a control, so that when it is dropped a control
// is dropped. Doing so will cause the LDESTROY_CALLBACKS HashMap to be accessed, causing a panic
// due to being borrowed by remove_ldestroy_callback.
pub fn add_ldestroy_callback_inner(ih: *mut Ihandle, cb: Box<FnMut(*mut Ihandle) + 'static>) -> Token {
    LDESTROY_CALLBACKS.with(|reg| {
        let mut map = reg.borrow_mut();
        let cbs = map.entry(ih).or_insert_with(|| { Vec::with_capacity(4) });
        let id = cbs.last().map(|&(id, _)| id + 1).unwrap_or(0);
        cbs.push((id, cb));

        Token { id: id, ih: ih }
    })
}

pub fn add_ldestroy_callback<F: 'static + FnMut(*mut Ihandle)>(ih: *mut Ihandle, cb: F) -> Token {
    add_ldestroy_callback_inner(ih, Box::new(cb))
}

// simplified version of CallbackRegistry::remove_callback
pub fn remove_ldestroy_callback(token: Token) {
    LDESTROY_CALLBACKS.with(|reg| {
        let mut map = reg.borrow_mut();
        if let hash_map::Entry::Occupied(mut entry) = map.entry(token.ih) {
            let is_empty = {
                let cbs = entry.get_mut();
                let index = cbs.iter().position(|&(id, _)| id == token.id).expect("failed to remove callback");
                // TODO: if this causes ldestroy_cb() to be called, it will panic
                cbs.remove(index);

                cbs.is_empty()
            };
            if is_empty {
                entry.remove();
            }

            // I could use the below with non-lexical borrows.
            //let cbs = entry.get_mut();
            //let index = cbs.iter().position(|&(id, _)| id == token.id).expect("failed to remove callback");
            // TODO: if this causes ldestroy_cb() to be called, it will panic
            //cbs.remove(index);

            //if cbs.is_empty() {
            //    entry.remove();
            //}
        }
    });
}

extern fn ldestroy_cb(ih: *mut Ihandle) -> c_int {
    handle_rc_destroy_cb(ih);
    LDESTROY_CALLBACKS.with(|cell| {
        if let Some(mut cbs) = cell.borrow_mut().remove(&ih) {
            for cb in cbs.iter_mut() {
                cb.1(ih);
            }
        }
        IUP_DEFAULT
    })
}

thread_local!(
    static EXISTING_HANDLES: RefCell<HashMap<*mut Ihandle, Weak<HandleBox>>> = RefCell::new(HashMap::new())
);

pub fn handle_rc_destroy_cb(ih: *mut Ihandle) {
    EXISTING_HANDLES.with(|cell| {
        let mut map = cell.borrow_mut();
        if let Some(weak) = map.remove(&ih) {
            // It should be removed when the last strong ref was dropped, so it should always be upgradable.
            let handle = weak.upgrade().expect("could not upgrade Weak in handle map");
            // Since the control is destroyed, zero out to prevent wrapper structs from
            // using after free.
            handle.set(ptr::null_mut());
        }
        // else: No wrapper handles currently exist. Don't need to do anything.

        // Detach any child that still has a reference from a wrapper so that it doesn't
        // get destroyed.
        unsafe {
            // Removing a child shifts the index of the following children by one as you'd expect.
            // The easiest way would be to iterate backward removing children. However,
            // IUP stores children as a singly-linked list starting with the first child.
            // So iterating reverse would be O(n^2) best case. Iterating forward would be O(n)
            // best case and O(n^2) worst case. However, using IupGetNextChild and IupGetBrother
            // is always O(n).
            let mut child = IupGetNextChild(ih, ptr::null_mut());
            while !child.is_null() {
                let brother = IupGetBrother(child);
                if map.contains_key(&child) {
                    IupDetach(child);
                }
                child = brother;
            }
        }
    });
}

/// A `HandleBox` is the wrapper's one reference to a control. If there are multiple `HandleRc`s,
/// they all refer to the same `HandleBox`. Since there will always be at most one `HandleBox` for
/// a certain `*mut Ihandle`, then when the `HandleBox` is dropped, there are no more references
/// from the wrapper to the `*mut Ihandle`.
struct HandleBox(Cell<*mut Ihandle>);

impl Deref for HandleBox {
    type Target = Cell<*mut Ihandle>;
    fn deref(&self) -> &Cell<*mut Ihandle> {
        &self.0
    }
}

impl Drop for HandleBox {
    fn drop(&mut self) {
        if self.get() == ptr::null_mut() {
            return;
        }
        EXISTING_HANDLES.with(|map| {
            map.borrow_mut().remove(&self.get()).expect("handle not found in map");
        });
        unsafe {
            if IupGetParent(self.get()) == ptr::null_mut()
            {
                //println!("deleting control with no parent: {:?}", self.get());
                IupDestroy(self.get());
            }
        }
    }
}

// As a future optimization, the implementation could be changed to not use Rc, since support
// for weak ptrs is not used. It would only get rid of one word of memory though. Not worth
// the time.
#[derive(Clone)]
pub struct HandleRc(Rc<HandleBox>);

impl HandleRc {
    pub unsafe fn new(ih: *mut Ihandle) -> HandleRc {
        assert!(ih != ptr::null_mut(), "handle must be non-null");
        let rc = EXISTING_HANDLES.with(|map|
            match map.borrow_mut().entry(ih) {
                hash_map::Entry::Occupied(entry) =>
                    entry.get().upgrade().expect("could not upgrade Weak in handle map"),
                hash_map::Entry::Vacant(entry) => {
                    IupSetCallback(ih, "LDESTROY_CB\0".as_ptr() as *const i8, ldestroy_cb);
                    let rc = Rc::new(HandleBox(Cell::new(ih)));
                    entry.insert(Rc::downgrade(&rc));
                    rc
                },
            }
        );
        HandleRc(rc)
    }

    pub fn get(&self) -> *mut Ihandle {
        self.0.get()
    }

    pub fn try_unwrap(self) -> Result<*mut Ihandle, HandleRc> {
        Rc::try_unwrap(self.0).map(|handle_box| {
            let handle = handle_box.get();
            // null out so that `HandleBox` doesn't IupDestroy() it and so that no other
            // `HandleRc` can return it
            handle_box.set(ptr::null_mut());
            handle
        }).map_err(|rc| HandleRc(rc))
    }
}
<commit_msg>Fix the borrow of the LDESTROY_CALLBACKS HashMap to be shorter<commit_after>/* Copyright 2015 Jordan Miner
 *
 * Licensed under the MIT license <LICENSE or
 * http://opensource.org/licenses/MIT>. This file may not be copied,
 * modified, or distributed except according to those terms.
 */

use std::cell::{Cell, RefCell};
use std::collections::{HashMap, hash_map};
use std::ops::Deref;
use std::rc::{Rc, Weak};
use std::ptr;
use libc::c_int;
use iup_sys::*;
use callbacks::Token;

thread_local!(
    static LDESTROY_CALLBACKS: RefCell<HashMap<*mut Ihandle, Vec<(usize, Box<FnMut(*mut Ihandle)>)>>> =
        RefCell::new(HashMap::new())
);

// simplified version of CallbackRegistry::add_callback
// An ldestroy callback should hold a reference to a control, so that when it is dropped a control
// is dropped. Doing so will cause the LDESTROY_CALLBACKS HashMap to be accessed, causing a panic
// due to being borrowed by remove_ldestroy_callback. It is fine to drop a control in the callback,
// just not to capture a control so that the control is dropped when the callback is.
pub fn add_ldestroy_callback_inner(ih: *mut Ihandle, cb: Box<FnMut(*mut Ihandle) + 'static>) -> Token {
    LDESTROY_CALLBACKS.with(|reg| {
        let mut map = reg.borrow_mut();
        let cbs = map.entry(ih).or_insert_with(|| { Vec::with_capacity(4) });
        let id = cbs.last().map(|&(id, _)| id + 1).unwrap_or(0);
        cbs.push((id, cb));

        Token { id: id, ih: ih }
    })
}

pub fn add_ldestroy_callback<F: 'static + FnMut(*mut Ihandle)>(ih: *mut Ihandle, cb: F) -> Token {
    add_ldestroy_callback_inner(ih, Box::new(cb))
}

// simplified version of CallbackRegistry::remove_callback
pub fn remove_ldestroy_callback(token: Token) {
    LDESTROY_CALLBACKS.with(|reg| {
        let mut map = reg.borrow_mut();
        if let hash_map::Entry::Occupied(mut entry) = map.entry(token.ih) {
            let is_empty = {
                let cbs = entry.get_mut();
                let index = cbs.iter().position(|&(id, _)| id == token.id).expect("failed to remove callback");
                // TODO: if this causes ldestroy_cb() to be called, it will panic
                cbs.remove(index);

                cbs.is_empty()
            };
            if is_empty {
                entry.remove();
            }

            // I could use the below with non-lexical borrows.
            //let cbs = entry.get_mut();
            //let index = cbs.iter().position(|&(id, _)| id == token.id).expect("failed to remove callback");
            // TODO: if this causes ldestroy_cb() to be called, it will panic
            //cbs.remove(index);

            //if cbs.is_empty() {
            //    entry.remove();
            //}
        }
    });
}

extern fn ldestroy_cb(ih: *mut Ihandle) -> c_int {
    handle_rc_destroy_cb(ih);
    LDESTROY_CALLBACKS.with(|cell| {
        // Putting this line in the if condition will currently cause the HashMap borrow to last
        // for the entire if body.
        let cbs_opt = cell.borrow_mut().remove(&ih);
        if let Some(mut cbs) = cbs_opt {
            for cb in cbs.iter_mut() {
                cb.1(ih);
            }
        }
        IUP_DEFAULT
    })
}

thread_local!(
    static EXISTING_HANDLES: RefCell<HashMap<*mut Ihandle, Weak<HandleBox>>> = RefCell::new(HashMap::new())
);

pub fn handle_rc_destroy_cb(ih: *mut Ihandle) {
    EXISTING_HANDLES.with(|cell| {
        let mut map = cell.borrow_mut();
        if let Some(weak) = map.remove(&ih) {
            // It should be removed when the last strong ref was dropped, so it should always be upgradable.
            let handle = weak.upgrade().expect("could not upgrade Weak in handle map");
            // Since the control is destroyed, zero out to prevent wrapper structs from
            // using after free.
            handle.set(ptr::null_mut());
        }
        // else: No wrapper handles currently exist. Don't need to do anything.

        // Detach any child that still has a reference from a wrapper so that it doesn't
        // get destroyed.
        unsafe {
            // Removing a child shifts the index of the following children by one as you'd expect.
            // The easiest way would be to iterate backward removing children. However,
            // IUP stores children as a singly-linked list starting with the first child.
            // So iterating reverse would be O(n^2) best case. Iterating forward would be O(n)
            // best case and O(n^2) worst case. However, using IupGetNextChild and IupGetBrother
            // is always O(n).
            let mut child = IupGetNextChild(ih, ptr::null_mut());
            while !child.is_null() {
                let brother = IupGetBrother(child);
                if map.contains_key(&child) {
                    IupDetach(child);
                }
                child = brother;
            }
        }
    });
}

/// A `HandleBox` is the wrapper's one reference to a control. If there are multiple `HandleRc`s,
/// they all refer to the same `HandleBox`. Since there will always be at most one `HandleBox` for
/// a certain `*mut Ihandle`, then when the `HandleBox` is dropped, there are no more references
/// from the wrapper to the `*mut Ihandle`.
struct HandleBox(Cell<*mut Ihandle>);

impl Deref for HandleBox {
    type Target = Cell<*mut Ihandle>;
    fn deref(&self) -> &Cell<*mut Ihandle> {
        &self.0
    }
}

impl Drop for HandleBox {
    fn drop(&mut self) {
        if self.get() == ptr::null_mut() {
            return;
        }
        EXISTING_HANDLES.with(|map| {
            map.borrow_mut().remove(&self.get()).expect("handle not found in map");
        });
        unsafe {
            if IupGetParent(self.get()) == ptr::null_mut()
            {
                //println!("deleting control with no parent: {:?}", self.get());
                IupDestroy(self.get());
            }
        }
    }
}

// As a future optimization, the implementation could be changed to not use Rc, since support
// for weak ptrs is not used. It would only get rid of one word of memory though. Not worth
// the time.
#[derive(Clone)]
pub struct HandleRc(Rc<HandleBox>);

impl HandleRc {
    pub unsafe fn new(ih: *mut Ihandle) -> HandleRc {
        assert!(ih != ptr::null_mut(), "handle must be non-null");
        let rc = EXISTING_HANDLES.with(|map|
            match map.borrow_mut().entry(ih) {
                hash_map::Entry::Occupied(entry) =>
                    entry.get().upgrade().expect("could not upgrade Weak in handle map"),
                hash_map::Entry::Vacant(entry) => {
                    IupSetCallback(ih, "LDESTROY_CB\0".as_ptr() as *const i8, ldestroy_cb);
                    let rc = Rc::new(HandleBox(Cell::new(ih)));
                    entry.insert(Rc::downgrade(&rc));
                    rc
                },
            }
        );
        HandleRc(rc)
    }

    pub fn get(&self) -> *mut Ihandle {
        self.0.get()
    }

    pub fn try_unwrap(self) -> Result<*mut Ihandle, HandleRc> {
        Rc::try_unwrap(self.0).map(|handle_box| {
            let handle = handle_box.get();
            // null out so that `HandleBox` doesn't IupDestroy() it and so that no other
            // `HandleRc` can return it
            handle_box.set(ptr::null_mut());
            handle
        }).map_err(|rc| HandleRc(rc))
    }
}
<|endoftext|>


<commit_before>/* Copyright 2015 Jordan Miner
 *
 * Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
 * http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
 * <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
 * option. This file may not be copied, modified, or distributed
 * except according to those terms.
 */

use std::borrow::Cow;
use std::io::{self, Read};
use std::iter::repeat;
use std::mem;
use std::string::FromUtf8Error;
use std::str::Utf8Error;
use super::{Value, Schema};

#[derive(Debug)]
pub enum DecodeErrorKind {
    ReadFailed(io::Error),
    UnexpectedEof,
    InvalidBoolean,
    IntegerOverflow,
    NegativeLength,
    InvalidUtf8(Utf8Error),
}

#[derive(Debug)]
pub struct DecodeError {
    pub kind: DecodeErrorKind,
}

impl From<io::Error> for DecodeError {
    fn from(err: io::Error) -> DecodeError {
        DecodeError { kind: DecodeErrorKind::ReadFailed(err) }
    }
}

impl From<FromUtf8Error> for DecodeError {
    fn from(err: FromUtf8Error) -> DecodeError {
        DecodeError { kind: DecodeErrorKind::InvalidUtf8(err.utf8_error()) }
    }
}

// TODO: delete and switch over to Read::read_exact() when it is marked stable in Rust
fn read_exact<R: Read>(reader: &mut R, buf: &mut [u8]) -> Result<(), DecodeError> {
    let mut index = 0;
    let len = buf.len(); // borrow checker workaround
    while index < len {
        let read_count = try!(reader.read(&mut buf[index..len]));
        index += read_count;
        if read_count == 0 {
            return Err(DecodeError { kind: DecodeErrorKind::UnexpectedEof });
        }
    };
    Ok(())
}

fn decode_var_len_u64<R: Read>(reader: &mut R) -> Result<u64, DecodeError> {
    let mut num = 0;
    let mut i = 0;
    loop {
        if i >= 10 {
            return Err(DecodeError { kind: DecodeErrorKind::IntegerOverflow })
        }
        let mut buf = [0u8; 1];
        try!(read_exact(reader, &mut buf));
        num |= (buf[0] as u64 & 0b0111_1111) << (i * 7);
        if buf[0] & 0b1000_0000 == 0 {
            break;
        }
        i += 1;
    }
    Ok(num)
}

pub fn decode_zig_zag(num: u64) -> i64 {
    // This compiles to (num << 63 as i64) >> 63 ^ (num >> 1), which I came up with as a
    // way to avoid the branch before I looked at the disassembly.
    if num & 1 == 1 {
        !(num >> 1) as i64
    } else {
        (num >> 1) as i64
    }
}

#[test]
fn test_decode_zig_zag() {
    assert_eq!(decode_zig_zag(0), 0);
    assert_eq!(decode_zig_zag(1), -1);
    assert_eq!(decode_zig_zag(2), 1);
    assert_eq!(decode_zig_zag(3), -2);
    assert_eq!(decode_zig_zag(0xFFFFFFFF_FFFFFFFF), i64::min_value());
    assert_eq!(decode_zig_zag(0xFFFFFFFF_FFFFFFFE), i64::max_value()); // 0x7FFF...FF
}

pub fn decode<'a, R: Read>(reader: &mut R, schema: &Schema<'a>)
-> Result<Value<'a, 'static>, DecodeError> {
    match schema {
        &Schema::Null => Ok(Value::Null),
        &Schema::Boolean => {
            let mut buf = [0u8; 1];
            try!(read_exact(reader, &mut buf[..]));
            match buf[0] {
                0 => Ok(Value::Boolean(false)),
                1 => Ok(Value::Boolean(true)),
                _ => Err(DecodeError { kind: DecodeErrorKind::InvalidBoolean }),
            }
        },
        &Schema::Int => {
            let num = decode_zig_zag(try!(decode_var_len_u64(reader)));
            if num < (i32::min_value() as i64) || num > (i32::max_value() as i64) {
                Err(DecodeError { kind: DecodeErrorKind::IntegerOverflow })
            } else {
                Ok(Value::Int(num as i32))
            }
        },
        &Schema::Long => {
            Ok(Value::Long(decode_zig_zag(try!(decode_var_len_u64(reader)))))
        },
        &Schema::Float => {
            let mut buf = [0u8; 4];
            try!(read_exact(reader, &mut buf[..]));
            Ok(Value::Float(unsafe { mem::transmute(buf) }))
        },
        &Schema::Double => {
            let mut buf = [0u8; 8];
            try!(read_exact(reader, &mut buf[..]));
            Ok(Value::Double(unsafe { mem::transmute(buf) }))
        },
        &Schema::Bytes => {
            if let Value::Long(len) = try!(decode(reader, &Schema::Long)) {
                // TODO: should read in 64 KB chunks so that a long length won't cause a OOM
                if len < 0 {
                    return Err(DecodeError { kind: DecodeErrorKind::NegativeLength });
                }
                let mut val: Vec<u8> = repeat(0).take(len as usize).collect();
                try!(read_exact(reader, &mut val));
                Ok(Value::Bytes(Cow::Owned(val)))
            } else {
                panic!("decode returned invalid value");
            }
        },
        &Schema::String => {
            if let Value::Long(len) = try!(decode(reader, &Schema::Long)) {
                // TODO: should read in 64 KB chunks so that a long length won't cause a OOM
                if len < 0 {
                    return Err(DecodeError { kind: DecodeErrorKind::NegativeLength });
                }
                let mut val: Vec<u8> = repeat(0).take(len as usize).collect();
                try!(read_exact(reader, &mut val));
                Ok(Value::String(try!(String::from_utf8(val)).into()))
            } else {
                panic!("decode returned invalid value");
            }
        },
        &Schema::Record(ref inner_schema) => {
            let mut values = Vec::new();
            for field in inner_schema.fields.iter() {
                values.push(try!(decode(reader, &field.ty)));
            }
            Ok(Value::Record(inner_schema.clone(), values))
        },
        //Schema::Error(ref inner_schema) => {
        //    let values = Vec::new();
        //    for field in inner_schema.fields {
        //        values.push(try!(decode(reader, field.ty)));
        //    }
        //    Ok(Value::Error(inner_schema, values))
        //},
        //Schema::Enum(ref inner_schema) => ,
        //Schema::Array { items } => ,
        //Schema::Map { values } => ,
        //Schema::Union { tys } => ,
        &Schema::Fixed(ref inner_schema) => {
            let mut val: Vec<u8> = repeat(0).take(inner_schema.size).collect();
            try!(read_exact(reader, &mut val));
            Ok(Value::Fixed(inner_schema.clone(), Cow::Owned(val)))
        },
        _ => unimplemented!(),
    }
}

#[test]
fn test_decode_null() {
    assert_eq!(decode(&mut &b""[..], &Schema::Null).unwrap(), Value::Null);
}

#[test]
fn test_decode_boolean() {
    assert_eq!(decode(&mut &b"\x01"[..], &Schema::Boolean).unwrap(), Value::Boolean(true));
    assert_eq!(decode(&mut &b"\x00"[..], &Schema::Boolean).unwrap(), Value::Boolean(false));
    assert!(decode(&mut &b"\x05"[..], &Schema::Boolean).is_err());
}

#[test]
fn test_decode_ints() {
    assert_eq!(decode(&mut &b"\x04"[..], &Schema::Int).unwrap(), Value::Int(2));
    assert_eq!(decode(&mut &b"\x84\x02"[..], &Schema::Int).unwrap(), Value::Int(130));
    assert_eq!(decode(&mut &b"\x83\x02"[..], &Schema::Int).unwrap(), Value::Int(-130));
    assert_eq!(decode(&mut &b"\x84\x02"[..], &Schema::Long).unwrap(), Value::Long(130));
}

#[test]
fn test_decode_floats() {
    assert_eq!(decode(&mut &b"\x00\x00\x00\x00"[..], &Schema::Float).unwrap(), Value::Float(0.0));
    assert_eq!(decode(&mut &b"\xC3\xF5\x48\x40"[..], &Schema::Float).unwrap(), Value::Float(3.14));
    assert_eq!(decode(&mut &b"\x58\x39\xB4\xC8\x76\xBE\x05\x40"[..], &Schema::Double).unwrap(),
        Value::Double(2.718));
}

#[test]
fn test_decode_bytes() {
    assert_eq!(decode(&mut &b"\x04\x84\x32"[..], &Schema::Bytes).unwrap(),
        Value::Bytes(Cow::Borrowed(&b"\x84\x32"[..])));
    match decode(&mut &b"\x03\x79\x65"[..], &Schema::Bytes) {
        Err(DecodeError { kind: DecodeErrorKind::NegativeLength }) => {},
        Err(_) => panic!("wrong error kind"),
        _ => panic!("error expected"),
    }
}

#[test]
fn test_decode_string() {
    assert_eq!(decode(&mut &b"\x06\x79\x65\x73"[..], &Schema::String).unwrap(),
        Value::String("yes".into()));
    match decode(&mut &b"\x03\x79\x65"[..], &Schema::String) {
        Err(DecodeError { kind: DecodeErrorKind::NegativeLength }) => {},
        Err(_) => panic!("wrong error kind"),
        _ => panic!("error expected"),
    }
}

#[test]
fn test_decode_record() {
    use std::rc::Rc;
    use super::{Field, RecordSchema};

    let fields = vec![
        Field { name: "year".into(), doc: None, properties: vec![], ty: Schema::Int },
        Field { name: "color".into(), doc: None, properties: vec![], ty: Schema::String },
        Field { name: "running".into(), doc: None, properties: vec![], ty: Schema::Boolean },
    ];
    let schema = Rc::new(RecordSchema::new("Car".into(), None, vec![], fields));
    if let Value::Record(_, rec_data) =
    decode(&mut &b"\xAE\x1F\x06\x52\x65\x64\x01"[..], &Schema::Record(schema)).unwrap() {
        assert_eq!(rec_data[0], Value::Int(2007));
        assert_eq!(rec_data[1], Value::String("Red".into()));
        assert_eq!(rec_data[2], Value::Boolean(true));
    } else {
        panic!("wrong type");
    }
}

#[test]
fn test_decode_fixed() {
    use std::rc::Rc;
    use super::FixedSchema;

    let schema = Rc::new(FixedSchema::new("FewBytes".into(), None, vec![], 3));
    if let Value::Fixed(_, rec_data) =
    decode(&mut &b"\xB4\xF6\x02"[..], &Schema::Fixed(schema)).unwrap() {
        assert_eq!(rec_data[0], 0xB4);
        assert_eq!(rec_data[1], 0xF6);
        assert_eq!(rec_data[2], 0x02);
    } else {
        panic!("wrong type");
    }
}
<commit_msg>Use Read::read_exact(), since was marked stable three releases ago<commit_after>/* Copyright 2015 Jordan Miner
 *
 * Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
 * http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
 * <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
 * option. This file may not be copied, modified, or distributed
 * except according to those terms.
 */

use std::borrow::Cow;
use std::io::{self, Read};
use std::iter::repeat;
use std::mem;
use std::string::FromUtf8Error;
use std::str::Utf8Error;
use super::{Value, Schema};

#[derive(Debug)]
pub enum DecodeErrorKind {
    ReadFailed(io::Error),
    InvalidBoolean,
    IntegerOverflow,
    NegativeLength,
    InvalidUtf8(Utf8Error),
}

#[derive(Debug)]
pub struct DecodeError {
    pub kind: DecodeErrorKind,
}

impl From<io::Error> for DecodeError {
    fn from(err: io::Error) -> DecodeError {
        DecodeError { kind: DecodeErrorKind::ReadFailed(err) }
    }
}

impl From<FromUtf8Error> for DecodeError {
    fn from(err: FromUtf8Error) -> DecodeError {
        DecodeError { kind: DecodeErrorKind::InvalidUtf8(err.utf8_error()) }
    }
}

fn decode_var_len_u64<R: Read>(reader: &mut R) -> Result<u64, DecodeError> {
    let mut num = 0;
    let mut i = 0;
    loop {
        if i >= 10 {
            return Err(DecodeError { kind: DecodeErrorKind::IntegerOverflow })
        }
        let mut buf = [0u8; 1];
        try!(reader.read_exact(&mut buf));
        num |= (buf[0] as u64 & 0b0111_1111) << (i * 7);
        if buf[0] & 0b1000_0000 == 0 {
            break;
        }
        i += 1;
    }
    Ok(num)
}

pub fn decode_zig_zag(num: u64) -> i64 {
    // This compiles to (num << 63 as i64) >> 63 ^ (num >> 1), which I came up with as a
    // way to avoid the branch before I looked at the disassembly.
    if num & 1 == 1 {
        !(num >> 1) as i64
    } else {
        (num >> 1) as i64
    }
}

#[test]
fn test_decode_zig_zag() {
    assert_eq!(decode_zig_zag(0), 0);
    assert_eq!(decode_zig_zag(1), -1);
    assert_eq!(decode_zig_zag(2), 1);
    assert_eq!(decode_zig_zag(3), -2);
    assert_eq!(decode_zig_zag(0xFFFFFFFF_FFFFFFFF), i64::min_value());
    assert_eq!(decode_zig_zag(0xFFFFFFFF_FFFFFFFE), i64::max_value()); // 0x7FFF...FF
}

pub fn decode<'a, R: Read>(reader: &mut R, schema: &Schema<'a>)
-> Result<Value<'a, 'static>, DecodeError> {
    match schema {
        &Schema::Null => Ok(Value::Null),
        &Schema::Boolean => {
            let mut buf = [0u8; 1];
            try!(reader.read_exact(&mut buf[..]));
            match buf[0] {
                0 => Ok(Value::Boolean(false)),
                1 => Ok(Value::Boolean(true)),
                _ => Err(DecodeError { kind: DecodeErrorKind::InvalidBoolean }),
            }
        },
        &Schema::Int => {
            let num = decode_zig_zag(try!(decode_var_len_u64(reader)));
            if num < (i32::min_value() as i64) || num > (i32::max_value() as i64) {
                Err(DecodeError { kind: DecodeErrorKind::IntegerOverflow })
            } else {
                Ok(Value::Int(num as i32))
            }
        },
        &Schema::Long => {
            Ok(Value::Long(decode_zig_zag(try!(decode_var_len_u64(reader)))))
        },
        &Schema::Float => {
            let mut buf = [0u8; 4];
            try!(reader.read_exact(&mut buf[..]));
            Ok(Value::Float(unsafe { mem::transmute(buf) }))
        },
        &Schema::Double => {
            let mut buf = [0u8; 8];
            try!(reader.read_exact(&mut buf[..]));
            Ok(Value::Double(unsafe { mem::transmute(buf) }))
        },
        &Schema::Bytes => {
            if let Value::Long(len) = try!(decode(reader, &Schema::Long)) {
                // TODO: should read in 64 KB chunks so that a long length won't cause a OOM
                if len < 0 {
                    return Err(DecodeError { kind: DecodeErrorKind::NegativeLength });
                }
                let mut val: Vec<u8> = repeat(0).take(len as usize).collect();
                try!(reader.read_exact(&mut val));
                Ok(Value::Bytes(Cow::Owned(val)))
            } else {
                panic!("decode returned invalid value");
            }
        },
        &Schema::String => {
            if let Value::Long(len) = try!(decode(reader, &Schema::Long)) {
                // TODO: should read in 64 KB chunks so that a long length won't cause a OOM
                if len < 0 {
                    return Err(DecodeError { kind: DecodeErrorKind::NegativeLength });
                }
                let mut val: Vec<u8> = repeat(0).take(len as usize).collect();
                try!(reader.read_exact(&mut val));
                Ok(Value::String(try!(String::from_utf8(val)).into()))
            } else {
                panic!("decode returned invalid value");
            }
        },
        &Schema::Record(ref inner_schema) => {
            let mut values = Vec::new();
            for field in inner_schema.fields.iter() {
                values.push(try!(decode(reader, &field.ty)));
            }
            Ok(Value::Record(inner_schema.clone(), values))
        },
        //Schema::Error(ref inner_schema) => {
        //    let values = Vec::new();
        //    for field in inner_schema.fields {
        //        values.push(try!(decode(reader, field.ty)));
        //    }
        //    Ok(Value::Error(inner_schema, values))
        //},
        //Schema::Enum(ref inner_schema) => ,
        //Schema::Array { items } => ,
        //Schema::Map { values } => ,
        //Schema::Union { tys } => ,
        &Schema::Fixed(ref inner_schema) => {
            let mut val: Vec<u8> = repeat(0).take(inner_schema.size).collect();
            try!(reader.read_exact(&mut val));
            Ok(Value::Fixed(inner_schema.clone(), Cow::Owned(val)))
        },
        _ => unimplemented!(),
    }
}

#[test]
fn test_decode_null() {
    assert_eq!(decode(&mut &b""[..], &Schema::Null).unwrap(), Value::Null);
}

#[test]
fn test_decode_boolean() {
    assert_eq!(decode(&mut &b"\x01"[..], &Schema::Boolean).unwrap(), Value::Boolean(true));
    assert_eq!(decode(&mut &b"\x00"[..], &Schema::Boolean).unwrap(), Value::Boolean(false));
    assert!(decode(&mut &b"\x05"[..], &Schema::Boolean).is_err());
}

#[test]
fn test_decode_ints() {
    assert_eq!(decode(&mut &b"\x04"[..], &Schema::Int).unwrap(), Value::Int(2));
    assert_eq!(decode(&mut &b"\x84\x02"[..], &Schema::Int).unwrap(), Value::Int(130));
    assert_eq!(decode(&mut &b"\x83\x02"[..], &Schema::Int).unwrap(), Value::Int(-130));
    assert_eq!(decode(&mut &b"\x84\x02"[..], &Schema::Long).unwrap(), Value::Long(130));
}

#[test]
fn test_decode_floats() {
    assert_eq!(decode(&mut &b"\x00\x00\x00\x00"[..], &Schema::Float).unwrap(), Value::Float(0.0));
    assert_eq!(decode(&mut &b"\xC3\xF5\x48\x40"[..], &Schema::Float).unwrap(), Value::Float(3.14));
    assert_eq!(decode(&mut &b"\x58\x39\xB4\xC8\x76\xBE\x05\x40"[..], &Schema::Double).unwrap(),
        Value::Double(2.718));
}

#[test]
fn test_decode_bytes() {
    assert_eq!(decode(&mut &b"\x04\x84\x32"[..], &Schema::Bytes).unwrap(),
        Value::Bytes(Cow::Borrowed(&b"\x84\x32"[..])));
    match decode(&mut &b"\x03\x79\x65"[..], &Schema::Bytes) {
        Err(DecodeError { kind: DecodeErrorKind::NegativeLength }) => {},
        Err(_) => panic!("wrong error kind"),
        _ => panic!("error expected"),
    }
}

#[test]
fn test_decode_string() {
    assert_eq!(decode(&mut &b"\x06\x79\x65\x73"[..], &Schema::String).unwrap(),
        Value::String("yes".into()));
    match decode(&mut &b"\x03\x79\x65"[..], &Schema::String) {
        Err(DecodeError { kind: DecodeErrorKind::NegativeLength }) => {},
        Err(_) => panic!("wrong error kind"),
        _ => panic!("error expected"),
    }
}

#[test]
fn test_decode_record() {
    use std::rc::Rc;
    use super::{Field, RecordSchema};

    let fields = vec![
        Field { name: "year".into(), doc: None, properties: vec![], ty: Schema::Int },
        Field { name: "color".into(), doc: None, properties: vec![], ty: Schema::String },
        Field { name: "running".into(), doc: None, properties: vec![], ty: Schema::Boolean },
    ];
    let schema = Rc::new(RecordSchema::new("Car".into(), None, vec![], fields));
    if let Value::Record(_, rec_data) =
    decode(&mut &b"\xAE\x1F\x06\x52\x65\x64\x01"[..], &Schema::Record(schema)).unwrap() {
        assert_eq!(rec_data[0], Value::Int(2007));
        assert_eq!(rec_data[1], Value::String("Red".into()));
        assert_eq!(rec_data[2], Value::Boolean(true));
    } else {
        panic!("wrong type");
    }
}

#[test]
fn test_decode_fixed() {
    use std::rc::Rc;
    use super::FixedSchema;

    let schema = Rc::new(FixedSchema::new("FewBytes".into(), None, vec![], 3));
    if let Value::Fixed(_, rec_data) =
    decode(&mut &b"\xB4\xF6\x02"[..], &Schema::Fixed(schema)).unwrap() {
        assert_eq!(rec_data[0], 0xB4);
        assert_eq!(rec_data[1], 0xF6);
        assert_eq!(rec_data[2], 0x02);
    } else {
        panic!("wrong type");
    }
}
<|endoftext|>


<commit_before>/// The Toml Delete extensions

use toml::Value;

use tokenizer::Token;
use tokenizer::tokenize_with_seperator;
use error::*;

pub trait TomlValueDeleteExt {

    /// Extension function for deleting a value in the current toml::Value document
    /// using a custom seperator.
    ///
    /// # Semantics
    ///
    /// The function does _not_ delete non-empty data structures. So deleting `array` from
    ///
    /// ```toml
    /// array = [ 1 ]
    /// ```
    ///
    /// does _not_ work.
    ///
    /// # Return value
    ///
    /// If the delete operation worked correctly, `Ok(Option<Value>)` is returned.
    ///
    /// The `Option<Value>` part is `None` if no value was actually removed as there was no value
    /// there. For example, if you're deleting `table.a` and the Table `table` has no key `a`, then
    /// `Ok(None)` is returned. Also, if you're deleting from an Array, but there is nothing in the
    /// array, or the array is shorter than the index you're deleting.
    /// If the delete operation actually removed something from the toml document, this value is
    /// returned as `Ok(Some(Value))`.
    ///
    /// On failure, `Err(e)` is returned
    ///
    fn delete_with_seperator(&mut self, query: &String, sep: char) -> Result<Option<Value>>;

    /// Extension function for inserting a value from the current toml::Value document
    ///
    /// See documentation of `TomlValueinsertExt::insert_with_seperator`
    fn delete(&mut self, query: &String) -> Result<Option<Value>> {
        self.delete_with_seperator(query, '.')
    }

}

impl TomlValueDeleteExt for Value {

    fn delete_with_seperator(&mut self, query: &String, sep: char) -> Result<Option<Value>> {
        use resolver::mut_resolver::resolve;

        let mut tokens = try!(tokenize_with_seperator(query, sep));
        let last_token = tokens.pop_last();

        if last_token.is_none() {
            match self {
                &mut Value::Table(ref mut tab) => {
                    match tokens {
                        Token::Identifier { ident, .. } => {
                            if !tab.is_empty() {
                                let kind = ErrorKind::CannotDeleteNonEmptyTable(ident.clone());
                                Err(Error::from(kind))
                            } else {
                                Ok(tab.remove(&ident))
                            }
                        },
                        _ => Ok(None)
                    }
                },
                _ => unimplemented!()
            }
        } else {
            let mut val = try!(resolve(self, &tokens));
            Ok(None)
        }
    }

}

#[cfg(test)]
mod test {
    use super::*;
    use error::*;
    use toml::Value;
    use toml::from_str as toml_from_str;

    #[test]
    fn test_delete_from_empty_document() {
        let mut toml : Value = toml_from_str("").unwrap();

        let res = toml.delete_with_seperator(&String::from("a"), '.');

        assert!(res.is_ok());

        let res = res.unwrap();
        assert!(res.is_none());
    }

    #[test]
    fn test_delete_from_empty_table() {
        let mut toml : Value = toml_from_str(r#"
        [table]
        "#).unwrap();

        let res = toml.delete_with_seperator(&String::from("table.a"), '.');

        assert!(res.is_ok());

        let res = res.unwrap();
        assert!(res.is_none());
    }

    #[test]
    fn test_delete_integer() {
        let mut toml : Value = toml_from_str(r#"
        value = 1
        "#).unwrap();

        let res = toml.delete_with_seperator(&String::from("value"), '.');

        assert!(res.is_ok());

        let res = res.unwrap();
        assert!(res.is_some());
        let res = res.unwrap();
        assert!(is_match!(res, Value::Integer(1)));
    }

    #[test]
    fn test_delete_string() {
        let mut toml : Value = toml_from_str(r#"
        value = "foo"
        "#).unwrap();

        let res = toml.delete_with_seperator(&String::from("value"), '.');

        assert!(res.is_ok());

        let res = res.unwrap();
        assert!(res.is_some());
        let res = res.unwrap();
        assert!(is_match!(res, Value::String(_)));
        match res {
            Value::String(ref s) => assert_eq!("foo", s),
            _ => panic!("What just happened?"),
        }
    }

    #[test]
    fn test_delete_empty_table() {
        let mut toml : Value = toml_from_str(r#"
        [table]
        "#).unwrap();

        let res = toml.delete_with_seperator(&String::from("table"), '.');

        assert!(res.is_ok());

        let res = res.unwrap();
        assert!(res.is_some());
        let res = res.unwrap();
        assert!(is_match!(res, Value::Table(_)));
        match res {
            Value::Table(ref t) => assert!(t.is_empty()),
            _ => panic!("What just happened?"),
        }
    }

    #[test]
    fn test_delete_empty_array() {
        let mut toml : Value = toml_from_str(r#"
        array = []
        "#).unwrap();

        let res = toml.delete_with_seperator(&String::from("array"), '.');

        assert!(res.is_ok());

        let res = res.unwrap();
        assert!(res.is_some());
        let res = res.unwrap();
        assert!(is_match!(res, Value::Array(_)));
        match res {
            Value::Array(ref a) => assert!(a.is_empty()),
            _ => panic!("What just happened?"),
        }
    }

    #[test]
    fn test_delete_nonempty_table() {
        let mut toml : Value = toml_from_str(r#"
        [table]
        a = 1
        "#).unwrap();

        let res = toml.delete_with_seperator(&String::from("table"), '.');

        assert!(res.is_err());

        let res = res.unwrap_err();
        assert!(is_match!(res.kind(), &ErrorKind::CannotDeleteNonEmptyTable(_)));
    }

    #[test]
    fn test_delete_nonempty_array() {
        let mut toml : Value = toml_from_str(r#"
        array = [ 1 ]
        "#).unwrap();

        let res = toml.delete_with_seperator(&String::from("array.[0]"), '.');

        assert!(res.is_err());

        let res = res.unwrap_err();
        assert!(is_match!(res.kind(), &ErrorKind::CannotDeleteNonEmptyArray(_)));
    }

}

<commit_msg>Impl Value::delete_with_seperator to fix test_delete_nonempty_array<commit_after>/// The Toml Delete extensions

use toml::Value;

use tokenizer::Token;
use tokenizer::tokenize_with_seperator;
use error::*;

pub trait TomlValueDeleteExt {

    /// Extension function for deleting a value in the current toml::Value document
    /// using a custom seperator.
    ///
    /// # Semantics
    ///
    /// The function does _not_ delete non-empty data structures. So deleting `array` from
    ///
    /// ```toml
    /// array = [ 1 ]
    /// ```
    ///
    /// does _not_ work.
    ///
    /// # Return value
    ///
    /// If the delete operation worked correctly, `Ok(Option<Value>)` is returned.
    ///
    /// The `Option<Value>` part is `None` if no value was actually removed as there was no value
    /// there. For example, if you're deleting `table.a` and the Table `table` has no key `a`, then
    /// `Ok(None)` is returned. Also, if you're deleting from an Array, but there is nothing in the
    /// array, or the array is shorter than the index you're deleting.
    /// If the delete operation actually removed something from the toml document, this value is
    /// returned as `Ok(Some(Value))`.
    ///
    /// On failure, `Err(e)` is returned
    ///
    fn delete_with_seperator(&mut self, query: &String, sep: char) -> Result<Option<Value>>;

    /// Extension function for inserting a value from the current toml::Value document
    ///
    /// See documentation of `TomlValueinsertExt::insert_with_seperator`
    fn delete(&mut self, query: &String) -> Result<Option<Value>> {
        self.delete_with_seperator(query, '.')
    }

}

impl TomlValueDeleteExt for Value {

    fn delete_with_seperator(&mut self, query: &String, sep: char) -> Result<Option<Value>> {
        use resolver::mut_resolver::resolve;

        let mut tokens = try!(tokenize_with_seperator(query, sep));
        let last_token = tokens.pop_last();

        /// Check whether a structure (Table/Array) is empty. If the Value has not these types,
        /// the default value is returned
        #[inline]
        fn is_empty(val: Option<&Value>, default: bool) -> bool {
            val.map(|v| match v {
                    &Value::Table(ref tab) => tab.is_empty(),
                    &Value::Array(ref arr) => arr.is_empty(),
                    _                      => default
                })
                .unwrap_or(default)
        }

        #[inline]
        fn is_table(val: Option<&Value>) -> bool {
            val.map(|v| is_match!(v, &Value::Table(_))).unwrap_or(false)
        }

        #[inline]
        fn is_array(val: Option<&Value>) -> bool {
            val.map(|v| is_match!(v, &Value::Array(_))).unwrap_or(false)
        }

        #[inline]
        fn name_of_val(val: Option<&Value>) -> &'static str {
            val.map(|v| match v {
                &Value::Array(_)    => "Array",
                &Value::Boolean(_)  => "Boolean",
                &Value::Datetime(_) => "Datetime",
                &Value::Float(_)    => "Float",
                &Value::Integer(_)  => "Integer",
                &Value::String(_)   => "String",
                &Value::Table(_)    => "Table",
            }).unwrap_or("None")
        }

        if last_token.is_none() {
            match self {
                &mut Value::Table(ref mut tab) => {
                    match tokens {
                        Token::Identifier { ident, .. } => {
                            if is_empty(tab.get(&ident), true) {
                                Ok(tab.remove(&ident))
                            } else {
                                if is_table(tab.get(&ident)) {
                                    let kind = ErrorKind::CannotDeleteNonEmptyTable(ident.clone());
                                    Err(Error::from(kind))
                                } else if is_array(tab.get(&ident)) {
                                    let kind = ErrorKind::CannotDeleteNonEmptyArray(ident.clone());
                                    Err(Error::from(kind))
                                } else {
                                    let act = name_of_val(tab.get(&ident));
                                    let tbl = "table";
                                    let k   = ErrorKind::CannotAccessBecauseTypeMismatch(tbl, act);
                                    Err(Error::from(k))
                                }
                            }
                        },
                        _ => Ok(None)
                    }
                },
                _ => unimplemented!()
            }
        } else {
            let mut val = try!(resolve(self, &tokens));
            Ok(None)
        }
    }

}

#[cfg(test)]
mod test {
    use super::*;
    use error::*;
    use toml::Value;
    use toml::from_str as toml_from_str;

    #[test]
    fn test_delete_from_empty_document() {
        let mut toml : Value = toml_from_str("").unwrap();

        let res = toml.delete_with_seperator(&String::from("a"), '.');

        assert!(res.is_ok());

        let res = res.unwrap();
        assert!(res.is_none());
    }

    #[test]
    fn test_delete_from_empty_table() {
        let mut toml : Value = toml_from_str(r#"
        [table]
        "#).unwrap();

        let res = toml.delete_with_seperator(&String::from("table.a"), '.');

        assert!(res.is_ok());

        let res = res.unwrap();
        assert!(res.is_none());
    }

    #[test]
    fn test_delete_integer() {
        let mut toml : Value = toml_from_str(r#"
        value = 1
        "#).unwrap();

        let res = toml.delete_with_seperator(&String::from("value"), '.');

        assert!(res.is_ok());

        let res = res.unwrap();
        assert!(res.is_some());
        let res = res.unwrap();
        assert!(is_match!(res, Value::Integer(1)));
    }

    #[test]
    fn test_delete_string() {
        let mut toml : Value = toml_from_str(r#"
        value = "foo"
        "#).unwrap();

        let res = toml.delete_with_seperator(&String::from("value"), '.');

        assert!(res.is_ok());

        let res = res.unwrap();
        assert!(res.is_some());
        let res = res.unwrap();
        assert!(is_match!(res, Value::String(_)));
        match res {
            Value::String(ref s) => assert_eq!("foo", s),
            _ => panic!("What just happened?"),
        }
    }

    #[test]
    fn test_delete_empty_table() {
        let mut toml : Value = toml_from_str(r#"
        [table]
        "#).unwrap();

        let res = toml.delete_with_seperator(&String::from("table"), '.');

        assert!(res.is_ok());

        let res = res.unwrap();
        assert!(res.is_some());
        let res = res.unwrap();
        assert!(is_match!(res, Value::Table(_)));
        match res {
            Value::Table(ref t) => assert!(t.is_empty()),
            _ => panic!("What just happened?"),
        }
    }

    #[test]
    fn test_delete_empty_array() {
        let mut toml : Value = toml_from_str(r#"
        array = []
        "#).unwrap();

        let res = toml.delete_with_seperator(&String::from("array"), '.');

        assert!(res.is_ok());

        let res = res.unwrap();
        assert!(res.is_some());
        let res = res.unwrap();
        assert!(is_match!(res, Value::Array(_)));
        match res {
            Value::Array(ref a) => assert!(a.is_empty()),
            _ => panic!("What just happened?"),
        }
    }

    #[test]
    fn test_delete_nonempty_table() {
        let mut toml : Value = toml_from_str(r#"
        [table]
        a = 1
        "#).unwrap();

        let res = toml.delete_with_seperator(&String::from("table"), '.');

        assert!(res.is_err());

        let res = res.unwrap_err();
        assert!(is_match!(res.kind(), &ErrorKind::CannotDeleteNonEmptyTable(_)));
    }

    #[test]
    fn test_delete_nonempty_array() {
        let mut toml : Value = toml_from_str(r#"
        array = [ 1 ]
        "#).unwrap();

        let res = toml.delete_with_seperator(&String::from("array"), '.');

        assert!(res.is_err());

        let res = res.unwrap_err();
        assert!(is_match!(res.kind(), &ErrorKind::CannotDeleteNonEmptyArray(_)));
    }

}

<|endoftext|>


<commit_before>#[cfg(unix)]
mod terminal {
    use termion::{ clear, /*color,*/ cursor, /*event, input, scroll, style, */
        /*input::TermRead, raw::IntoRawMode,*/
        //AsyncReader,
        async_stdin };
    use std::io::{ Read, Result, Stdout, Write, stdout };
    use std::thread;
    use std::time::Duration;
    use std::sync::{ Arc, RwLock };
    use super::state::State;
    use agent::Agent;
    use termion::raw::{ IntoRawMode };

    pub struct Terminal<'o> {
        out: &'o mut Write,
        current_view: View,
        exit_requested: bool,
        state: Arc<RwLock<State>>
    }

    impl<'o> Terminal<'o> {
        /// Create a new `Terminal` wrapper around the mutable Write reference (which is assumed to be
        /// an ANSI terminal).
        pub fn new<W: Write>(out: &'o mut W, state: Arc<RwLock<State>>) -> Self {
            Terminal {
                out,
                state,
                current_view: View::AgentList,
                exit_requested: false
            }
        }

        pub fn stdout() -> Stdout {
            stdout().into_raw_mode().unwrap()
        }

        /*
        fn clear(&mut self) {
            write!(self.out, "{}", clear::All).unwrap();
        }

        fn reposition(&mut self) {
            write!(self.out, "{}", cursor::Goto(1, 1)).unwrap();
        }

        fn draw_frame(&mut self, title: String, width: u16, height: u16, lines: Vec<String>) {
            let top_frame = format!("+{:-^32}+", title);
            write!(self.out, "{}", top_frame);
        }

        /// Return the size of the terminal in terms of number of columns and rows.
        fn size(&mut self) -> Result<(u16, u16)> {
            ::termion::terminal_size()
        }
        */

        /// Release the terminal and assume that it isn't controlled any more by this instance.
        pub fn release(&mut self) {
            write!(self.out, "{}", cursor::Show).unwrap();
        }

        pub fn initialise_screen(&mut self) {
            write!(self.out, "{}{}{}", clear::All, cursor::Goto(1, 1), cursor::Hide).unwrap();
        }

        pub fn start(&mut self) {
            // input isn't a top priority so we want to read keyboard events asynchronously to minimise
            // the amount of time spent on blocking calls.
            let mut stdin = async_stdin().bytes();

            // print out the initial screen, this may be changed later at the user's request
            self.render_view();

            while !self.exit_requested {
                // we don't want to repaint the whole screen every time (it would be expensive) so we'll
                // just redraw the parts that may actually change
                self.update_view();

                if let Some(Ok(read_byte)) = stdin.next() {
                    self.handle_input(read_byte);
                }

                self.out.flush().unwrap();

                // a 50ms sleep should be low enough in order to provide a comfortable user experience
                // and keep a relatively low cpu usage
                thread::sleep(Duration::from_millis(50));
            }
        }

        fn handle_input(&mut self, read_byte: u8) {
            match read_byte {
                b'q' => self.exit_requested = true,
                _ => ()
            }
        }

        /// `render_view` issues a full repaint of the screen drawing out the selected view based on the
        /// current state.
        fn render_view(&mut self) {
            match self.current_view {
                View::AgentList => self.render_agent_list()
            }
        }

        fn update_view(&mut self) {
            if let Ok(state) = self.state.try_read() {
                let output = match self.current_view {
                    View::AgentList => self.update_agent_list(&state.registered_agents),
                };

                write!(self.out, "{}", output).unwrap();
            }
        }

        fn render_agent_list(&mut self) {
            self.initialise_screen();
            write!(self.out, "{:32} {:20} {:10}", "NAME", "ADDRESS", "APP").unwrap();
            self.update_view();
        }

        fn update_agent_list(&self, agents: &Vec<Agent>) -> String {
            let mut output = format!("{}", cursor::Goto(1, 2));

            for agent in agents {
                output += &format!("{:32} {:20} {:10}", agent.agent_id, agent.address, agent.app);
            }

            output
        }

    }

    enum View {
        AgentList
    }

    impl<'o> Drop for Terminal<'o> {
        /// When this instance goes out of scope, we'll release the terminal
        fn drop(&mut self) {
            self.release();
        }
    }

    #[cfg(test)]
    mod tests {

        use super::*;
        use std::io::BufWriter;

        #[test]
        fn test_initialise() {
            compare(vec![ ],
            //           <------- show cursor --->
                    vec![ 27, 91, 63, 50, 53, 104 ]);
        }

        #[test]
        fn test_clear() {
            compare(vec![ (|t| t.clear()) ],
            //            < clear screen> <------- show cursor --->
                    vec![ 27, 91, 50, 74, 27, 91, 63, 50, 53, 104 ]);
        }

        fn compare(actions: Vec<fn(&mut Terminal) -> ()>, expected: Vec<u8>) {
            let buf = vec![];
            let mut bw = BufWriter::new(buf);
            let mut state = State::new();
            let mut state = Arc::new(RwLock::new(state));

            {
                let mut t = Terminal::new(&mut bw, state.clone());

                for a in actions {
                    a(&mut t);
                }
            }

            assert_eq!(expected, bw.into_inner().unwrap());

        }
    }
}

#[cfg(windows)]
pub mod terminal {
    use std::io::{ Stdout, Write, stdout };
    use std::sync::{ Arc, RwLock };
    use ::state::State;

    pub struct Terminal<'o> {
        out: &'o mut Write
    }

    impl<'o> Terminal<'o> {
        pub fn stdout() -> Stdout {
            stdout()
        }

        pub fn new<W: Write>(out: &'o mut W, _state: Arc<RwLock<State>>) -> Self {
            Self {
                out: out
            }
        }

        pub fn start(&mut self) {
            writeln!(self.out, "Terminal started").unwrap();
        }
    }
}
<commit_msg>Minor updates<commit_after>#[cfg(unix)]
mod terminal {
    use termion::{ clear, /*color,*/ cursor, /*event, input, scroll, style, */
        /*input::TermRead, raw::IntoRawMode,*/
        //AsyncReader,
        async_stdin };
    use std::io::{ Read, Result, Stdout, Write, stdout };
    use std::thread;
    use std::time::Duration;
    use std::sync::{ Arc, RwLock };
    use super::state::State;
    use agent::Agent;
    use termion::raw::{ IntoRawMode };

    pub struct Terminal<'o> {
        out: &'o mut Write,
        current_view: View,
        exit_requested: bool,
        state: Arc<RwLock<State>>
    }

    impl<'o> Terminal<'o> {
        /// Create a new `Terminal` wrapper around the mutable Write reference (which is assumed to be
        /// an ANSI terminal).
        pub fn new<W: Write>(out: &'o mut W, state: Arc<RwLock<State>>) -> Self {
            Terminal {
                out,
                state,
                current_view: View::AgentList,
                exit_requested: false
            }
        }

        pub fn stdout() -> Stdout {
            stdout().into_raw_mode().unwrap()
        }

        /*
        fn clear(&mut self) {
            write!(self.out, "{}", clear::All).unwrap();
        }

        fn reposition(&mut self) {
            write!(self.out, "{}", cursor::Goto(1, 1)).unwrap();
        }

        fn draw_frame(&mut self, title: String, width: u16, height: u16, lines: Vec<String>) {
            let top_frame = format!("+{:-^32}+", title);
            write!(self.out, "{}", top_frame);
        }

        /// Return the size of the terminal in terms of number of columns and rows.
        fn size(&mut self) -> Result<(u16, u16)> {
            ::termion::terminal_size()
        }
        */

        /// Release the terminal and assume that it isn't controlled any more by this instance.
        pub fn release(&mut self) {
            write!(self.out, "{}", cursor::Show).unwrap();
        }

        pub fn initialise_screen(&mut self) {
            write!(self.out, "{}{}{}", clear::All, cursor::Goto(1, 1), cursor::Hide).unwrap();
        }

        pub fn start(&mut self) {
            // input isn't a top priority so we want to read keyboard events asynchronously to minimise
            // the amount of time spent on blocking calls.
            let mut stdin = async_stdin().bytes();

            // print out the initial screen, this may be changed later at the user's request
            self.render_view();

            while !self.exit_requested {
                // we don't want to repaint the whole screen every time (it would be expensive) so we'll
                // just redraw the parts that may actually change
                self.update_view();

                if let Some(Ok(read_byte)) = stdin.next() {
                    self.handle_input(read_byte);
                }

                self.out.flush().unwrap();

                // a 50ms sleep should be low enough in order to provide a comfortable user experience
                // and keep a relatively low cpu usage
                thread::sleep(Duration::from_millis(50));
            }
        }

        fn handle_input(&mut self, read_byte: u8) {
            match read_byte {
                b'q' => self.exit_requested = true,
                _ => ()
            }
        }

        /// `render_view` issues a full repaint of the screen drawing out the selected view based on the
        /// current state.
        fn render_view(&mut self) {
            match self.current_view {
                View::AgentList => self.render_agent_list()
            }
        }

        fn update_view(&mut self) {
            if let Ok(state) = self.state.try_read() {
                let output = match self.current_view {
                    View::AgentList => self.update_agent_list(&state.registered_agents),
                };

                write!(self.out, "{}", output).unwrap();
            }
        }

        fn render_agent_list(&mut self) {
            self.initialise_screen();
            write!(self.out, "{:32} {:20} {:10}", "NAME", "ADDRESS", "APP").unwrap();
            self.update_view();
        }

        fn update_agent_list(&self, agents: &Vec<Agent>) -> String {
            let mut output = format!("{}", cursor::Goto(1, 2));

            for agent in agents {
                output += &format!("{:32} {:20} {:10}", agent.agent_id, agent.address, agent.app);
            }

            output
        }

    }

    enum View {
        AgentList
    }

    impl<'o> Drop for Terminal<'o> {
        /// When this instance goes out of scope, we'll release the terminal
        fn drop(&mut self) {
            self.release();
        }
    }

    #[cfg(test)]
    mod tests {

        use super::*;
        use std::io::BufWriter;

        #[test]
        fn test_initialise() {
            compare(vec![ ],
            //           <------- show cursor --->
                    vec![ 27, 91, 63, 50, 53, 104 ]);
        }

        #[test]
        fn test_clear() {
            compare(vec![ (|t| t.clear()) ],
            //            < clear screen> <------- show cursor --->
                    vec![ 27, 91, 50, 74, 27, 91, 63, 50, 53, 104 ]);
        }

        fn compare(actions: Vec<fn(&mut Terminal) -> ()>, expected: Vec<u8>) {
            let buf = vec![];
            let mut bw = BufWriter::new(buf);
            let mut state = State::new();
            let mut state = Arc::new(RwLock::new(state));

            {
                let mut t = Terminal::new(&mut bw, state.clone());

                for a in actions {
                    a(&mut t);
                }
            }

            assert_eq!(expected, bw.into_inner().unwrap());

        }
    }
}

#[cfg(windows)]
pub mod terminal {
    use std::io::{ Stdout, Write, stdout };
    use std::sync::{ Arc, RwLock };
    use ::state::State;

    pub struct Terminal<'o> {
        out: &'o mut Write,
        state: Arc<RwLock<State>>
    }

    impl<'o> Terminal<'o> {
        pub fn stdout() -> Stdout {
            stdout()
        }

        pub fn new<W: Write>(out: &'o mut W, state: Arc<RwLock<State>>) -> Self {
            Self {
                out,
                state
            }
        }

        pub fn start(&mut self) {
            if let Ok(_) = self.state.read() {
                writeln!(self.out, "Terminal started").unwrap();
            }
        }
    }
}
<|endoftext|>


<commit_before>// aux-build:my-core.rs
// build-aux-docs
// ignore-cross-compile
// ignore-tidy-linelength

#![deny(intra_doc_link_resolution_failure)]
#![feature(no_core, lang_items)]
#![no_core]
#![crate_type = "rlib"]

// @has intra_link_prim_methods_external_core/index.html
// @has - '//*[@id="main"]//a[@href="https://doc.rust-lang.org/nightly/std/primitive.char.html"]' 'char'
// @has - '//*[@id="main"]//a[@href="https://doc.rust-lang.org/nightly/std/primitive.char.html#method.len_utf8"]' 'char::len_utf8'

//! A [`char`] and its [`char::len_utf8`].

extern crate my_core;
<commit_msg>Avoid running test on Windows platforms<commit_after>// aux-build:my-core.rs
// build-aux-docs
// ignore-cross-compile
// ignore-windows
// ignore-tidy-linelength

#![deny(intra_doc_link_resolution_failure)]
#![feature(no_core, lang_items)]
#![no_core]
#![crate_type = "rlib"]

// @has intra_link_prim_methods_external_core/index.html
// @has - '//*[@id="main"]//a[@href="https://doc.rust-lang.org/nightly/std/primitive.char.html"]' 'char'
// @has - '//*[@id="main"]//a[@href="https://doc.rust-lang.org/nightly/std/primitive.char.html#method.len_utf8"]' 'char::len_utf8'

//! A [`char`] and its [`char::len_utf8`].

extern crate my_core;
<|endoftext|>