<commit_before>use super::Chip8;
use std::slice::bytes::copy_memory;

// 0nnn - SYS addr
// Jump to a machine code routine at nnn.
//
// This instruction is only used on the old computers on which Chip-8 was
// originally implemented. It is ignored by modern interpreters.
pub fn jump_to_sys_routine(slf: &mut Chip8, addr: uint) {
    // Do nothing
}

// 00E0 - CLS
// Clear the display.
pub fn clear_display(slf: &mut Chip8) {
    for px in slf.display.mut_iter() {
        *px = 0;
    }
}

// 00EE - RET
// Return from a subroutine.
//
// The interpreter sets the program counter to the address at the top of
// the stack, then subtracts 1 from the stack pointer.
pub fn ret_from_subroutine(slf: &mut Chip8) {
    slf.pc = slf.stack[slf.sp as uint];
    slf.sp -= 1;
}

// 1nnn - JP addr
// Jump to location nnn.
//
// The interpreter sets the program counter to nnn.
pub fn jump_addr(slf: &mut Chip8, addr: u16) {
    slf.pc = addr;
}

// 2nnn - CALL addr
// Call subroutine at nnn.
//
// The interpreter increments the stack pointer, then puts the current PC
// on the top of the stack. The PC is then set to nnn.
pub fn call_subroutine(slf: &mut Chip8, addr: uint) {
    slf.sp += 1;
    slf.stack[slf.sp as uint] = slf.pc;
    slf.pc = addr as u16;
}

// 3xkk - SE Vx, byte
// Skip next instruction if Vx = kk.
//
// The interpreter compares register Vx to kk, and if they are equal,
// increments the program counter by 2.
pub fn skip_next_vx_eq(slf: &mut Chip8, x: uint, to: u8) {
    if slf.v[x] == to {
        slf.pc += 2;
    }
}

// 4xkk - SNE Vx, byte
// Skip next instruction if Vx != kk.
//
// The interpreter compares register Vx to kk, and if they are not equal,
// increments the program counter by 2.
pub fn skip_next_vx_ne(slf: &mut Chip8, x: uint, to: u8) {
    if slf.v[x] != to {
        slf.pc += 2;
    }
}

// 5xy0 - SE Vx, Vy
// Skip next instruction if Vx = Vy.
//
// The interpreter compares register Vx to register Vy, and if they are
// equal, increments the program counter by 2.
pub fn skip_next_vx_eq_vy(slf: &mut Chip8, x: uint, y: uint) {
    if slf.v[x] == slf.v[y] {
        slf.pc += 2;
    }
}

// 6xkk - LD Vx, byte
// Set Vx = kk.
//
// The interpreter puts the value kk into register Vx.
pub fn set_vx_byte(slf: &mut Chip8, x: uint, byte: u8) {
    slf.v[x] = byte;
}

// 7xkk - ADD Vx, byte
// Set Vx = Vx + kk.
//
// Adds the value kk to the value of register Vx, then stores the
// result in Vx.
pub fn add_vx_byte(slf: &mut Chip8, x: uint, byte: u8) {
    slf.v[x] += byte;
}

// 8xy0 - LD Vx, Vy
// Set Vx = Vy.
//
// Stores the value of register Vy in register Vx.
pub fn set_vx_to_vy(slf: &mut Chip8, x: uint, y: uint) {
    slf.v[x] = slf.v[y];
}

pub fn set_i(slf: &mut Chip8, to: u16) {
    slf.i = to;
}

pub fn set_vx_rand_and(slf: &mut Chip8, x: uint, to: u8) {
    use std::rand::{task_rng, Rng};
    let mut rgen = task_rng();
    slf.v[x] = rgen.gen::<u8>() & to;
}

pub fn display_sprite(slf: &mut Chip8, vx: uint, vy: uint, n: uint) {
    use super::{ DISPLAY_WIDTH, DISPLAY_HEIGHT };
    let x_off = slf.v[vx] as uint;
    let y_off = slf.v[vy] as uint * DISPLAY_WIDTH;
    let offset = x_off + y_off;

    for mut y in range(0u, n) {
        if y >= DISPLAY_HEIGHT {
            y = y - DISPLAY_HEIGHT;
        }
        let b = slf.ram[slf.i as uint + y];
        for mut x in range(0u, 8) {
            if x >= DISPLAY_WIDTH {
                x = x - DISPLAY_WIDTH;
            }
            let idx = offset + (y * DISPLAY_WIDTH) + x;
            if idx < DISPLAY_WIDTH * DISPLAY_HEIGHT {
                slf.display[idx] ^= b & (0b10000000 >> x);
            } else {
                println!("Warning: Out of bounds VRAM write: {}", idx);
            }
        }
    }

    (slf.draw_callback)(slf.display);
}

pub fn add_vx_vy(slf: &mut Chip8, x: uint, y: uint) {
    let result = (slf.v[x] + slf.v[y]) as u16;
    slf.v[0xF] = (result > 255) as u8;
    slf.v[x] = result as u8;
}

pub fn sub_vx_vy(slf: &mut Chip8, x: uint, y: uint) {
    slf.v[0xF] = (slf.v[x] > slf.v[y]) as u8;
    slf.v[x] -= slf.v[y];
}

pub fn add_vx_to_i(slf: &mut Chip8, x: uint) {
    slf.i += x as u16;
}

pub fn copy_v0_through_vx_to_mem(slf: &mut Chip8, x: uint) {
    if x == 0 {
        return;
    }
    copy_memory(slf.ram.mut_slice(slf.i as uint, slf.i as uint + x),
                slf.v.slice(0, x));
}

pub fn read_v0_through_vx_from_mem(slf: &mut Chip8, x: uint) {
    if x == 0 {
        return;
    }
    copy_memory(slf.v.mut_slice(0, x),
                slf.ram.slice(slf.i as uint, slf.i as uint + x));
}

pub fn skip_next_vx_ne_vy(slf: &mut Chip8, x: uint, y: uint) {
    if slf.v[x] != slf.v[y] {
        slf.pc += 2;
    }
}

// Fx33 - LD B, Vx
// Store BCD representation of Vx in memory locations I, I+1, and I+2.
//
// The interpreter takes the decimal value of Vx, and places the hundreds
// digit in memory at location in I, the tens digit at location I+1,
// and the ones digit at location I+2.
pub fn store_bcd_of_vx_to_i(slf: &mut Chip8, x: uint) {
    let num = slf.v[x];
    let h = num / 100;
    let t = (num - h * 100) / 10;
    let o = (num - h * 100 - t * 10);
    slf.ram[slf.i as uint] = h;
    slf.ram[slf.i as uint + 1] = t;
    slf.ram[slf.i as uint + 2] = o;
}

// Fx29 - LD F, Vx
// Set I = location of sprite for digit Vx.
//
// The value of I is set to the location for the hexadecimal sprite
// corresponding to the value of Vx. See section 2.4, Display, for more
// information on the Chip-8 hexadecimal font.
//
// For crusty-chip, the fontset is stored at 0x000
pub fn set_i_to_loc_of_digit_vx(slf: &mut Chip8, x: uint) {
    slf.i = (slf.v[x] * 5) as u16;
}

// Fx0A - LD Vx, K
// Wait for a key press, store the value of the key in Vx.
//
// All execution stops until a key is pressed, then the value of that key
// is stored in Vx.
pub fn wait_for_keypress_store_in_vx(slf: &mut Chip8, x: uint) {
    slf.keypress_wait.wait = true;
    slf.keypress_wait.vx = x;
}

// Fx15 - LD DT, Vx
// Set delay timer = Vx.
//
// DT is set equal to the value of Vx.
pub fn set_delay_timer(slf: &mut Chip8, x: u8) {
    slf.delay_timer = x;
}

// Fx18 - LD ST, Vx
// Set sound timer = Vx.
//
// ST is set equal to the value of Vx.
pub fn set_sound_timer(slf: &mut Chip8, x: u8) {
    slf.sound_timer = x;
}

// Fx07 - LD Vx, DT
// Set Vx = delay timer value.
//
// The value of DT is placed into Vx.
pub fn set_vx_to_delay_timer(slf: &mut Chip8, x: uint) {
    slf.v[x] = slf.delay_timer;
}

// 8xy3 - XOR Vx, Vy
// Set Vx = Vx XOR Vy.
//
// Performs a bitwise exclusive OR on the values of Vx and Vy, then stores
// the result in Vx. An exclusive OR compares the corrseponding bits from
// two values, and if the bits are not both the same, then the
// corresponding bit in the result is set to 1. Otherwise, it is 0.
pub fn set_vx_to_vx_xor_vy(slf: &mut Chip8, x: uint, y: uint) {
    slf.v[x] ^= slf.v[y];
}

// 8xyE - SHL Vx {, Vy}
// Set Vx = Vx SHL 1.
//
// If the most-significant bit of Vx is 1, then VF is set to 1, otherwise
// to 0. Then Vx is multiplied by 2.
pub fn set_vx_to_vx_shl_1(slf: &mut Chip8, x: uint) {
    // TODO: Is this just a left shift by 1?
    slf.v[x] <<= 1;
}

// 8xy2 - AND Vx, Vy
// Set Vx = Vx AND Vy.
//
// Performs a bitwise AND on the values of Vx and Vy, then stores the
// result in Vx. A bitwise AND compares the corrseponding bits from two
// values, and if both bits are 1, then the same bit in the result is also
// 1. Otherwise, it is 0.
pub fn set_vx_to_vx_and_vy(slf: &mut Chip8, x: uint, y: uint) {
    slf.v[x] &= slf.v[y];
}

// ExA1 - SKNP Vx
// Skip next instruction if key with the value of Vx is not pressed.
//
// Checks the keyboard, and if the key corresponding to the value of
// Vx is currently in the up position, PC is increased by 2.
pub fn skip_next_key_vx_not_pressed(slf: &mut Chip8, x: uint) {
    if !slf.keys[slf.v[x] as uint] {
        slf.pc += 2;
    }
}<commit_msg>ops: Rename `slf` args to `ch8`<commit_after>use super::Chip8;
use std::slice::bytes::copy_memory;

// 0nnn - SYS addr
// Jump to a machine code routine at nnn.
//
// This instruction is only used on the old computers on which Chip-8 was
// originally implemented. It is ignored by modern interpreters.
pub fn jump_to_sys_routine(ch8: &mut Chip8, addr: uint) {
    // Do nothing
}

// 00E0 - CLS
// Clear the display.
pub fn clear_display(ch8: &mut Chip8) {
    for px in ch8.display.mut_iter() {
        *px = 0;
    }
}

// 00EE - RET
// Return from a subroutine.
//
// The interpreter sets the program counter to the address at the top of
// the stack, then subtracts 1 from the stack pointer.
pub fn ret_from_subroutine(ch8: &mut Chip8) {
    ch8.pc = ch8.stack[ch8.sp as uint];
    ch8.sp -= 1;
}

// 1nnn - JP addr
// Jump to location nnn.
//
// The interpreter sets the program counter to nnn.
pub fn jump_addr(ch8: &mut Chip8, addr: u16) {
    ch8.pc = addr;
}

// 2nnn - CALL addr
// Call subroutine at nnn.
//
// The interpreter increments the stack pointer, then puts the current PC
// on the top of the stack. The PC is then set to nnn.
pub fn call_subroutine(ch8: &mut Chip8, addr: uint) {
    ch8.sp += 1;
    ch8.stack[ch8.sp as uint] = ch8.pc;
    ch8.pc = addr as u16;
}

// 3xkk - SE Vx, byte
// Skip next instruction if Vx = kk.
//
// The interpreter compares register Vx to kk, and if they are equal,
// increments the program counter by 2.
pub fn skip_next_vx_eq(ch8: &mut Chip8, x: uint, to: u8) {
    if ch8.v[x] == to {
        ch8.pc += 2;
    }
}

// 4xkk - SNE Vx, byte
// Skip next instruction if Vx != kk.
//
// The interpreter compares register Vx to kk, and if they are not equal,
// increments the program counter by 2.
pub fn skip_next_vx_ne(ch8: &mut Chip8, x: uint, to: u8) {
    if ch8.v[x] != to {
        ch8.pc += 2;
    }
}

// 5xy0 - SE Vx, Vy
// Skip next instruction if Vx = Vy.
//
// The interpreter compares register Vx to register Vy, and if they are
// equal, increments the program counter by 2.
pub fn skip_next_vx_eq_vy(ch8: &mut Chip8, x: uint, y: uint) {
    if ch8.v[x] == ch8.v[y] {
        ch8.pc += 2;
    }
}

// 6xkk - LD Vx, byte
// Set Vx = kk.
//
// The interpreter puts the value kk into register Vx.
pub fn set_vx_byte(ch8: &mut Chip8, x: uint, byte: u8) {
    ch8.v[x] = byte;
}

// 7xkk - ADD Vx, byte
// Set Vx = Vx + kk.
//
// Adds the value kk to the value of register Vx, then stores the
// result in Vx.
pub fn add_vx_byte(ch8: &mut Chip8, x: uint, byte: u8) {
    ch8.v[x] += byte;
}

// 8xy0 - LD Vx, Vy
// Set Vx = Vy.
//
// Stores the value of register Vy in register Vx.
pub fn set_vx_to_vy(ch8: &mut Chip8, x: uint, y: uint) {
    ch8.v[x] = ch8.v[y];
}

pub fn set_i(ch8: &mut Chip8, to: u16) {
    ch8.i = to;
}

pub fn set_vx_rand_and(ch8: &mut Chip8, x: uint, to: u8) {
    use std::rand::{task_rng, Rng};
    let mut rgen = task_rng();
    ch8.v[x] = rgen.gen::<u8>() & to;
}

pub fn display_sprite(ch8: &mut Chip8, vx: uint, vy: uint, n: uint) {
    use super::{ DISPLAY_WIDTH, DISPLAY_HEIGHT };
    let x_off = ch8.v[vx] as uint;
    let y_off = ch8.v[vy] as uint * DISPLAY_WIDTH;
    let offset = x_off + y_off;

    for mut y in range(0u, n) {
        if y >= DISPLAY_HEIGHT {
            y = y - DISPLAY_HEIGHT;
        }
        let b = ch8.ram[ch8.i as uint + y];
        for mut x in range(0u, 8) {
            if x >= DISPLAY_WIDTH {
                x = x - DISPLAY_WIDTH;
            }
            let idx = offset + (y * DISPLAY_WIDTH) + x;
            if idx < DISPLAY_WIDTH * DISPLAY_HEIGHT {
                ch8.display[idx] ^= b & (0b10000000 >> x);
            } else {
                println!("Warning: Out of bounds VRAM write: {}", idx);
            }
        }
    }

    (ch8.draw_callback)(ch8.display);
}

pub fn add_vx_vy(ch8: &mut Chip8, x: uint, y: uint) {
    let result = (ch8.v[x] + ch8.v[y]) as u16;
    ch8.v[0xF] = (result > 255) as u8;
    ch8.v[x] = result as u8;
}

pub fn sub_vx_vy(ch8: &mut Chip8, x: uint, y: uint) {
    ch8.v[0xF] = (ch8.v[x] > ch8.v[y]) as u8;
    ch8.v[x] -= ch8.v[y];
}

pub fn add_vx_to_i(ch8: &mut Chip8, x: uint) {
    ch8.i += x as u16;
}

pub fn copy_v0_through_vx_to_mem(ch8: &mut Chip8, x: uint) {
    if x == 0 {
        return;
    }
    copy_memory(ch8.ram.mut_slice(ch8.i as uint, ch8.i as uint + x),
                ch8.v.slice(0, x));
}

pub fn read_v0_through_vx_from_mem(ch8: &mut Chip8, x: uint) {
    if x == 0 {
        return;
    }
    copy_memory(ch8.v.mut_slice(0, x),
                ch8.ram.slice(ch8.i as uint, ch8.i as uint + x));
}

pub fn skip_next_vx_ne_vy(ch8: &mut Chip8, x: uint, y: uint) {
    if ch8.v[x] != ch8.v[y] {
        ch8.pc += 2;
    }
}

// Fx33 - LD B, Vx
// Store BCD representation of Vx in memory locations I, I+1, and I+2.
//
// The interpreter takes the decimal value of Vx, and places the hundreds
// digit in memory at location in I, the tens digit at location I+1,
// and the ones digit at location I+2.
pub fn store_bcd_of_vx_to_i(ch8: &mut Chip8, x: uint) {
    let num = ch8.v[x];
    let h = num / 100;
    let t = (num - h * 100) / 10;
    let o = (num - h * 100 - t * 10);
    ch8.ram[ch8.i as uint] = h;
    ch8.ram[ch8.i as uint + 1] = t;
    ch8.ram[ch8.i as uint + 2] = o;
}

// Fx29 - LD F, Vx
// Set I = location of sprite for digit Vx.
//
// The value of I is set to the location for the hexadecimal sprite
// corresponding to the value of Vx. See section 2.4, Display, for more
// information on the Chip-8 hexadecimal font.
//
// For crusty-chip, the fontset is stored at 0x000
pub fn set_i_to_loc_of_digit_vx(ch8: &mut Chip8, x: uint) {
    ch8.i = (ch8.v[x] * 5) as u16;
}

// Fx0A - LD Vx, K
// Wait for a key press, store the value of the key in Vx.
//
// All execution stops until a key is pressed, then the value of that key
// is stored in Vx.
pub fn wait_for_keypress_store_in_vx(ch8: &mut Chip8, x: uint) {
    ch8.keypress_wait.wait = true;
    ch8.keypress_wait.vx = x;
}

// Fx15 - LD DT, Vx
// Set delay timer = Vx.
//
// DT is set equal to the value of Vx.
pub fn set_delay_timer(ch8: &mut Chip8, x: u8) {
    ch8.delay_timer = x;
}

// Fx18 - LD ST, Vx
// Set sound timer = Vx.
//
// ST is set equal to the value of Vx.
pub fn set_sound_timer(ch8: &mut Chip8, x: u8) {
    ch8.sound_timer = x;
}

// Fx07 - LD Vx, DT
// Set Vx = delay timer value.
//
// The value of DT is placed into Vx.
pub fn set_vx_to_delay_timer(ch8: &mut Chip8, x: uint) {
    ch8.v[x] = ch8.delay_timer;
}

// 8xy3 - XOR Vx, Vy
// Set Vx = Vx XOR Vy.
//
// Performs a bitwise exclusive OR on the values of Vx and Vy, then stores
// the result in Vx. An exclusive OR compares the corrseponding bits from
// two values, and if the bits are not both the same, then the
// corresponding bit in the result is set to 1. Otherwise, it is 0.
pub fn set_vx_to_vx_xor_vy(ch8: &mut Chip8, x: uint, y: uint) {
    ch8.v[x] ^= ch8.v[y];
}

// 8xyE - SHL Vx {, Vy}
// Set Vx = Vx SHL 1.
//
// If the most-significant bit of Vx is 1, then VF is set to 1, otherwise
// to 0. Then Vx is multiplied by 2.
pub fn set_vx_to_vx_shl_1(ch8: &mut Chip8, x: uint) {
    // TODO: Is this just a left shift by 1?
    ch8.v[x] <<= 1;
}

// 8xy2 - AND Vx, Vy
// Set Vx = Vx AND Vy.
//
// Performs a bitwise AND on the values of Vx and Vy, then stores the
// result in Vx. A bitwise AND compares the corrseponding bits from two
// values, and if both bits are 1, then the same bit in the result is also
// 1. Otherwise, it is 0.
pub fn set_vx_to_vx_and_vy(ch8: &mut Chip8, x: uint, y: uint) {
    ch8.v[x] &= ch8.v[y];
}

// ExA1 - SKNP Vx
// Skip next instruction if key with the value of Vx is not pressed.
//
// Checks the keyboard, and if the key corresponding to the value of
// Vx is currently in the up position, PC is increased by 2.
pub fn skip_next_key_vx_not_pressed(ch8: &mut Chip8, x: uint) {
    if !ch8.keys[ch8.v[x] as uint] {
        ch8.pc += 2;
    }
}<|endoftext|>


<commit_before>use std::collections::HashMap;
use std::ops::{Sub, Div};
use itertools::Itertools;
use error::*;
use eval::Env;
use types::{Expr, List, Vector, Function, Lambda};
use util::*;

pub fn env<'a>() -> Env<'a> {
    let table: Vec<(&str, Lambda)> = vec![
        ("not", not),
        ("or", or),
        ("and", and),
        ("print", print),
        ("+", add),
        ("-", sub),
        ("*", mul),
        ("/", div),
        ("=", equal),
        ("<", less),
        ("<=", less_eq),
        (">", greater),
        (">=", greater_eq),
        ("first", first),
        ("rest", rest),
        ("cons", cons),
        ("exit", exit),
    ];

    let builtins = table
        .into_iter()
        .map(|(symbol, f)| {
            (
                String::from(symbol),
                Expr::from(Function::builtin(symbol, f)),
            )
        })
        .collect::<HashMap<_, _>>();

    Env::new(builtins, None)
}

fn numeric_op<F, G>(name: &str, args: &[Expr], fn_int: F, fn_flt: G) -> Result<Expr>
where
    F: Fn(&[i64]) -> Result<i64>,
    G: Fn(&[f64]) -> Result<f64>,
{
    // Check all arguments are numeric
    if args.iter().all(Expr::is_num) {
        if args.iter().any(Expr::is_flt) {
            // If any are float, promote to float
            let floats = args.iter()
                .map(|x| match x {
                    &Expr::Int(y) => y as f64,
                    &Expr::Flt(y) => y,
                    _ => unreachable!(),
                })
                .collect::<Vec<_>>();
            fn_flt(&floats).map(Expr::from)
        } else {
            // Otherwise perform integer operation
            let ints = args.iter()
                .map(|x| match x {
                    &Expr::Int(y) => y,
                    _ => unreachable!(),
                })
                .collect::<Vec<_>>();
            fn_int(&ints).map(Expr::from)
        }
    } else {
        Err(format!("#[{}] expected numeric", name).into())
    }
}

pub fn add(args: &[Expr], _env: &Env) -> Result<Expr> {
    numeric_op("+", args,
        |ints| Ok(ints.iter().sum::<i64>()),
        |floats| Ok(floats.iter().sum::<f64>())
    )
}

pub fn sub(args: &[Expr], _env: &Env) -> Result<Expr> {
    ensure_min_args("-", args, 1)?;

    // Check all arguments are numeric
    if !args.iter().all(Expr::is_num) {
        return Err("#[-] expected numeric".into());
    }

    // If one argument, negate and return
    if args.len() == 1 {
        return match args[0] {
            Expr::Int(x) => Ok(Expr::from(-x)),
            Expr::Flt(x) => Ok(Expr::from(-x)),
            _ => Err("invalid type".into())
        }
    }

    numeric_op("-", args,
        |ints| Ok(ints[1..].iter().fold(ints[0], Sub::sub)),
        |floats| Ok(floats[1..].iter().fold(floats[0], Sub::sub))
    )
}

pub fn mul(args: &[Expr], _env: &Env) -> Result<Expr> {
    numeric_op("*", args,
        |ints| Ok(ints.iter().product::<i64>()),
        |floats| Ok(floats.iter().product::<f64>())
    )
}

pub fn div(args: &[Expr], _env: &Env) -> Result<Expr> {
    ensure_min_args("[/]", args, 1)?;

    // Check all arguments are numeric
    if !args.iter().all(Expr::is_num) {
        return Err("#[-] expected numeric".into());
    }

    // If one argument, invert and return
    if args.len() == 1 {
        return match args[0] {
            Expr::Int(x) => Ok(Expr::from(1.0f64 / x as f64)),
            Expr::Flt(x) => Ok(Expr::from(-x)),
            _ => Err("invalid type".into())
        }
    }

    let int_div = |ints: &[i64]| {
        ints[1..].iter()
            .map(|&x| if x == 0i64 { Err("division by zero".into()) } else { Ok(x) })
            .fold_results(ints[0], Div::div)
    };

    numeric_op("/", args,
        int_div,
        |floats| Ok(floats[1..].iter().fold(floats[0], Div::div))
    )
}

pub fn equal(args: &[Expr], _env: &Env) -> Result<Expr> {
    ensure_args("[=]", args, 2)?;
    Ok(Expr::from(args[0] == args[1]))
}

pub fn less(args: &[Expr], _env: &Env) -> Result<Expr> {
    ensure_args("[<]", args, 2)?;
    match (&args[0], &args[1]) {
        (&Expr::Int(ref a), &Expr::Int(ref b)) => Ok(Expr::from(a < b)),
        (&Expr::Flt(ref a), &Expr::Flt(ref b)) => Ok(Expr::from(a < b)),
        (&Expr::Str(ref a), &Expr::Str(ref b)) => Ok(Expr::from(a < b)),
        _ => Err(
            format!("comparison undefined for: {}, {}", args[0], args[1]).into(),
        ),
    }
}

pub fn less_eq(args: &[Expr], _env: &Env) -> Result<Expr> {
    ensure_args("[<=]", args, 2)?;
    match (&args[0], &args[1]) {
        (&Expr::Int(ref a), &Expr::Int(ref b)) => Ok(Expr::from(a <= b)),
        (&Expr::Flt(ref a), &Expr::Flt(ref b)) => Ok(Expr::from(a <= b)),
        (&Expr::Str(ref a), &Expr::Str(ref b)) => Ok(Expr::from(a <= b)),
        _ => Err(
            format!("comparison undefined for: {}, {}", args[0], args[1]).into(),
        ),
    }
}

pub fn greater(args: &[Expr], _env: &Env) -> Result<Expr> {
    ensure_args("[>]", args, 2)?;
    match (&args[0], &args[1]) {
        (&Expr::Int(ref a), &Expr::Int(ref b)) => Ok(Expr::from(a > b)),
        (&Expr::Flt(ref a), &Expr::Flt(ref b)) => Ok(Expr::from(a > b)),
        (&Expr::Str(ref a), &Expr::Str(ref b)) => Ok(Expr::from(a > b)),
        _ => Err(
            format!("comparison undefined for: {}, {}", args[0], args[1]).into(),
        ),
    }
}

pub fn greater_eq(args: &[Expr], _env: &Env) -> Result<Expr> {
    ensure_args("[>=]", args, 2)?;
    match (&args[0], &args[1]) {
        (&Expr::Int(ref a), &Expr::Int(ref b)) => Ok(Expr::from(a >= b)),
        (&Expr::Flt(ref a), &Expr::Flt(ref b)) => Ok(Expr::from(a >= b)),
        (&Expr::Str(ref a), &Expr::Str(ref b)) => Ok(Expr::from(a >= b)),
        _ => Err(
            format!("comparison undefined for: {}, {}", args[0], args[1]).into(),
        ),
    }
}

pub fn not(args: &[Expr], _env: &Env) -> Result<Expr> {
    ensure_args("[not]", args, 1)?;
    Ok(Expr::from(!args[0].truthiness()))
}

// TODO: convert to special form
pub fn and(args: &[Expr], _env: &Env) -> Result<Expr> {
    args
        .into_iter()
        .map(|a| a.boolean())
        .collect::<Option<Vec<_>>>()
        .ok_or("#[and] expected boolean argument".into())
        .map(|bools| bools.iter().all(|b| *b))
        .map(Expr::from)
}

// TODO: convert to special form
pub fn or(args: &[Expr], _env: &Env) -> Result<Expr> {
    args
        .into_iter()
        .map(|a| a.boolean())
        .collect::<Option<Vec<_>>>()
        .ok_or("#[or] expected boolean argument".into())
        .map(|bools| bools.iter().any(|b| *b))
        .map(Expr::from)
}

pub fn print(args: &[Expr], _env: &Env) -> Result<Expr> {
    ensure_args("#[print]", args, 1)?;
    println!("{}", args[0]);
    Ok(Expr::Nil)
}

pub fn first(args: &[Expr], _env: &Env) -> Result<Expr> {
    ensure_args("#[first]", args, 1)?;

    match &args[0] {
        &Expr::List(ref l) => Ok(l.0.first().cloned().unwrap_or(Expr::Nil)),
        &Expr::Vector(ref q) => Ok(q.0.first().cloned().unwrap_or(Expr::Nil)),
        _ => Err("#[first] expected list".into()),
    }
}

pub fn rest(args: &[Expr], _env: &Env) -> Result<Expr> {
    match &args[0] {
        &Expr::List(ref l) => {
            Ok(
                l.0
                    .split_first()
                    .map(|(_, rest)| Expr::List(List(rest.to_vec())))
                    .unwrap_or(Expr::Nil),
            )
        }
        &Expr::Vector(ref v) => {
            Ok(
                v.0
                    .split_first()
                    .map(|(_, rest)| Expr::Vector(Vector(rest.to_vec())))
                    .unwrap_or(Expr::Nil),
            )
        }
        _ => Err("#[rest] expected list".into()),
    }
}

// (cons item seq)
pub fn cons(args: &[Expr], _env: &Env) -> Result<Expr> {
    ensure_args("cons]", args, 2)?;

    match &args[2] {
        &Expr::List(ref l) => {
            let mut new = l.clone();
            new.0.insert(0, args[1].clone());
            Ok(Expr::List(new))
        }
        &Expr::Vector(ref v) => {
            let mut new = v.clone();
            new.0.push(args[1].clone());
            Ok(Expr::Vector(new))
        }
        _ => Err("#[cons] expected list".into()),
    }
}

// (exit)
pub fn exit(_args: &[Expr], _env: &Env) -> Result<Expr> {
    Err(ErrorKind::Exit(0).into())
}
<commit_msg>Fix division<commit_after>use std::collections::HashMap;
use std::ops::{Sub, Div};
use itertools::Itertools;
use error::*;
use eval::Env;
use types::{Expr, List, Vector, Function, Lambda};
use util::*;

pub fn env<'a>() -> Env<'a> {
    let table: Vec<(&str, Lambda)> = vec![
        ("not", not),
        ("or", or),
        ("and", and),
        ("print", print),
        ("+", add),
        ("-", sub),
        ("*", mul),
        ("/", div),
        ("=", equal),
        ("<", less),
        ("<=", less_eq),
        (">", greater),
        (">=", greater_eq),
        ("first", first),
        ("rest", rest),
        ("cons", cons),
        ("exit", exit),
    ];

    let builtins = table
        .into_iter()
        .map(|(symbol, f)| {
            (
                String::from(symbol),
                Expr::from(Function::builtin(symbol, f)),
            )
        })
        .collect::<HashMap<_, _>>();

    Env::new(builtins, None)
}

fn numeric_op<F, G>(name: &str, args: &[Expr], fn_int: F, fn_flt: G) -> Result<Expr>
where
    F: Fn(&[i64]) -> Result<i64>,
    G: Fn(&[f64]) -> Result<f64>,
{
    // Check all arguments are numeric
    if args.iter().all(Expr::is_num) {
        if args.iter().any(Expr::is_flt) {
            // If any are float, promote to float
            let floats = args.iter()
                .map(|x| match x {
                    &Expr::Int(y) => y as f64,
                    &Expr::Flt(y) => y,
                    _ => unreachable!(),
                })
                .collect::<Vec<_>>();
            fn_flt(&floats).map(Expr::from)
        } else {
            // Otherwise perform integer operation
            let ints = args.iter()
                .map(|x| match x {
                    &Expr::Int(y) => y,
                    _ => unreachable!(),
                })
                .collect::<Vec<_>>();
            fn_int(&ints).map(Expr::from)
        }
    } else {
        Err(format!("#[{}] expected numeric", name).into())
    }
}

pub fn add(args: &[Expr], _env: &Env) -> Result<Expr> {
    numeric_op("+", args,
        |ints| Ok(ints.iter().sum::<i64>()),
        |floats| Ok(floats.iter().sum::<f64>())
    )
}

pub fn sub(args: &[Expr], _env: &Env) -> Result<Expr> {
    ensure_min_args("-", args, 1)?;

    // Check all arguments are numeric
    if !args.iter().all(Expr::is_num) {
        return Err("#[-] expected numeric".into());
    }

    // If one argument, negate and return
    if args.len() == 1 {
        return match args[0] {
            Expr::Int(x) => Ok(Expr::from(-x)),
            Expr::Flt(x) => Ok(Expr::from(-x)),
            _ => Err("invalid type".into())
        }
    }

    numeric_op("-", args,
        |ints| Ok(ints[1..].iter().fold(ints[0], Sub::sub)),
        |floats| Ok(floats[1..].iter().fold(floats[0], Sub::sub))
    )
}

pub fn mul(args: &[Expr], _env: &Env) -> Result<Expr> {
    numeric_op("*", args,
        |ints| Ok(ints.iter().product::<i64>()),
        |floats| Ok(floats.iter().product::<f64>())
    )
}

pub fn div(args: &[Expr], _env: &Env) -> Result<Expr> {
    ensure_min_args("[/]", args, 1)?;

    // Check all arguments are numeric
    if !args.iter().all(Expr::is_num) {
        return Err("#[-] expected numeric".into());
    }

    // If one argument, invert and return
    if args.len() == 1 {
        return match args[0] {
            Expr::Int(x) => Ok(Expr::from(1.0f64 / x as f64)),
            Expr::Flt(x) => Ok(Expr::from(1.0f64 / x)),
            _ => Err("invalid type".into())
        }
    }

    let int_div = |ints: &[i64]| {
        ints[1..].iter()
            .map(|&x| if x == 0i64 { Err("division by zero".into()) } else { Ok(x) })
            .fold_results(ints[0], Div::div)
    };

    numeric_op("/", args,
        int_div,
        |floats| Ok(floats[1..].iter().fold(floats[0], Div::div))
    )
}

pub fn equal(args: &[Expr], _env: &Env) -> Result<Expr> {
    ensure_args("[=]", args, 2)?;
    Ok(Expr::from(args[0] == args[1]))
}

pub fn less(args: &[Expr], _env: &Env) -> Result<Expr> {
    ensure_args("[<]", args, 2)?;
    match (&args[0], &args[1]) {
        (&Expr::Int(ref a), &Expr::Int(ref b)) => Ok(Expr::from(a < b)),
        (&Expr::Flt(ref a), &Expr::Flt(ref b)) => Ok(Expr::from(a < b)),
        (&Expr::Str(ref a), &Expr::Str(ref b)) => Ok(Expr::from(a < b)),
        _ => Err(
            format!("comparison undefined for: {}, {}", args[0], args[1]).into(),
        ),
    }
}

pub fn less_eq(args: &[Expr], _env: &Env) -> Result<Expr> {
    ensure_args("[<=]", args, 2)?;
    match (&args[0], &args[1]) {
        (&Expr::Int(ref a), &Expr::Int(ref b)) => Ok(Expr::from(a <= b)),
        (&Expr::Flt(ref a), &Expr::Flt(ref b)) => Ok(Expr::from(a <= b)),
        (&Expr::Str(ref a), &Expr::Str(ref b)) => Ok(Expr::from(a <= b)),
        _ => Err(
            format!("comparison undefined for: {}, {}", args[0], args[1]).into(),
        ),
    }
}

pub fn greater(args: &[Expr], _env: &Env) -> Result<Expr> {
    ensure_args("[>]", args, 2)?;
    match (&args[0], &args[1]) {
        (&Expr::Int(ref a), &Expr::Int(ref b)) => Ok(Expr::from(a > b)),
        (&Expr::Flt(ref a), &Expr::Flt(ref b)) => Ok(Expr::from(a > b)),
        (&Expr::Str(ref a), &Expr::Str(ref b)) => Ok(Expr::from(a > b)),
        _ => Err(
            format!("comparison undefined for: {}, {}", args[0], args[1]).into(),
        ),
    }
}

pub fn greater_eq(args: &[Expr], _env: &Env) -> Result<Expr> {
    ensure_args("[>=]", args, 2)?;
    match (&args[0], &args[1]) {
        (&Expr::Int(ref a), &Expr::Int(ref b)) => Ok(Expr::from(a >= b)),
        (&Expr::Flt(ref a), &Expr::Flt(ref b)) => Ok(Expr::from(a >= b)),
        (&Expr::Str(ref a), &Expr::Str(ref b)) => Ok(Expr::from(a >= b)),
        _ => Err(
            format!("comparison undefined for: {}, {}", args[0], args[1]).into(),
        ),
    }
}

pub fn not(args: &[Expr], _env: &Env) -> Result<Expr> {
    ensure_args("[not]", args, 1)?;
    Ok(Expr::from(!args[0].truthiness()))
}

// TODO: convert to special form
pub fn and(args: &[Expr], _env: &Env) -> Result<Expr> {
    args
        .into_iter()
        .map(|a| a.boolean())
        .collect::<Option<Vec<_>>>()
        .ok_or("#[and] expected boolean argument".into())
        .map(|bools| bools.iter().all(|b| *b))
        .map(Expr::from)
}

// TODO: convert to special form
pub fn or(args: &[Expr], _env: &Env) -> Result<Expr> {
    args
        .into_iter()
        .map(|a| a.boolean())
        .collect::<Option<Vec<_>>>()
        .ok_or("#[or] expected boolean argument".into())
        .map(|bools| bools.iter().any(|b| *b))
        .map(Expr::from)
}

pub fn print(args: &[Expr], _env: &Env) -> Result<Expr> {
    ensure_args("#[print]", args, 1)?;
    println!("{}", args[0]);
    Ok(Expr::Nil)
}

pub fn first(args: &[Expr], _env: &Env) -> Result<Expr> {
    ensure_args("#[first]", args, 1)?;

    match &args[0] {
        &Expr::List(ref l) => Ok(l.0.first().cloned().unwrap_or(Expr::Nil)),
        &Expr::Vector(ref q) => Ok(q.0.first().cloned().unwrap_or(Expr::Nil)),
        _ => Err("#[first] expected list".into()),
    }
}

pub fn rest(args: &[Expr], _env: &Env) -> Result<Expr> {
    match &args[0] {
        &Expr::List(ref l) => {
            Ok(
                l.0
                    .split_first()
                    .map(|(_, rest)| Expr::List(List(rest.to_vec())))
                    .unwrap_or(Expr::Nil),
            )
        }
        &Expr::Vector(ref v) => {
            Ok(
                v.0
                    .split_first()
                    .map(|(_, rest)| Expr::Vector(Vector(rest.to_vec())))
                    .unwrap_or(Expr::Nil),
            )
        }
        _ => Err("#[rest] expected list".into()),
    }
}

// (cons item seq)
pub fn cons(args: &[Expr], _env: &Env) -> Result<Expr> {
    ensure_args("cons]", args, 2)?;

    match &args[2] {
        &Expr::List(ref l) => {
            let mut new = l.clone();
            new.0.insert(0, args[1].clone());
            Ok(Expr::List(new))
        }
        &Expr::Vector(ref v) => {
            let mut new = v.clone();
            new.0.push(args[1].clone());
            Ok(Expr::Vector(new))
        }
        _ => Err("#[cons] expected list".into()),
    }
}

// (exit)
pub fn exit(_args: &[Expr], _env: &Env) -> Result<Expr> {
    Err(ErrorKind::Exit(0).into())
}
<|endoftext|>


<commit_before>use std::old_io::{File, Open, Read, Write, ReadWrite};
use std::vec;

use protobuf;
use rustc_serialize::base64::{self, ToBase64};
use sodiumoxide::crypto::sign::ed25519;

use error::{SimplesResult};
use simples_pb::{Wallet, WalletKey};

fn load_proto_from_file<Message: protobuf::MessageStatic>(
    path: &str) -> SimplesResult<Message>
{
    let mut proto_in = File::open(&Path::new(path));
    let wallet_bytes = try!(proto_in.read_to_end());
    Ok(try!(protobuf::parse_from_bytes(&wallet_bytes[])))
}

fn save_proto_to_file<Message: protobuf::MessageStatic>(
    path: &str, msg: &Message) -> SimplesResult<()>
{
    let mut proto_out = File::create(&Path::new(path));
    let wallet_bytes = try!(msg.write_to_bytes());
    Ok(try!(proto_out.write(&wallet_bytes[])))
}

pub fn load_from_file(path: &str) -> SimplesResult<Wallet> {
    load_proto_from_file(path)
}

pub fn save_to_file(path: &str, wallet: &Wallet) -> SimplesResult<()> {
    save_proto_to_file(path, wallet)
}

pub fn pretty_format(wallet_key: &WalletKey) -> String {
    let mut formatted = String::new();
    let pk = wallet_key.get_public_key();
    let sk = wallet_key.get_secret_key();
    formatted.push_str(&format!(
        "[ {} ]\n", pk.to_base64(base64::STANDARD))[]);
    formatted.push_str(&format!(
        " name: {}\n", wallet_key.get_name())[]);
    formatted.push_str(&format!(
        " desc: {}\n", wallet_key.get_description())[]);
    formatted.push_str(&format!(
        "   pk: {}\n", pk.to_base64(base64::STANDARD))[]);
    formatted.push_str(&format!(
        "   sk: {}\n", sk.to_base64(base64::STANDARD))[]);
    formatted
}

pub trait WalletExt {
    fn generate_new_key(
        &mut self, name: &str, description: &str) -> WalletKey;
    fn search_keys(&self, search_str: &str) -> Vec<&WalletKey>;
}

impl WalletExt for Wallet {
    fn generate_new_key(
        &mut self, name: &str, description: &str) -> WalletKey
    {
        let (pk, sk) = ed25519::gen_keypair();
        let mut key = WalletKey::new();
        key.set_public_key(pk.0.to_vec());
        key.set_secret_key(sk.0.to_vec());
        key.set_name(String::from_str(name));
        key.set_description(String::from_str(description));
        let copy = key.clone();
        self.mut_keys().push(key);
        copy
    }

    fn search_keys(&self, search_str: &str) -> Vec<&WalletKey> {
        self.get_keys().iter()
            .filter(|wkey| {
                let name = &wkey.get_name()[];
                let pk_base64 = wkey.get_public_key()
                    .to_base64(base64::STANDARD);
                name.starts_with(search_str) ||
                    pk_base64.starts_with(search_str)
            }).collect()
    }
}
<commit_msg>Updated io to nightly.<commit_after>use std::old_io::{File};

use protobuf;
use rustc_serialize::base64::{self, ToBase64};
use sodiumoxide::crypto::sign::ed25519;

use error::{SimplesResult};
use simples_pb::{Wallet, WalletKey};

fn load_proto_from_file<Message: protobuf::MessageStatic>(
    path: &str) -> SimplesResult<Message>
{
    let mut proto_in = File::open(&Path::new(path));
    let wallet_bytes = try!(proto_in.read_to_end());
    Ok(try!(protobuf::parse_from_bytes(&wallet_bytes[])))
}

fn save_proto_to_file<Message: protobuf::MessageStatic>(
    path: &str, msg: &Message) -> SimplesResult<()>
{
    let mut proto_out = File::create(&Path::new(path));
    let wallet_bytes = try!(msg.write_to_bytes());
    Ok(try!(proto_out.write_all(&wallet_bytes[])))
}

pub fn load_from_file(path: &str) -> SimplesResult<Wallet> {
    load_proto_from_file(path)
}

pub fn save_to_file(path: &str, wallet: &Wallet) -> SimplesResult<()> {
    save_proto_to_file(path, wallet)
}

pub fn pretty_format(wallet_key: &WalletKey) -> String {
    let mut formatted = String::new();
    let pk = wallet_key.get_public_key();
    let sk = wallet_key.get_secret_key();
    formatted.push_str(&format!(
        "[ {} ]\n", pk.to_base64(base64::STANDARD))[]);
    formatted.push_str(&format!(
        " name: {}\n", wallet_key.get_name())[]);
    formatted.push_str(&format!(
        " desc: {}\n", wallet_key.get_description())[]);
    formatted.push_str(&format!(
        "   pk: {}\n", pk.to_base64(base64::STANDARD))[]);
    formatted.push_str(&format!(
        "   sk: {}\n", sk.to_base64(base64::STANDARD))[]);
    formatted
}

pub trait WalletExt {
    fn generate_new_key(
        &mut self, name: &str, description: &str) -> WalletKey;
    fn search_keys(&self, search_str: &str) -> Vec<&WalletKey>;
}

impl WalletExt for Wallet {
    fn generate_new_key(
        &mut self, name: &str, description: &str) -> WalletKey
    {
        let (pk, sk) = ed25519::gen_keypair();
        let mut key = WalletKey::new();
        key.set_public_key(pk.0.to_vec());
        key.set_secret_key(sk.0.to_vec());
        key.set_name(String::from_str(name));
        key.set_description(String::from_str(description));
        let copy = key.clone();
        self.mut_keys().push(key);
        copy
    }

    fn search_keys(&self, search_str: &str) -> Vec<&WalletKey> {
        self.get_keys().iter()
            .filter(|wkey| {
                let name = &wkey.get_name()[];
                let pk_base64 = wkey.get_public_key()
                    .to_base64(base64::STANDARD);
                name.starts_with(search_str) ||
                    pk_base64.starts_with(search_str)
            }).collect()
    }
}
<|endoftext|>


<commit_before>use ncurses as nc;
use std::cmp;
use unicode_width::UnicodeWidthChar;

use crate::screen_data::ScreenData;

pub struct Window {
    rect: Rect,
    win: nc::WINDOW,
    win_impl: Box<dyn WindowImpl + Send>
}

impl Window {
    pub fn new<WI>(win_impl: WI, r: Rect) -> Window
        where WI: WindowImpl + Send + 'static
    {
        let win = nc::newwin(r.height, r.width, r.y, r.x);
        nc::leaveok(win, true);
        Window {
            rect: r,
            win,
            win_impl: Box::new(win_impl)
        }
    }

    pub fn draw(&self, sd: &ScreenData) {
        self.win_impl.draw(self.win, self.rect, sd);
    }

    pub fn clear(&self) {
        nc::wclear(self.win);
    }

    pub fn noutrefresh(&self) {
        nc::wnoutrefresh(self.win);
    }

    pub fn resize(&mut self, r: Rect) {
        self.rect = r;
        nc::mvwin(self.win, r.y, r.x);
        unsafe {
            wresize(self.win, r.height, r.width);
        }
    }

    pub fn rect(&self) -> Rect {
        self.rect
    }
}

pub trait WindowImpl {
    fn draw(&self, win: nc::WINDOW, r: Rect, sd: &ScreenData);
}

pub struct Gutter;

impl WindowImpl for Gutter {
    fn draw(&self, win: nc::WINDOW, _r: Rect, sd: &ScreenData) {
        if let Some(row) = sd.highlighted_row {
            let y = row as i32;
            nc::mvwaddstr(win, y, 0, ">");
        }
        for row in sd.marked_rows.iter() {
            let y = *row as i32;
            nc::mvwaddstr(win, y, 1, ">");
        }
    }
}

pub struct MiniBuf;

impl WindowImpl for MiniBuf {
    fn draw(&self, win: nc::WINDOW, _r: Rect, sd: &ScreenData) {
        nc::mvwaddstr(win, 0, 0, &*sd.query_string);
    }
}

impl MiniBuf {
    pub fn set_cursor(win: &Window, cursor_index: i32) {
        let mut beg_y = 0i32;
        let mut beg_x = 0i32;
        nc::getbegyx(win.win, &mut beg_y, &mut beg_x);
        let mut cur_y = 0i32;
        let mut cur_x = 0i32;
        nc::getyx(win.win, &mut cur_y, &mut cur_x);
        let x_offset = 0; // use someday
        let mut scr_cur_y = beg_y + cur_y;
        let mut scr_cur_x = beg_x + x_offset + cursor_index;
        nc::setsyx(&mut scr_cur_y, &mut scr_cur_x);
    }
}

pub struct ListView;

impl WindowImpl for ListView {
    fn draw(&self, win: nc::WINDOW, r: Rect, sd: &ScreenData) {
        let num_lines = cmp::min(sd.items.len(), r.height as usize);
        for (y, item) in sd.items.iter().take(num_lines).enumerate() {
            let s = item.as_str();
            let sliced = slice_by_width(s, r.width as usize, sd.is_cjk);
            nc::mvwaddstr(win, y as i32, 0, sliced);
        }
    }
}

pub struct StatusLine;

impl WindowImpl for StatusLine {
    fn draw(&self, win: nc::WINDOW, _r: Rect, sd: &ScreenData) {
        let msg = sd.status_message.as_ref().map(|s| s.as_ref()).unwrap_or("");
        let s = format!("{}/{} {}", sd.item_list_len, sd.total_lines, msg);
        nc::mvwaddstr(win, 0, 0, &s);
    }
}

#[derive(Clone, Copy)]
pub struct Rect {
    pub height: i32,
    pub width: i32,
    pub y: i32,
    pub x: i32,
}

fn slice_by_width(s: &str, slice_width: usize, is_cjk: bool) -> &str {
    let mut bytes = 0;
    let mut width = 0;
    for ch in s.chars() {
        let w = if is_cjk {
            UnicodeWidthChar::width_cjk(ch).unwrap_or(0)
        } else {
            UnicodeWidthChar::width(ch).unwrap_or(0)
        };
        if width + w > slice_width {
            break;
        }
        width += w;
        bytes += ch.len_utf8();
    }
    &s[..bytes]
}

// ncurses-rs 5.73.0 does't provide this function yet.
extern {
    fn wresize(win: nc::WINDOW, lines: ::libc::c_int, columns: ::libc::c_int);
}
<commit_msg>Use ncurses::wresize<commit_after>use ncurses as nc;
use std::cmp;
use unicode_width::UnicodeWidthChar;

use crate::screen_data::ScreenData;

pub struct Window {
    rect: Rect,
    win: nc::WINDOW,
    win_impl: Box<dyn WindowImpl + Send>
}

impl Window {
    pub fn new<WI>(win_impl: WI, r: Rect) -> Window
        where WI: WindowImpl + Send + 'static
    {
        let win = nc::newwin(r.height, r.width, r.y, r.x);
        nc::leaveok(win, true);
        Window {
            rect: r,
            win,
            win_impl: Box::new(win_impl)
        }
    }

    pub fn draw(&self, sd: &ScreenData) {
        self.win_impl.draw(self.win, self.rect, sd);
    }

    pub fn clear(&self) {
        nc::wclear(self.win);
    }

    pub fn noutrefresh(&self) {
        nc::wnoutrefresh(self.win);
    }

    pub fn resize(&mut self, r: Rect) {
        self.rect = r;
        nc::mvwin(self.win, r.y, r.x);
        nc::wresize(self.win, r.height, r.width);
    }

    pub fn rect(&self) -> Rect {
        self.rect
    }
}

pub trait WindowImpl {
    fn draw(&self, win: nc::WINDOW, r: Rect, sd: &ScreenData);
}

pub struct Gutter;

impl WindowImpl for Gutter {
    fn draw(&self, win: nc::WINDOW, _r: Rect, sd: &ScreenData) {
        if let Some(row) = sd.highlighted_row {
            let y = row as i32;
            nc::mvwaddstr(win, y, 0, ">");
        }
        for row in sd.marked_rows.iter() {
            let y = *row as i32;
            nc::mvwaddstr(win, y, 1, ">");
        }
    }
}

pub struct MiniBuf;

impl WindowImpl for MiniBuf {
    fn draw(&self, win: nc::WINDOW, _r: Rect, sd: &ScreenData) {
        nc::mvwaddstr(win, 0, 0, &*sd.query_string);
    }
}

impl MiniBuf {
    pub fn set_cursor(win: &Window, cursor_index: i32) {
        let mut beg_y = 0i32;
        let mut beg_x = 0i32;
        nc::getbegyx(win.win, &mut beg_y, &mut beg_x);
        let mut cur_y = 0i32;
        let mut cur_x = 0i32;
        nc::getyx(win.win, &mut cur_y, &mut cur_x);
        let x_offset = 0; // use someday
        let mut scr_cur_y = beg_y + cur_y;
        let mut scr_cur_x = beg_x + x_offset + cursor_index;
        nc::setsyx(&mut scr_cur_y, &mut scr_cur_x);
    }
}

pub struct ListView;

impl WindowImpl for ListView {
    fn draw(&self, win: nc::WINDOW, r: Rect, sd: &ScreenData) {
        let num_lines = cmp::min(sd.items.len(), r.height as usize);
        for (y, item) in sd.items.iter().take(num_lines).enumerate() {
            let s = item.as_str();
            let sliced = slice_by_width(s, r.width as usize, sd.is_cjk);
            nc::mvwaddstr(win, y as i32, 0, sliced);
        }
    }
}

pub struct StatusLine;

impl WindowImpl for StatusLine {
    fn draw(&self, win: nc::WINDOW, _r: Rect, sd: &ScreenData) {
        let msg = sd.status_message.as_ref().map(|s| s.as_ref()).unwrap_or("");
        let s = format!("{}/{} {}", sd.item_list_len, sd.total_lines, msg);
        nc::mvwaddstr(win, 0, 0, &s);
    }
}

#[derive(Clone, Copy)]
pub struct Rect {
    pub height: i32,
    pub width: i32,
    pub y: i32,
    pub x: i32,
}

fn slice_by_width(s: &str, slice_width: usize, is_cjk: bool) -> &str {
    let mut bytes = 0;
    let mut width = 0;
    for ch in s.chars() {
        let w = if is_cjk {
            UnicodeWidthChar::width_cjk(ch).unwrap_or(0)
        } else {
            UnicodeWidthChar::width(ch).unwrap_or(0)
        };
        if width + w > slice_width {
            break;
        }
        width += w;
        bytes += ch.len_utf8();
    }
    &s[..bytes]
}
<|endoftext|>


<commit_before>// Copyright 2015 Deyan Ginev. See the LICENSE
// file at the top-level directory of this distribution.
//
// Licensed under the MIT license <LICENSE-MIT or http://opensource.org/licenses/MIT>.
// This file may not be copied, modified, or distributed
// except according to those terms.
extern crate zmq;
extern crate rand;
extern crate tempfile;

use zmq::{Error, Message, Context, SNDMORE};
use std::ops::Deref;
use std::thread;
use std::env;
use std::fs::File;
use std::io::{Read,Write, Seek, SeekFrom};
use std::path::Path;
// use std::str;
use std::process::Command;
use rand::{random};

pub trait Worker {
  // fn work(&self, &Message) -> Option<Message>;
  fn convert(&self, &Path) -> Option<File>;
  fn message_size(&self) -> usize;
  fn service(&self) -> String;
  fn source(&self) -> String;
  fn sink(&self) -> String;

  fn start(&self, limit : Option<i32>) -> Result<(), Error> {
    let mut work_counter = 0;
    // Connect to a task ventilator
    let mut context_source = Context::new();
    let mut source = context_source.socket(zmq::DEALER).unwrap();
    let identity : String = (0..10).map(|_| rand::random::<u8>() as char).collect();
    source.set_identity(identity.as_bytes()).unwrap();

    assert!(source.connect(&self.source()).is_ok());
    // Connect to a task sink
    let mut context_sink = Context::new();
    let mut sink = context_sink.socket(zmq::PUSH).unwrap();
    assert!(sink.connect(&self.sink()).is_ok());
    // Work in perpetuity
    loop {
      let mut taskid_msg = Message::new().unwrap();
      let mut recv_msg = Message::new().unwrap();

      source.send_str(&self.service(), 0).unwrap();
      source.recv(&mut taskid_msg, 0).unwrap();
      let taskid = taskid_msg.as_str().unwrap();

      // Prepare a File for the input
      let input_filepath = env::temp_dir().to_str().unwrap().to_string() + "/" + taskid ;
      let mut file = File::create(input_filepath.clone()).unwrap();
      loop {
        source.recv(&mut recv_msg, 0).unwrap();

        file.write(recv_msg.deref()).unwrap();
        if !source.get_rcvmore().unwrap() {
          break;
        }
      }

      file.seek(SeekFrom::Start(0)).unwrap();
      let file_opt = self.convert(Path::new(&input_filepath));
      if file_opt.is_some() {
        let mut converted_file = file_opt.unwrap();
        sink.send_str(&self.service(), SNDMORE).unwrap();
        sink.send_str(taskid, SNDMORE).unwrap();
        loop {
          // Stream converted data via zmq
          let message_size = self.message_size();
          let mut data = vec![0; message_size];
          let size = converted_file.read(&mut data).unwrap();
          data.truncate(size);
          if size < message_size {
            // If exhausted, send the last frame
            sink.send(&data,0).unwrap();
            // And terminate
            break;
          } else {
            // If more to go, send the frame and indicate there's more to come
            sink.send(&data,SNDMORE).unwrap();
          }
        }
      }
      else {
        // If there was nothing to do, retry a minute later
        thread::sleep_ms(60000);
        continue
      }

      work_counter += 1;
      match limit {
        Some(upper_bound) => {
          if work_counter >= upper_bound {
            // Give enough time to complete the Final job.
            thread::sleep_ms(500);
            break;
          }
        },
        None => {}
      };
    }
    Ok(())
  }
}
pub struct EchoWorker {
  pub service : String,
  pub version : f32,
  pub message_size : usize,
  pub source : String,
  pub sink : String
}
impl Default for EchoWorker {
  fn default() -> EchoWorker {
    EchoWorker {
      service: "echo_service".to_string(),
      version: 0.1,
      message_size: 100000,
      source: "tcp://localhost:5555".to_string(),
      sink: "tcp://localhost:5556".to_string()
    }
  }
}
impl Worker for EchoWorker {
  fn service(&self) -> String {self.service.clone()}
  fn source(&self) -> String {self.source.clone()}
  fn sink(&self) -> String {self.sink.clone()}
  fn message_size(&self) -> usize {self.message_size.clone()}

  fn convert(&self, path : &Path) -> Option<File> {
    Some(File::open(path).unwrap())
  }
}

pub struct TexToHtmlWorker {
  pub service : String,
  pub version : f32,
  pub message_size : usize,
  pub source : String,
  pub sink : String
}
impl Default for TexToHtmlWorker {
  fn default() -> TexToHtmlWorker {
    TexToHtmlWorker {
      service: "tex_to_html".to_string(),
      version: 0.1,
      message_size: 100000,
      source: "tcp://localhost:5555".to_string(),
      sink: "tcp://localhost:5556".to_string()
    }
  }
}
impl Worker for TexToHtmlWorker {
  fn service(&self) -> String {self.service.clone()}
  fn source(&self) -> String {self.source.clone()}
  fn sink(&self) -> String {self.sink.clone()}
  fn message_size(&self) -> usize {self.message_size.clone()}

  fn convert(&self, path : &Path) -> Option<File> {
    let name = path.file_stem().unwrap().to_str().unwrap();
    let destination_path = env::temp_dir().to_str().unwrap().to_string() + "/" +name+ ".zip";

    Command::new("latexmlc")
      .arg("--whatsin")
      .arg("archive")
      .arg("--whatsout")
      .arg("archive")
      .arg("--format")
      .arg("html5")
      .arg("--pmml")
      .arg("--cmml")
      .arg("--preload")
      .arg("[ids]latexml.sty")
      .arg("--css")
      .arg("http://latexml.mathweb.org/css/external/LaTeXML.css")
      .arg("--nodefaultresources")
      .arg("--inputencoding")
      .arg("iso-8859-1")
      .arg("--timeout")
      .arg("300")
      .arg("--log")
      .arg("cortex.log")
      .arg("--destination")
      .arg(destination_path.clone())
      .arg(path.clone())
      .output()
      .unwrap_or_else(|e| { panic!("failed to execute process: {}", e) });

    // println!("Dest: {:?}", destination_path);
    // println!("Log: {:?}", str::from_utf8(&output.stderr));
    Some(File::open(destination_path.clone()).unwrap())
  }
}
<commit_msg>job limit is usize<commit_after>// Copyright 2015 Deyan Ginev. See the LICENSE
// file at the top-level directory of this distribution.
//
// Licensed under the MIT license <LICENSE-MIT or http://opensource.org/licenses/MIT>.
// This file may not be copied, modified, or distributed
// except according to those terms.
extern crate zmq;
extern crate rand;
extern crate tempfile;

use zmq::{Error, Message, Context, SNDMORE};
use std::ops::Deref;
use std::thread;
use std::env;
use std::fs::File;
use std::io::{Read,Write, Seek, SeekFrom};
use std::path::Path;
// use std::str;
use std::process::Command;
use rand::{random};

pub trait Worker {
  // fn work(&self, &Message) -> Option<Message>;
  fn convert(&self, &Path) -> Option<File>;
  fn message_size(&self) -> usize;
  fn service(&self) -> String;
  fn source(&self) -> String;
  fn sink(&self) -> String;

  fn start(&self, limit : Option<usize>) -> Result<(), Error> {
    let mut work_counter = 0;
    // Connect to a task ventilator
    let mut context_source = Context::new();
    let mut source = context_source.socket(zmq::DEALER).unwrap();
    let identity : String = (0..10).map(|_| rand::random::<u8>() as char).collect();
    source.set_identity(identity.as_bytes()).unwrap();

    assert!(source.connect(&self.source()).is_ok());
    // Connect to a task sink
    let mut context_sink = Context::new();
    let mut sink = context_sink.socket(zmq::PUSH).unwrap();
    assert!(sink.connect(&self.sink()).is_ok());
    // Work in perpetuity
    loop {
      let mut taskid_msg = Message::new().unwrap();
      let mut recv_msg = Message::new().unwrap();

      source.send_str(&self.service(), 0).unwrap();
      source.recv(&mut taskid_msg, 0).unwrap();
      let taskid = taskid_msg.as_str().unwrap();

      // Prepare a File for the input
      let input_filepath = env::temp_dir().to_str().unwrap().to_string() + "/" + taskid ;
      let mut file = File::create(input_filepath.clone()).unwrap();
      loop {
        source.recv(&mut recv_msg, 0).unwrap();

        file.write(recv_msg.deref()).unwrap();
        if !source.get_rcvmore().unwrap() {
          break;
        }
      }

      file.seek(SeekFrom::Start(0)).unwrap();
      let file_opt = self.convert(Path::new(&input_filepath));
      if file_opt.is_some() {
        let mut converted_file = file_opt.unwrap();
        sink.send_str(&self.service(), SNDMORE).unwrap();
        sink.send_str(taskid, SNDMORE).unwrap();
        loop {
          // Stream converted data via zmq
          let message_size = self.message_size();
          let mut data = vec![0; message_size];
          let size = converted_file.read(&mut data).unwrap();
          data.truncate(size);
          if size < message_size {
            // If exhausted, send the last frame
            sink.send(&data,0).unwrap();
            // And terminate
            break;
          } else {
            // If more to go, send the frame and indicate there's more to come
            sink.send(&data,SNDMORE).unwrap();
          }
        }
      }
      else {
        // If there was nothing to do, retry a minute later
        thread::sleep_ms(60000);
        continue
      }

      work_counter += 1;
      match limit {
        Some(upper_bound) => {
          if work_counter >= upper_bound {
            // Give enough time to complete the Final job.
            thread::sleep_ms(500);
            break;
          }
        },
        None => {}
      };
    }
    Ok(())
  }
}
pub struct EchoWorker {
  pub service : String,
  pub version : f32,
  pub message_size : usize,
  pub source : String,
  pub sink : String
}
impl Default for EchoWorker {
  fn default() -> EchoWorker {
    EchoWorker {
      service: "echo_service".to_string(),
      version: 0.1,
      message_size: 100000,
      source: "tcp://localhost:5555".to_string(),
      sink: "tcp://localhost:5556".to_string()
    }
  }
}
impl Worker for EchoWorker {
  fn service(&self) -> String {self.service.clone()}
  fn source(&self) -> String {self.source.clone()}
  fn sink(&self) -> String {self.sink.clone()}
  fn message_size(&self) -> usize {self.message_size.clone()}

  fn convert(&self, path : &Path) -> Option<File> {
    Some(File::open(path).unwrap())
  }
}

pub struct TexToHtmlWorker {
  pub service : String,
  pub version : f32,
  pub message_size : usize,
  pub source : String,
  pub sink : String
}
impl Default for TexToHtmlWorker {
  fn default() -> TexToHtmlWorker {
    TexToHtmlWorker {
      service: "tex_to_html".to_string(),
      version: 0.1,
      message_size: 100000,
      source: "tcp://localhost:5555".to_string(),
      sink: "tcp://localhost:5556".to_string()
    }
  }
}
impl Worker for TexToHtmlWorker {
  fn service(&self) -> String {self.service.clone()}
  fn source(&self) -> String {self.source.clone()}
  fn sink(&self) -> String {self.sink.clone()}
  fn message_size(&self) -> usize {self.message_size.clone()}

  fn convert(&self, path : &Path) -> Option<File> {
    let name = path.file_stem().unwrap().to_str().unwrap();
    let destination_path = env::temp_dir().to_str().unwrap().to_string() + "/" +name+ ".zip";

    Command::new("latexmlc")
      .arg("--whatsin")
      .arg("archive")
      .arg("--whatsout")
      .arg("archive")
      .arg("--format")
      .arg("html5")
      .arg("--pmml")
      .arg("--cmml")
      .arg("--preload")
      .arg("[ids]latexml.sty")
      .arg("--css")
      .arg("http://latexml.mathweb.org/css/external/LaTeXML.css")
      .arg("--nodefaultresources")
      .arg("--inputencoding")
      .arg("iso-8859-1")
      .arg("--timeout")
      .arg("300")
      .arg("--log")
      .arg("cortex.log")
      .arg("--destination")
      .arg(destination_path.clone())
      .arg(path.clone())
      .output()
      .unwrap_or_else(|e| { panic!("failed to execute process: {}", e) });

    // println!("Dest: {:?}", destination_path);
    // println!("Log: {:?}", str::from_utf8(&output.stderr));
    Some(File::open(destination_path.clone()).unwrap())
  }
}
<|endoftext|>


<commit_before>// Options
// Space before /
// Quote style
// Ordering of attributes

#[cfg(test)]
mod test {
    use std::io::{IoResult,MemWriter};

    use super::super::{Document,Element,Text,Comment,ProcessingInstruction};
    use super::super::{ElementElementChild,TextElementChild,CommentElementChild,PIElementChild};
    use super::super::{ElementRootChild,CommentRootChild,PIRootChild};

    macro_rules! assert_str_eq(
        ($l:expr, $r:expr) => (assert_eq!($l.as_slice(), $r.as_slice()));
    )

    enum Content {
        WElement(Element),
        WElementEnd(String),
        WText(Text),
        WComment(Comment),
        WProcessingInstruction(ProcessingInstruction),
    }

    fn format_element<W : Writer>(element: Element, todo: &mut Vec<Content>, writer: &mut W) -> IoResult<()> {
        try!(write!(writer, "<{}", element.name()));

        for attr in element.attributes().iter() {
            try!(write!(writer, " {}='{}'", attr.name(), attr.value()));
        }

        let mut children = element.children();
        if children.is_empty() {
            writer.write_str("/>")
        } else {
            try!(writer.write_str(">"));

            todo.push(WElementEnd(element.name()));
            children.reverse();
            let x = children.move_iter().map(|c| match c {
                ElementElementChild(element) => WElement(element),
                TextElementChild(t) => WText(t),
                CommentElementChild(c) => WComment(c),
                PIElementChild(p) => WProcessingInstruction(p),
            }).collect();
            todo.push_all_move(x);

            Ok(())
        }
    }

    fn format_comment<W : Writer>(comment: Comment, writer: &mut W) -> IoResult<()> {
        write!(writer, "<!--{}-->", comment.text())
    }

    fn format_processing_instruction<W : Writer>(pi: ProcessingInstruction, writer: &mut W) -> IoResult<()> {
        match pi.value() {
            None    => write!(writer, "<?{}?>", pi.target()),
            Some(v) => write!(writer, "<?{} {}?>", pi.target(), v),
        }
    }

    fn format_one<W : Writer>(content: Content, todo: &mut Vec<Content>, writer: &mut W) -> IoResult<()> {
        match content {
            WElement(e)               => format_element(e, todo, writer),
            WElementEnd(name)         => write!(writer, "</{}>", name),
            WText(t)                  => writer.write_str(t.text().as_slice()),
            WComment(c)               => format_comment(c, writer),
            WProcessingInstruction(p) => format_processing_instruction(p, writer),
        }
    }

    fn format_document<W : Writer>(doc: &Document, writer: &mut W) -> IoResult<()> {
        try!(writer.write_str("<?xml version='1.0'?>"));

        for child in doc.root().children().move_iter() {
            match child {
                ElementRootChild(e) => {
                    let mut todo = vec![WElement(e)];

                    while ! todo.is_empty() {
                        try!(format_one(todo.pop().unwrap(), &mut todo, writer));
                    }
                },
                CommentRootChild(c) => try!(format_comment(c, writer)),
                PIRootChild(_) => fail!("pi?"),
            }
        }

        Ok(())
    }

    fn format_xml(doc: &Document) -> String {
        let mut w = MemWriter::new();
        format_document(doc, &mut w).ok().expect("Not formatted");
        String::from_utf8(w.unwrap()).ok().expect("Not a string")
    }

    #[test]
    fn top_element() {
        let d = Document::new();
        let e = d.new_element("hello".to_string());
        d.root().append_child(e);

        let xml = format_xml(&d);
        assert_str_eq!(xml, "<?xml version='1.0'?><hello/>");
    }

    #[test]
    fn element_with_attributes() {
        let d = Document::new();
        let e = d.new_element("hello".to_string());
        e.set_attribute("a".to_string(), "b".to_string());
        d.root().append_child(e);

        let xml = format_xml(&d);
        assert_str_eq!(xml, "<?xml version='1.0'?><hello a='b'/>");
    }

    #[test]
    fn nested_element() {
        let d = Document::new();
        let hello = d.new_element("hello".to_string());
        let world = d.new_element("world".to_string());
        hello.append_child(world);
        d.root().append_child(hello);

        let xml = format_xml(&d);
        assert_str_eq!(xml, "<?xml version='1.0'?><hello><world/></hello>");
    }

    #[test]
    fn nested_text() {
        let d = Document::new();
        let hello = d.new_element("hello".to_string());
        let text = d.new_text("A fine day to you!".to_string());
        hello.append_child(text);
        d.root().append_child(hello);

        let xml = format_xml(&d);
        assert_str_eq!(xml, "<?xml version='1.0'?><hello>A fine day to you!</hello>");
    }

    #[test]
    fn nested_comment() {
        let d = Document::new();
        let hello = d.new_element("hello".to_string());
        let comment = d.new_comment(" Fill this in ".to_string());
        hello.append_child(comment);
        d.root().append_child(hello);

        let xml = format_xml(&d);
        assert_str_eq!(xml, "<?xml version='1.0'?><hello><!-- Fill this in --></hello>");
    }

    #[test]
    fn nested_processing_instruction_without_value() {
        let d = Document::new();
        let hello = d.new_element("hello".to_string());
        let pi = d.new_processing_instruction("display".to_string(), None);
        hello.append_child(pi);
        d.root().append_child(hello);

        let xml = format_xml(&d);
        assert_str_eq!(xml, "<?xml version='1.0'?><hello><?display?></hello>");
    }

    #[test]
    fn nested_processing_instruction_with_value() {
        let d = Document::new();
        let hello = d.new_element("hello".to_string());
        let pi = d.new_processing_instruction("display".to_string(), Some("screen".to_string()));
        hello.append_child(pi);
        d.root().append_child(hello);

        let xml = format_xml(&d);
        assert_str_eq!(xml, "<?xml version='1.0'?><hello><?display screen?></hello>");
    }

    #[test]
    fn top_level_comment() {
        let d = Document::new();
        let pi = d.new_comment(" Fill this in ".to_string());
        d.root().append_child(pi);

        let xml = format_xml(&d);
        assert_str_eq!(xml, "<?xml version='1.0'?><!-- Fill this in -->");
    }
}
<commit_msg>Write top-level processing instructions<commit_after>// Options
// Space before /
// Quote style
// Ordering of attributes

#[cfg(test)]
mod test {
    use std::io::{IoResult,MemWriter};

    use super::super::{Document,Element,Text,Comment,ProcessingInstruction};
    use super::super::{ElementElementChild,TextElementChild,CommentElementChild,PIElementChild};
    use super::super::{ElementRootChild,CommentRootChild,PIRootChild};

    macro_rules! assert_str_eq(
        ($l:expr, $r:expr) => (assert_eq!($l.as_slice(), $r.as_slice()));
    )

    enum Content {
        WElement(Element),
        WElementEnd(String),
        WText(Text),
        WComment(Comment),
        WProcessingInstruction(ProcessingInstruction),
    }

    fn format_element<W : Writer>(element: Element, todo: &mut Vec<Content>, writer: &mut W) -> IoResult<()> {
        try!(write!(writer, "<{}", element.name()));

        for attr in element.attributes().iter() {
            try!(write!(writer, " {}='{}'", attr.name(), attr.value()));
        }

        let mut children = element.children();
        if children.is_empty() {
            writer.write_str("/>")
        } else {
            try!(writer.write_str(">"));

            todo.push(WElementEnd(element.name()));
            children.reverse();
            let x = children.move_iter().map(|c| match c {
                ElementElementChild(element) => WElement(element),
                TextElementChild(t) => WText(t),
                CommentElementChild(c) => WComment(c),
                PIElementChild(p) => WProcessingInstruction(p),
            }).collect();
            todo.push_all_move(x);

            Ok(())
        }
    }

    fn format_comment<W : Writer>(comment: Comment, writer: &mut W) -> IoResult<()> {
        write!(writer, "<!--{}-->", comment.text())
    }

    fn format_processing_instruction<W : Writer>(pi: ProcessingInstruction, writer: &mut W) -> IoResult<()> {
        match pi.value() {
            None    => write!(writer, "<?{}?>", pi.target()),
            Some(v) => write!(writer, "<?{} {}?>", pi.target(), v),
        }
    }

    fn format_one<W : Writer>(content: Content, todo: &mut Vec<Content>, writer: &mut W) -> IoResult<()> {
        match content {
            WElement(e)               => format_element(e, todo, writer),
            WElementEnd(name)         => write!(writer, "</{}>", name),
            WText(t)                  => writer.write_str(t.text().as_slice()),
            WComment(c)               => format_comment(c, writer),
            WProcessingInstruction(p) => format_processing_instruction(p, writer),
        }
    }

    fn format_document<W : Writer>(doc: &Document, writer: &mut W) -> IoResult<()> {
        try!(writer.write_str("<?xml version='1.0'?>"));

        for child in doc.root().children().move_iter() {
            match child {
                ElementRootChild(e) => {
                    let mut todo = vec![WElement(e)];

                    while ! todo.is_empty() {
                        try!(format_one(todo.pop().unwrap(), &mut todo, writer));
                    }
                },
                CommentRootChild(c) => try!(format_comment(c, writer)),
                PIRootChild(p) => try!(format_processing_instruction(p, writer)),
            }
        }

        Ok(())
    }

    fn format_xml(doc: &Document) -> String {
        let mut w = MemWriter::new();
        format_document(doc, &mut w).ok().expect("Not formatted");
        String::from_utf8(w.unwrap()).ok().expect("Not a string")
    }

    #[test]
    fn top_element() {
        let d = Document::new();
        let e = d.new_element("hello".to_string());
        d.root().append_child(e);

        let xml = format_xml(&d);
        assert_str_eq!(xml, "<?xml version='1.0'?><hello/>");
    }

    #[test]
    fn element_with_attributes() {
        let d = Document::new();
        let e = d.new_element("hello".to_string());
        e.set_attribute("a".to_string(), "b".to_string());
        d.root().append_child(e);

        let xml = format_xml(&d);
        assert_str_eq!(xml, "<?xml version='1.0'?><hello a='b'/>");
    }

    #[test]
    fn nested_element() {
        let d = Document::new();
        let hello = d.new_element("hello".to_string());
        let world = d.new_element("world".to_string());
        hello.append_child(world);
        d.root().append_child(hello);

        let xml = format_xml(&d);
        assert_str_eq!(xml, "<?xml version='1.0'?><hello><world/></hello>");
    }

    #[test]
    fn nested_text() {
        let d = Document::new();
        let hello = d.new_element("hello".to_string());
        let text = d.new_text("A fine day to you!".to_string());
        hello.append_child(text);
        d.root().append_child(hello);

        let xml = format_xml(&d);
        assert_str_eq!(xml, "<?xml version='1.0'?><hello>A fine day to you!</hello>");
    }

    #[test]
    fn nested_comment() {
        let d = Document::new();
        let hello = d.new_element("hello".to_string());
        let comment = d.new_comment(" Fill this in ".to_string());
        hello.append_child(comment);
        d.root().append_child(hello);

        let xml = format_xml(&d);
        assert_str_eq!(xml, "<?xml version='1.0'?><hello><!-- Fill this in --></hello>");
    }

    #[test]
    fn nested_processing_instruction_without_value() {
        let d = Document::new();
        let hello = d.new_element("hello".to_string());
        let pi = d.new_processing_instruction("display".to_string(), None);
        hello.append_child(pi);
        d.root().append_child(hello);

        let xml = format_xml(&d);
        assert_str_eq!(xml, "<?xml version='1.0'?><hello><?display?></hello>");
    }

    #[test]
    fn nested_processing_instruction_with_value() {
        let d = Document::new();
        let hello = d.new_element("hello".to_string());
        let pi = d.new_processing_instruction("display".to_string(), Some("screen".to_string()));
        hello.append_child(pi);
        d.root().append_child(hello);

        let xml = format_xml(&d);
        assert_str_eq!(xml, "<?xml version='1.0'?><hello><?display screen?></hello>");
    }

    #[test]
    fn top_level_comment() {
        let d = Document::new();
        let comment = d.new_comment(" Fill this in ".to_string());
        d.root().append_child(comment);

        let xml = format_xml(&d);
        assert_str_eq!(xml, "<?xml version='1.0'?><!-- Fill this in -->");
    }

    #[test]
    fn top_level_processing_instruction() {
        let d = Document::new();
        let pi = d.new_processing_instruction("display".to_string(), None);
        d.root().append_child(pi);

        let xml = format_xml(&d);
        assert_str_eq!(xml, "<?xml version='1.0'?><?display?>");
    }
}
<|endoftext|>


<commit_before>//! Module: czmq-zframe

use {czmq_sys, Error, ErrorKind, Result};
use std::{error, fmt, ptr, result, slice};
use std::ffi::{CStr, CString};
use std::str::{self, Utf8Error};
use std::os::raw::c_void;
use zmsg::ZMsgable;

bitflags! {
    pub flags Flags: i32 {
        const ZFRAME_MORE     = 0b00000001,
        const ZFRAME_REUSE    = 0b00000010,
        const ZFRAME_DONTWAIT = 0b00000100,
    }
}

pub struct ZFrame {
    zframe: *mut czmq_sys::zframe_t,
}

unsafe impl Send for ZFrame {}
unsafe impl Sync for ZFrame {}

impl Drop for ZFrame {
    fn drop(&mut self) {
        unsafe { czmq_sys::zframe_destroy(&mut self.zframe) };
    }
}

impl ZFrame {
    pub fn new(data: &[u8]) -> Result<ZFrame> {
        let data_c = CString::new(data).unwrap().into_raw();
        let zframe = unsafe { czmq_sys::zframe_new(data_c as *const c_void, data.len() as u64) };
        unsafe { CString::from_raw(data_c) };

        if zframe == ptr::null_mut() {
            return Err(Error::new(ErrorKind::NullPtr, ZFrameError::Instantiate));
        }

        Ok(ZFrame {
            zframe: zframe,
        })
    }

    pub fn empty() -> Result<ZFrame> {
        let zframe = unsafe { czmq_sys::zframe_new_empty() };

        if zframe == ptr::null_mut() {
            return Err(Error::new(ErrorKind::NullPtr, ZFrameError::Instantiate));
        }

        Ok(ZFrame {
            zframe: zframe,
        })
    }

    // The correlating C fn zframe_from wraps zframe_new(), which is
    // already wrapped by ZFrame::new, thus we can call our new()
    // instead.
    pub fn from(frame: &str) -> Result<ZFrame> {
        Self::new(frame.as_bytes())
    }

    pub fn from_raw(zframe: *mut czmq_sys::zframe_t) -> ZFrame {
        ZFrame {
            zframe: zframe,
        }
    }

    pub fn recv<S: ZMsgable>(source: &S) -> Result<ZFrame> {
        let zframe = unsafe { czmq_sys::zframe_recv(source.borrow_raw()) };

        if zframe == ptr::null_mut() {
            return Err(Error::new(ErrorKind::NullPtr, ZFrameError::CmdFailed));
        }

        Ok(ZFrame {
            zframe: zframe,
        })
    }

    // This fn consumes the ZFrame, implying no REUSE flag
    pub fn send<D: ZMsgable>(self, dest: &D, flags: Option<Flags>) -> Result<i32> {
        let mut zframe = self;
        zframe.do_send(dest.borrow_raw(), flags)
    }

    // This fn doesn't consume the ZFrame, which implies REUSE flag
    pub fn send_reuse<D: ZMsgable>(&mut self, dest: &D, flags: Option<Flags>) -> Result<i32> {
        let flags = if let Some(f) = flags {
            f | ZFRAME_REUSE
        } else {
            ZFRAME_REUSE
        };
        self.do_send(dest.borrow_raw(), Some(flags))
    }

    fn do_send(&mut self, dest: *mut c_void, flags: Option<Flags>) -> Result<i32> {
        let flags_c = if let Some(f) = flags { f.bits() } else { 0 };
        let size = unsafe { czmq_sys::zframe_send(&mut self.zframe as *mut *mut czmq_sys::zframe_t, dest, flags_c) };
        if size == -1 {
            Err(Error::new(ErrorKind::NonZero, ZFrameError::CmdFailed))
        } else {
            Ok(size)
        }
    }

    pub fn size(&self) -> usize {
        unsafe { czmq_sys::zframe_size(self.zframe) as usize }
    }

    pub fn data<'a>(&'a self) -> Result<result::Result<&'a str, Utf8Error>> {
        let data = unsafe { czmq_sys::zframe_data(self.zframe) };

        if data == ptr::null_mut() {
            Err(Error::new(ErrorKind::NullPtr, ZFrameError::CmdFailed))
        } else {
            let s = unsafe { slice::from_raw_parts(data, self.size()) };
            Ok(str::from_utf8(s))
        }
    }

    pub fn meta<'a>(&'a self, property: &str) -> Result<Option<result::Result<&'a str, &[u8]>>> {
        let property_c = try!(CString::new(property));
        let meta = unsafe { czmq_sys::zframe_meta(self.zframe, property_c.as_ptr()) };

        if meta == ptr::null_mut() {
            Ok(None)
        } else {
            let c_str = unsafe { CStr::from_ptr(meta) };
            let bytes = c_str.to_bytes();
            match c_str.to_str() {
                Ok(s) => Ok(Some(Ok(s))),
                Err(_) => Ok(Some(Err(bytes))),
            }
        }
    }

    pub fn dup(&self) -> Result<ZFrame> {
        let zframe = unsafe { czmq_sys::zframe_dup(self.zframe) };

        if zframe == ptr::null_mut() {
            Err(Error::new(ErrorKind::NullPtr, ZFrameError::CmdFailed))
        } else {
            Ok(ZFrame {
                zframe: zframe,
            })
        }
    }

    pub fn strhex<'a>(&'a self) -> Result<result::Result<&'a str, Utf8Error>> {
        let hex = unsafe { czmq_sys::zframe_strhex(self.zframe) };

        if hex == ptr::null_mut() {
            Err(Error::new(ErrorKind::NullPtr, ZFrameError::CmdFailed))
        } else {
            Ok(unsafe { CStr::from_ptr(hex) }.to_str())
        }
    }

    pub fn strdup(&self) -> Result<result::Result<String, Utf8Error>> {
        let string = unsafe { czmq_sys::zframe_strdup(self.zframe) };

        if string == ptr::null_mut() {
            Err(Error::new(ErrorKind::NullPtr, ZFrameError::CmdFailed))
        } else {
            let cstr = unsafe { CStr::from_ptr(string) }.to_str();
            match cstr {
                Ok(s) => Ok(Ok(s.to_string())),
                Err(e) => Ok(Err(e))
            }
        }
    }

    pub fn streq(&self, string: &str) -> bool {
        unsafe { czmq_sys::zframe_streq(self.zframe, CString::new(string).unwrap_or(CString::new("").unwrap()).as_ptr()) == 1 }
    }

    pub fn more(&self) -> bool {
        unsafe { czmq_sys::zframe_more(self.zframe) == 1 }
    }

    pub fn set_more(&self, more: bool) {
        unsafe { czmq_sys::zframe_set_more(self.zframe, if more { 1 } else { 0 }) }
    }

    pub fn eq(&self, other: &ZFrame) -> bool {
        unsafe { czmq_sys::zframe_eq(self.zframe, other.zframe) == 1 }
    }

    pub fn reset(&self, data: &[u8]) {
        unsafe { czmq_sys::zframe_reset(self.zframe, data.as_ptr() as *const c_void, data.len() as u64) };
    }

    pub fn print(&self, prefix: Option<&str>) {
        let prefix_ptr = match prefix {
            Some(p) => CString::new(p).unwrap_or(CString::new("").unwrap()).as_ptr(),
            None => ptr::null(),
        };
        unsafe { czmq_sys::zframe_print(self.zframe, prefix_ptr) };
    }

    pub fn borrow_raw(&self) -> *mut czmq_sys::zframe_t {
        self.zframe
    }
}

#[derive(Debug)]
pub enum ZFrameError {
    Instantiate,
    CmdFailed,
}

impl fmt::Display for ZFrameError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            ZFrameError::Instantiate => write!(f, "Could not instantiate new ZFrame struct"),
            ZFrameError::CmdFailed => write!(f, "ZFrame command failed"),
        }
    }
}

impl error::Error for ZFrameError {
    fn description(&self) -> &str {
        match *self {
            ZFrameError::Instantiate => "Could not instantiate new ZFrame struct",
            ZFrameError::CmdFailed => "ZFrame command failed",
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use {zmq, ZSock, zsys_init};

    #[test]
    fn test_sendrecv_zmq() {
        let mut ctx = zmq::Context::new();

        let mut server = ctx.socket(zmq::REP).unwrap();
        server.bind("inproc://zframe_sendrecv_zmq").unwrap();

        let mut client = ctx.socket(zmq::REQ).unwrap();
        client.connect("inproc://zframe_sendrecv_zmq").unwrap();

        let mut zframe1 = ZFrame::from("Hello world!").unwrap();
        zframe1.send_reuse(&client, Some(ZFRAME_MORE)).unwrap();
        zframe1.send(&client, None).unwrap();

        for _ in 1..2 {
            let zframe = ZFrame::recv(&server).unwrap();
            assert_eq!(zframe.data().unwrap().unwrap(), "Hello world!");
        }
    }

    #[test]
    fn test_sendrecv_zsock() {
        zsys_init();

        let server = ZSock::new_rep("inproc://zframe_sendrecv_zsock").unwrap();
        let client = ZSock::new_req("inproc://zframe_sendrecv_zsock").unwrap();

        let mut zframe1 = ZFrame::from("Hello world!").unwrap();
        zframe1.send_reuse(&client, Some(ZFRAME_MORE)).unwrap();
        zframe1.send(&client, None).unwrap();

        for _ in 1..2 {
            let zframe = ZFrame::recv(&server).unwrap();
            assert_eq!(zframe.data().unwrap().unwrap(), "Hello world!");
        }
    }

    #[test]
    fn test_dup() {
        let zframe = ZFrame::from("moo cow").unwrap();
        let zframe_dup = zframe.dup().unwrap();
        assert_eq!(zframe_dup.data().unwrap().unwrap(), "moo cow");
    }

    #[test]
    fn test_strhex() {
        let zframe = ZFrame::from("Oh Tobias, you blowhard!").unwrap();
        assert_eq!(zframe.strhex().unwrap().unwrap(), "4F6820546F626961732C20796F7520626C6F776861726421");
    }

    #[test]
    fn test_strdup() {
        let zframe = ZFrame::from("Because that's how you get ants, Lana!").unwrap();
        assert_eq!(zframe.strdup().unwrap().unwrap(), "Because that's how you get ants, Lana!");
    }

    #[test]
    fn test_streq() {
        let zframe = ZFrame::from("And that's why you always leave a note.").unwrap();
        assert!(zframe.streq("And that's why you always leave a note."));
    }

    #[test]
    fn test_more() {
        let zframe = ZFrame::empty().unwrap();
        zframe.set_more(true);
        assert!(zframe.more());
        zframe.set_more(false);
        assert!(!zframe.more());
    }

    #[test]
    fn test_eq() {
        let zframe1 = ZFrame::from("Steve Holt!").unwrap();
        let zframe2 = ZFrame::from("Steve Holt!").unwrap();
        assert!(zframe1.eq(&zframe2));
    }

    #[test]
    fn test_reset() {
        let zframe = ZFrame::from("Phrasing!").unwrap();
        zframe.reset("Holy shitsnacks!".as_bytes());
        assert_eq!(zframe.data().unwrap().unwrap(), "Holy shitsnacks!");
    }

    // XXX We need to capture output here before we can test.
    // #[test]
    // fn test_print() {
    //     let zframe = ZFrame::from("You're not my supervisor!").unwrap();
    //     zframe.print(Some("prefix_"));
    //     zframe.print(None);
    // }
}
<commit_msg>Return byte array rather than Utf8Error in Err case<commit_after>//! Module: czmq-zframe

use {czmq_sys, Error, ErrorKind, Result};
use std::{error, fmt, ptr, result, slice};
use std::ffi::{CStr, CString};
use std::str::{self, Utf8Error};
use std::os::raw::c_void;
use zmsg::ZMsgable;

bitflags! {
    pub flags Flags: i32 {
        const ZFRAME_MORE     = 0b00000001,
        const ZFRAME_REUSE    = 0b00000010,
        const ZFRAME_DONTWAIT = 0b00000100,
    }
}

pub struct ZFrame {
    zframe: *mut czmq_sys::zframe_t,
}

unsafe impl Send for ZFrame {}
unsafe impl Sync for ZFrame {}

impl Drop for ZFrame {
    fn drop(&mut self) {
        unsafe { czmq_sys::zframe_destroy(&mut self.zframe) };
    }
}

impl ZFrame {
    pub fn new(data: &[u8]) -> Result<ZFrame> {
        let data_c = CString::new(data).unwrap().into_raw();
        let zframe = unsafe { czmq_sys::zframe_new(data_c as *const c_void, data.len() as u64) };
        unsafe { CString::from_raw(data_c) };

        if zframe == ptr::null_mut() {
            return Err(Error::new(ErrorKind::NullPtr, ZFrameError::Instantiate));
        }

        Ok(ZFrame {
            zframe: zframe,
        })
    }

    pub fn empty() -> Result<ZFrame> {
        let zframe = unsafe { czmq_sys::zframe_new_empty() };

        if zframe == ptr::null_mut() {
            return Err(Error::new(ErrorKind::NullPtr, ZFrameError::Instantiate));
        }

        Ok(ZFrame {
            zframe: zframe,
        })
    }

    // The correlating C fn zframe_from wraps zframe_new(), which is
    // already wrapped by ZFrame::new, thus we can call our new()
    // instead.
    pub fn from(frame: &str) -> Result<ZFrame> {
        Self::new(frame.as_bytes())
    }

    pub fn from_raw(zframe: *mut czmq_sys::zframe_t) -> ZFrame {
        ZFrame {
            zframe: zframe,
        }
    }

    pub fn recv<S: ZMsgable>(source: &S) -> Result<ZFrame> {
        let zframe = unsafe { czmq_sys::zframe_recv(source.borrow_raw()) };

        if zframe == ptr::null_mut() {
            return Err(Error::new(ErrorKind::NullPtr, ZFrameError::CmdFailed));
        }

        Ok(ZFrame {
            zframe: zframe,
        })
    }

    // This fn consumes the ZFrame, implying no REUSE flag
    pub fn send<D: ZMsgable>(self, dest: &D, flags: Option<Flags>) -> Result<i32> {
        let mut zframe = self;
        zframe.do_send(dest.borrow_raw(), flags)
    }

    // This fn doesn't consume the ZFrame, which implies REUSE flag
    pub fn send_reuse<D: ZMsgable>(&mut self, dest: &D, flags: Option<Flags>) -> Result<i32> {
        let flags = if let Some(f) = flags {
            f | ZFRAME_REUSE
        } else {
            ZFRAME_REUSE
        };
        self.do_send(dest.borrow_raw(), Some(flags))
    }

    fn do_send(&mut self, dest: *mut c_void, flags: Option<Flags>) -> Result<i32> {
        let flags_c = if let Some(f) = flags { f.bits() } else { 0 };
        let size = unsafe { czmq_sys::zframe_send(&mut self.zframe as *mut *mut czmq_sys::zframe_t, dest, flags_c) };
        if size == -1 {
            Err(Error::new(ErrorKind::NonZero, ZFrameError::CmdFailed))
        } else {
            Ok(size)
        }
    }

    pub fn size(&self) -> usize {
        unsafe { czmq_sys::zframe_size(self.zframe) as usize }
    }

    pub fn data<'a>(&'a self) -> Result<result::Result<&'a str, &[u8]>> {
        let data = unsafe { czmq_sys::zframe_data(self.zframe) };

        if data == ptr::null_mut() {
            Err(Error::new(ErrorKind::NullPtr, ZFrameError::CmdFailed))
        } else {
            let bytes = unsafe { slice::from_raw_parts(data, self.size()) };
            match str::from_utf8(bytes) {
                Ok(s) => Ok(Ok(s)),
                Err(_) => Ok(Err(bytes))
            }
        }
    }

    pub fn meta<'a>(&'a self, property: &str) -> Result<Option<result::Result<&'a str, &[u8]>>> {
        let property_c = try!(CString::new(property));
        let meta = unsafe { czmq_sys::zframe_meta(self.zframe, property_c.as_ptr()) };

        if meta == ptr::null_mut() {
            Ok(None)
        } else {
            let c_str = unsafe { CStr::from_ptr(meta) };
            let bytes = c_str.to_bytes();
            match c_str.to_str() {
                Ok(s) => Ok(Some(Ok(s))),
                Err(_) => Ok(Some(Err(bytes))),
            }
        }
    }

    pub fn dup(&self) -> Result<ZFrame> {
        let zframe = unsafe { czmq_sys::zframe_dup(self.zframe) };

        if zframe == ptr::null_mut() {
            Err(Error::new(ErrorKind::NullPtr, ZFrameError::CmdFailed))
        } else {
            Ok(ZFrame {
                zframe: zframe,
            })
        }
    }

    pub fn strhex<'a>(&'a self) -> Result<result::Result<&'a str, Utf8Error>> {
        let hex = unsafe { czmq_sys::zframe_strhex(self.zframe) };

        if hex == ptr::null_mut() {
            Err(Error::new(ErrorKind::NullPtr, ZFrameError::CmdFailed))
        } else {
            Ok(unsafe { CStr::from_ptr(hex) }.to_str())
        }
    }

    pub fn strdup(&self) -> Result<result::Result<String, Utf8Error>> {
        let string = unsafe { czmq_sys::zframe_strdup(self.zframe) };

        if string == ptr::null_mut() {
            Err(Error::new(ErrorKind::NullPtr, ZFrameError::CmdFailed))
        } else {
            let cstr = unsafe { CStr::from_ptr(string) }.to_str();
            match cstr {
                Ok(s) => Ok(Ok(s.to_string())),
                Err(e) => Ok(Err(e))
            }
        }
    }

    pub fn streq(&self, string: &str) -> bool {
        unsafe { czmq_sys::zframe_streq(self.zframe, CString::new(string).unwrap_or(CString::new("").unwrap()).as_ptr()) == 1 }
    }

    pub fn more(&self) -> bool {
        unsafe { czmq_sys::zframe_more(self.zframe) == 1 }
    }

    pub fn set_more(&self, more: bool) {
        unsafe { czmq_sys::zframe_set_more(self.zframe, if more { 1 } else { 0 }) }
    }

    pub fn eq(&self, other: &ZFrame) -> bool {
        unsafe { czmq_sys::zframe_eq(self.zframe, other.zframe) == 1 }
    }

    pub fn reset(&self, data: &[u8]) {
        unsafe { czmq_sys::zframe_reset(self.zframe, data.as_ptr() as *const c_void, data.len() as u64) };
    }

    pub fn print(&self, prefix: Option<&str>) {
        let prefix_ptr = match prefix {
            Some(p) => CString::new(p).unwrap_or(CString::new("").unwrap()).as_ptr(),
            None => ptr::null(),
        };
        unsafe { czmq_sys::zframe_print(self.zframe, prefix_ptr) };
    }

    pub fn borrow_raw(&self) -> *mut czmq_sys::zframe_t {
        self.zframe
    }
}

#[derive(Debug)]
pub enum ZFrameError {
    Instantiate,
    CmdFailed,
}

impl fmt::Display for ZFrameError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            ZFrameError::Instantiate => write!(f, "Could not instantiate new ZFrame struct"),
            ZFrameError::CmdFailed => write!(f, "ZFrame command failed"),
        }
    }
}

impl error::Error for ZFrameError {
    fn description(&self) -> &str {
        match *self {
            ZFrameError::Instantiate => "Could not instantiate new ZFrame struct",
            ZFrameError::CmdFailed => "ZFrame command failed",
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use {zmq, ZSock, zsys_init};

    #[test]
    fn test_sendrecv_zmq() {
        let mut ctx = zmq::Context::new();

        let mut server = ctx.socket(zmq::REP).unwrap();
        server.bind("inproc://zframe_sendrecv_zmq").unwrap();

        let mut client = ctx.socket(zmq::REQ).unwrap();
        client.connect("inproc://zframe_sendrecv_zmq").unwrap();

        let mut zframe1 = ZFrame::from("Hello world!").unwrap();
        zframe1.send_reuse(&client, Some(ZFRAME_MORE)).unwrap();
        zframe1.send(&client, None).unwrap();

        for _ in 1..2 {
            let zframe = ZFrame::recv(&server).unwrap();
            assert_eq!(zframe.data().unwrap().unwrap(), "Hello world!");
        }
    }

    #[test]
    fn test_sendrecv_zsock() {
        zsys_init();

        let server = ZSock::new_rep("inproc://zframe_sendrecv_zsock").unwrap();
        let client = ZSock::new_req("inproc://zframe_sendrecv_zsock").unwrap();

        let mut zframe1 = ZFrame::from("Hello world!").unwrap();
        zframe1.send_reuse(&client, Some(ZFRAME_MORE)).unwrap();
        zframe1.send(&client, None).unwrap();

        for _ in 1..2 {
            let zframe = ZFrame::recv(&server).unwrap();
            assert_eq!(zframe.data().unwrap().unwrap(), "Hello world!");
        }
    }

    #[test]
    fn test_dup() {
        let zframe = ZFrame::from("moo cow").unwrap();
        let zframe_dup = zframe.dup().unwrap();
        assert_eq!(zframe_dup.data().unwrap().unwrap(), "moo cow");
    }

    #[test]
    fn test_strhex() {
        let zframe = ZFrame::from("Oh Tobias, you blowhard!").unwrap();
        assert_eq!(zframe.strhex().unwrap().unwrap(), "4F6820546F626961732C20796F7520626C6F776861726421");
    }

    #[test]
    fn test_strdup() {
        let zframe = ZFrame::from("Because that's how you get ants, Lana!").unwrap();
        assert_eq!(zframe.strdup().unwrap().unwrap(), "Because that's how you get ants, Lana!");
    }

    #[test]
    fn test_streq() {
        let zframe = ZFrame::from("And that's why you always leave a note.").unwrap();
        assert!(zframe.streq("And that's why you always leave a note."));
    }

    #[test]
    fn test_more() {
        let zframe = ZFrame::empty().unwrap();
        zframe.set_more(true);
        assert!(zframe.more());
        zframe.set_more(false);
        assert!(!zframe.more());
    }

    #[test]
    fn test_eq() {
        let zframe1 = ZFrame::from("Steve Holt!").unwrap();
        let zframe2 = ZFrame::from("Steve Holt!").unwrap();
        assert!(zframe1.eq(&zframe2));
    }

    #[test]
    fn test_reset() {
        let zframe = ZFrame::from("Phrasing!").unwrap();
        zframe.reset("Holy shitsnacks!".as_bytes());
        assert_eq!(zframe.data().unwrap().unwrap(), "Holy shitsnacks!");
    }

    // XXX We need to capture output here before we can test.
    // #[test]
    // fn test_print() {
    //     let zframe = ZFrame::from("You're not my supervisor!").unwrap();
    //     zframe.print(Some("prefix_"));
    //     zframe.print(None);
    // }
}
<|endoftext|>


<commit_before>use std::fs::File;
use std::io;
use std::io::Write;
#[cfg(windows)]
use std::io::{Seek, SeekFrom};
use std::path::Path;
#[cfg(unix)]
use std::os::unix::fs::FileExt;
#[cfg(windows)]
use std::os::windows::fs::FileExt;

use super::{ReadAt, WriteAt};

/// A wrapper for `File` that provides optimized random access through
/// `ReadAt` and `WriteAt`.
///
/// * On Unix the operating system is advised that reads will be in random
///   order (`FADV_RANDOM`).
/// * On Windows the implementation is orders of magnitude faster than `ReadAt`
///   directly on `File`.
///
/// # Examples
///
/// Read the fifth 512-byte sector of a file:
///
/// ```
/// # use std::error::Error;
/// #
/// # fn try_main() -> Result<(), Box<Error>> {
/// use positioned_io::{RandomAccessFile, ReadAt};
///
/// // open a file (note: binding does not need to be mut)
/// let raf = RandomAccessFile::open("tests/pi.txt")?;
///
/// // read up to 512 bytes
/// let mut buf = [0; 512];
/// let bytes_read = raf.read_at(2048, &mut buf)?;
/// #     assert!(buf.starts_with(b"4"));
/// #     Ok(())
/// # }
/// #
/// # fn main() {
/// #     try_main().unwrap();
/// # }
#[derive(Debug)]
pub struct RandomAccessFile {
    file: File,
    #[cfg(not(unix))]
    pos: u64,
}

impl RandomAccessFile {
    /// [Opens](https://doc.rust-lang.org/std/fs/struct.File.html#method.open)
    /// a file for random access.
    pub fn open<P: AsRef<Path>>(path: P) -> io::Result<RandomAccessFile> {
        RandomAccessFile::try_new(File::open(path)?)
    }

    /// Creates a `RandomAccessFile` wrapper around a `File`.
    pub fn try_new(file: File) -> io::Result<RandomAccessFile> {
        RandomAccessFile::try_new_impl(file)
    }

    #[cfg(all(unix, target_os = "linux"))]
    fn try_new_impl(file: File) -> io::Result<RandomAccessFile> {
        unsafe {
            use std::os::unix::io::AsRawFd;
            libc::posix_fadvise(file.as_raw_fd(), 0, file.metadata()?.len() as libc::off_t, libc::POSIX_FADV_RANDOM);
        }

        Ok(RandomAccessFile { file })
    }

    #[cfg(all(unix, not(target_os = "linux")))]
    fn try_new_impl(file: File) -> io::Result<RandomAccessFile> {
        Ok(RandomAccessFile { file })
    }

    #[cfg(not(unix))]
    fn try_new_impl(mut file: File) -> io::Result<RandomAccessFile> {
        let pos = file.seek(SeekFrom::Current(0))?;
        Ok(RandomAccessFile { file, pos })
    }

    /// Tries to unwrap the inner `File`.
    pub fn try_into_inner(self) -> Result<File, (RandomAccessFile, io::Error)> {
        RandomAccessFile::try_into_inner_impl(self)
    }

    #[cfg(unix)]
    fn try_into_inner_impl(self) -> Result<File, (RandomAccessFile, io::Error)> {
        Ok(self.file)
    }

    #[cfg(not(unix))]
    fn try_into_inner_impl(mut self) -> Result<File, (RandomAccessFile, io::Error)> {
        match self.file.seek(SeekFrom::Start(self.pos)) {
            Ok(_) => Ok(self.file),
            Err(err) => Err((self, err)),
        }
    }
}

#[cfg(unix)]
impl ReadAt for RandomAccessFile {
    #[inline]
    fn read_at(&self, pos: u64, buf: &mut [u8]) -> io::Result<usize> {
        FileExt::read_at(&self.file, buf, pos)
    }
}

#[cfg(unix)]
impl WriteAt for &RandomAccessFile {
    fn write_at(&mut self, pos: u64, buf: &[u8]) -> io::Result<usize> {
        FileExt::write_at(&self.file, buf, pos)
    }

    fn flush(&mut self) -> io::Result<()> {
        Write::flush(&mut &self.file)
    }
}

#[cfg(windows)]
impl ReadAt for RandomAccessFile {
    #[inline]
    fn read_at(&self, pos: u64, buf: &mut [u8]) -> io::Result<usize> {
        FileExt::seek_read(&self.file, buf, pos)
    }
}

#[cfg(windows)]
impl WriteAt for &RandomAccessFile {
    fn write_at(&mut self, pos: u64, buf: &[u8]) -> io::Result<usize> {
        FileExt::seek_write(&self.file, buf, pos)
    }

    fn flush(&mut self) -> io::Result<()> {
        Write::flush(&mut &self.file)
    }
}

impl WriteAt for RandomAccessFile {
    fn write_at(&mut self, pos: u64, buf: &[u8]) -> io::Result<usize> {
        WriteAt::write_at(&mut &*self, pos, buf)
    }

    fn flush(&mut self) -> io::Result<()> {
        WriteAt::flush(&mut &*self)
    }
}
<commit_msg>simpliy len in posix_fadvise call (#18)<commit_after>use std::fs::File;
use std::io;
use std::io::Write;
#[cfg(windows)]
use std::io::{Seek, SeekFrom};
use std::path::Path;
#[cfg(unix)]
use std::os::unix::fs::FileExt;
#[cfg(windows)]
use std::os::windows::fs::FileExt;

use super::{ReadAt, WriteAt};

/// A wrapper for `File` that provides optimized random access through
/// `ReadAt` and `WriteAt`.
///
/// * On Unix the operating system is advised that reads will be in random
///   order (`FADV_RANDOM`).
/// * On Windows the implementation is orders of magnitude faster than `ReadAt`
///   directly on `File`.
///
/// # Examples
///
/// Read the fifth 512-byte sector of a file:
///
/// ```
/// # use std::error::Error;
/// #
/// # fn try_main() -> Result<(), Box<Error>> {
/// use positioned_io::{RandomAccessFile, ReadAt};
///
/// // open a file (note: binding does not need to be mut)
/// let raf = RandomAccessFile::open("tests/pi.txt")?;
///
/// // read up to 512 bytes
/// let mut buf = [0; 512];
/// let bytes_read = raf.read_at(2048, &mut buf)?;
/// #     assert!(buf.starts_with(b"4"));
/// #     Ok(())
/// # }
/// #
/// # fn main() {
/// #     try_main().unwrap();
/// # }
#[derive(Debug)]
pub struct RandomAccessFile {
    file: File,
    #[cfg(not(unix))]
    pos: u64,
}

impl RandomAccessFile {
    /// [Opens](https://doc.rust-lang.org/std/fs/struct.File.html#method.open)
    /// a file for random access.
    pub fn open<P: AsRef<Path>>(path: P) -> io::Result<RandomAccessFile> {
        RandomAccessFile::try_new(File::open(path)?)
    }

    /// Creates a `RandomAccessFile` wrapper around a `File`.
    pub fn try_new(file: File) -> io::Result<RandomAccessFile> {
        RandomAccessFile::try_new_impl(file)
    }

    #[cfg(all(unix, target_os = "linux"))]
    fn try_new_impl(file: File) -> io::Result<RandomAccessFile> {
        unsafe {
            use std::os::unix::io::AsRawFd;
            libc::posix_fadvise(file.as_raw_fd(), 0, 0, libc::POSIX_FADV_RANDOM);
        }

        Ok(RandomAccessFile { file })
    }

    #[cfg(all(unix, not(target_os = "linux")))]
    fn try_new_impl(file: File) -> io::Result<RandomAccessFile> {
        Ok(RandomAccessFile { file })
    }

    #[cfg(not(unix))]
    fn try_new_impl(mut file: File) -> io::Result<RandomAccessFile> {
        let pos = file.seek(SeekFrom::Current(0))?;
        Ok(RandomAccessFile { file, pos })
    }

    /// Tries to unwrap the inner `File`.
    pub fn try_into_inner(self) -> Result<File, (RandomAccessFile, io::Error)> {
        RandomAccessFile::try_into_inner_impl(self)
    }

    #[cfg(unix)]
    fn try_into_inner_impl(self) -> Result<File, (RandomAccessFile, io::Error)> {
        Ok(self.file)
    }

    #[cfg(not(unix))]
    fn try_into_inner_impl(mut self) -> Result<File, (RandomAccessFile, io::Error)> {
        match self.file.seek(SeekFrom::Start(self.pos)) {
            Ok(_) => Ok(self.file),
            Err(err) => Err((self, err)),
        }
    }
}

#[cfg(unix)]
impl ReadAt for RandomAccessFile {
    #[inline]
    fn read_at(&self, pos: u64, buf: &mut [u8]) -> io::Result<usize> {
        FileExt::read_at(&self.file, buf, pos)
    }
}

#[cfg(unix)]
impl WriteAt for &RandomAccessFile {
    fn write_at(&mut self, pos: u64, buf: &[u8]) -> io::Result<usize> {
        FileExt::write_at(&self.file, buf, pos)
    }

    fn flush(&mut self) -> io::Result<()> {
        Write::flush(&mut &self.file)
    }
}

#[cfg(windows)]
impl ReadAt for RandomAccessFile {
    #[inline]
    fn read_at(&self, pos: u64, buf: &mut [u8]) -> io::Result<usize> {
        FileExt::seek_read(&self.file, buf, pos)
    }
}

#[cfg(windows)]
impl WriteAt for &RandomAccessFile {
    fn write_at(&mut self, pos: u64, buf: &[u8]) -> io::Result<usize> {
        FileExt::seek_write(&self.file, buf, pos)
    }

    fn flush(&mut self) -> io::Result<()> {
        Write::flush(&mut &self.file)
    }
}

impl WriteAt for RandomAccessFile {
    fn write_at(&mut self, pos: u64, buf: &[u8]) -> io::Result<usize> {
        WriteAt::write_at(&mut &*self, pos, buf)
    }

    fn flush(&mut self) -> io::Result<()> {
        WriteAt::flush(&mut &*self)
    }
}
<|endoftext|>