<commit_before>use memory::Memory;

/// Z80 status flags
pub const CF : i32 = 1<<0;      // carry flag
pub const NF : i32 = 1<<1;      // add/subtract flag
pub const VF : i32 = 1<<2;      // parity/overflow flag
//const PF : i32 = 1<<2;      // parity/overflow flag
pub const XF : i32 = 1<<3;      // undocumented flag bit 3
pub const HF : i32 = 1<<4;      // half-carry flag
pub const YF : i32 = 1<<5;      // undocumented flag bit 5
pub const ZF : i32 = 1<<6;      // zero flag
pub const SF : i32 = 1<<7;      // sign flag

/// 8-bit register indices
pub const B : usize = 0;
pub const C : usize = 1;
pub const D : usize = 2;
pub const E : usize = 3;
pub const H : usize = 4;
pub const L : usize = 5;
pub const F : usize = 6;
pub const A : usize = 7;
pub const IXH : usize = 8;
pub const IXL : usize = 9;
pub const IYH : usize = 10;
pub const IYL : usize = 11;
pub const SPH : usize = 12;
pub const SPL : usize = 13;
pub const NUM_REGS : usize = 14;

/// 16-bit register indices
pub const BC : usize = 0;
pub const DE : usize = 2;
pub const HL : usize = 4;
pub const AF : usize = 6;
pub const IX : usize = 8;
pub const IY : usize = 10;
pub const SP : usize = 12;

/// the Z80 CPU state
pub struct CPU {
    pub reg : [u8; NUM_REGS],

    pub wz: u16,
    pub pc: u16,

    pub af_: u16,
    pub bc_: u16,
    pub de_: u16,
    pub hl_: u16,
    pub wz_: u16,

    pub i: u8,
    pub r: u8,
    pub im: u8,

    pub halt: bool,
    pub iff1: bool,
    pub iff2: bool,

    pub mem : Memory,
}

impl CPU {
    /// initialize a new Z80 CPU object
    pub fn new() -> CPU {
        CPU {
            reg: [0; NUM_REGS],
            af_: 0, bc_: 0, de_: 0, hl_: 0, wz_: 0,
            wz: 0, pc: 0,
            i: 0, r: 0, im: 0,
            halt: false, iff1: false, iff2: false,
            mem: Memory::new()
        }
    }

    /// reset the cpu
    pub fn reset(&mut self) {
        self.pc = 0;
        self.wz = 0;
        self.im = 0;
        self.halt = false;
        self.iff1 = false;
        self.iff2 = false;
        self.i = 0;
        self.r = 0;
    }

    /// write 16-bit register value
    pub fn w16(&mut self, r: usize, val: u16) {
        self.reg[r]   = (val>>8) as u8;
        self.reg[r+1] = (val&0xFF) as u8;
    }

    /// read 16-bit register value
    pub fn r16(&self, r: usize) -> u16 {
        let h : u16 = (self.reg[r] as u16) <<8;
        let l : u16 = self.reg[r+1] as u16;
        h | l
    }

    /// go into HALT state
    pub fn halt(&mut self) {
        self.halt = true;
        self.pc -= 1;
    }

    /// push a value on the stack
    pub fn push(&mut self, val: u16) {
        let sp = self.r16(SP).wrapping_sub(2);
        self.w16(SP, sp);
        self.mem.w16(sp, val);
    }

    /// implement RST instruction
    pub fn rst(&mut self, val: u8) {
        // workaround for https://github.com/rust-lang/rust/issues/29975
        let pc = self.pc;
        self.push(pc);
        self.pc = val as u16;
        self.wz = self.pc;
    }

    /// compute flags for add/adc
    pub fn flags_add(acc: i32, add: i32, res: i32) -> u8 {
        let f : i32 =
            (if (res & 0xFF)==0 { ZF } else { res & SF }) |
            (res & (YF|XF)) |
            ((res>>8) & CF) |
            ((acc^add^res) & HF) |
            ((((acc^add^0x80) & (add^res))>>5) & VF);
        f as u8
    }

    /// compute flags for sub/sbc
    pub fn flags_sub(acc: i32, sub: i32, res: i32) -> u8 {
        let f : i32 = NF |
            (if (res & 0xFF)==0 { ZF } else { res & SF }) |
            (res & (YF|XF)) |
            ((res>>8) & CF) |
            ((acc^sub^res) & HF) |
            ((((acc^sub)&(res^acc))>>5)&VF);
        f as u8
    }

    /// 8-bit addition
    pub fn add8(&mut self, v: u8) {
        let acc = self.reg[A] as i32;
        let add = v as i32;
        let res = acc + add;
        self.reg[F] = CPU::flags_add(acc, add, res);
        self.reg[A] = (res&0xFF) as u8;
    }

    /// 8-bit addition with carry
    pub fn adc8(&mut self, v: u8) {
        let acc = self.reg[A] as i32;
        let add = v as i32;
        let res = acc + add + ((self.reg[F] as i32) & CF);
        self.reg[F] = CPU::flags_add(acc, add, res);
        self.reg[A] = (res&0xFF) as u8;
    }

    /// 8-bit subtraction
    pub fn sub8(&mut self, v: u8) {
        let acc = self.reg[A] as i32;
        let sub = v as i32;
        let res = acc - sub;
        self.reg[F] = CPU::flags_sub(acc, sub, res);
        self.reg[A] = (res&0xFF) as u8;
    }

    /// 8-bit subtraction with carry
    pub fn sbc8(&mut self, v: u8) {
        let acc = self.reg[A] as i32;
        let sub = v as i32;
        let res = acc - sub - ((self.reg[F] as i32) & CF);
        self.reg[F] = CPU::flags_sub(acc, sub, res);
        self.reg[A] = (res&0xFF) as u8;
    }
}

#[cfg(test)]
mod tests {

    use super::*;

    #[test]
    fn new() {
        let cpu = CPU::new();
        assert!((0 == cpu.reg[A]) && (0 == cpu.reg[F]));
        assert!((0 == cpu.reg[B]) && (0 == cpu.reg[C]));
        assert!((0 == cpu.reg[D]) && (0 == cpu.reg[E]));
        assert!((0 == cpu.reg[H]) && (0 == cpu.reg[L]));
        assert!((0 == cpu.reg[IXH]) && (0 == cpu.reg[IXL]));
        assert!((0 == cpu.reg[IYH]) && (0 == cpu.reg[IYL]));
        assert!((0 == cpu.reg[SPH]) && (0 == cpu.reg[SPL]));
        assert!(0 == cpu.wz);
        assert!(0 == cpu.pc);
        assert!((0 == cpu.af_) && (0 == cpu.bc_));
        assert!((0 == cpu.de_) && (0 == cpu.hl_));
        assert!(0 == cpu.wz_);
        assert!((0 == cpu.i) && (0 == cpu.r));
        assert!(0 == cpu.im);
        assert!(!cpu.halt);
        assert!(!cpu.iff1);
        assert!(!cpu.iff2);
    }

    #[test]
    fn reset() {
        let mut cpu = CPU::new();
        cpu.pc = 0x1234;
        cpu.wz = 1234;
        cpu.im = 45;
        cpu.halt = true;
        cpu.iff1 = true;
        cpu.iff2 = true;
        cpu.i = 2;
        cpu.r = 3;
        cpu.reset();
        assert!(0 == cpu.pc);
        assert!(0 == cpu.wz);
        assert!(0 == cpu.im);
        assert!(!cpu.halt);
        assert!(!cpu.iff1);
        assert!(!cpu.iff2);
        assert!(0 == cpu.i);
        assert!(0 == cpu.r);
    }

    #[test]
    fn reg16_rw() {
        let mut cpu = CPU::new();
        cpu.w16(BC, 0x1234);
        cpu.w16(DE, 0x5678);
        cpu.w16(HL, 0x1357);
        cpu.w16(AF, 0x1122);
        assert!(0x12 == cpu.reg[B]);
        assert!(0x34 == cpu.reg[C]);
        assert!(0x1234 == cpu.r16(BC));
        assert!(0x56 == cpu.reg[D]);
        assert!(0x78 == cpu.reg[E]);
        assert!(0x5678 == cpu.r16(DE));
        assert!(0x13 == cpu.reg[H]);
        assert!(0x57 == cpu.reg[L]);
        assert!(0x1357 == cpu.r16(HL));
        assert!(0x11 == cpu.reg[F]);
        assert!(0x22 == cpu.reg[A]);
    }

    #[test]
    fn halt() {
        let mut cpu = CPU::new();
        cpu.pc = 0x1234;
        cpu.halt();
        assert!(cpu.halt);
        assert!(0x1233 == cpu.pc);
    }

    #[test]
    fn rst() {
        let mut cpu = CPU::new();
        cpu.pc = 0x123;
        cpu.w16(SP, 0x100);
        cpu.rst(0x38);
        assert!(0xFE == cpu.r16(SP));
        assert!(cpu.mem.r16(cpu.r16(SP)) == 0x123);
        assert!(0x38 == cpu.pc);
        assert!(0x38 == cpu.wz);
    }

    #[test]
    fn push() {
        let mut cpu = CPU::new();
        cpu.w16(SP, 0x100);
        cpu.push(0x1234);
        assert!(0xFE == cpu.r16(SP));
        assert!(cpu.mem.r16(cpu.r16(SP)) == 0x1234);
    }

    fn test_flags(flags: u8, mask: i32) -> bool {
        (((flags as i32) & !(XF|YF)) & mask) == mask
    }

    #[test]
    fn add8() {
        let mut cpu = CPU::new();
        cpu.reg[A] = 0xF;
        cpu.add8(0xF);
        assert!(0x1E == cpu.reg[A]);
        assert!(test_flags(cpu.reg[F], HF));
        cpu.add8(0xE0);
        assert!(0xFE == cpu.reg[A]);
        assert!(test_flags(cpu.reg[F], SF));
        cpu.reg[A] = 0x81;
        cpu.add8(0x80);
        assert!(0x01 == cpu.reg[A]);
        assert!(test_flags(cpu.reg[F], VF|CF));
        cpu.add8(0xFF);
        assert!(0x00 == cpu.reg[A]);
        assert!(test_flags(cpu.reg[F], ZF|HF|CF));
    }

    #[test]
    fn adc8() {
        let mut cpu = CPU::new();
        cpu.reg[A] = 0x00;
        cpu.adc8(0x00);
        assert!(0x00 == cpu.reg[A]);
        assert!(test_flags(cpu.reg[F], ZF));
        cpu.adc8(0x41);
        assert!(0x41 == cpu.reg[A]);
        assert!(test_flags(cpu.reg[F], 0));
        cpu.adc8(0x61);
        assert!(0xA2 == cpu.reg[A]);
        assert!(test_flags(cpu.reg[F], SF|VF));
        cpu.adc8(0x81);
        assert!(0x23 == cpu.reg[A]);
        assert!(test_flags(cpu.reg[F], VF|CF));
        cpu.adc8(0x41);
        assert!(0x65 == cpu.reg[A]);
        assert!(test_flags(cpu.reg[F], 0));
    }

    #[test]
    fn sub8() {
        let mut cpu = CPU::new();
        cpu.reg[A] = 0x04;
        cpu.sub8(0x04);
        assert!(0x00 == cpu.reg[A]);
        assert!(test_flags(cpu.reg[F], ZF|NF));
        cpu.sub8(0x01);
        assert!(0xFF == cpu.reg[A]);
        assert!(test_flags(cpu.reg[F], SF|HF|NF|CF));
        cpu.sub8(0xF8);
        assert!(0x07 == cpu.reg[A]);
        assert!(test_flags(cpu.reg[F], NF));
        cpu.sub8(0x0F);
        assert!(0xF8 == cpu.reg[A]);
        assert!(test_flags(cpu.reg[F], SF|HF|NF|CF));
    }

    #[test]
    fn sbc8() {
        let mut cpu = CPU::new();
        cpu.reg[A] = 0x04;
        cpu.sbc8(0x04);
        assert!(0x00 == cpu.reg[A]);
        assert!(test_flags(cpu.reg[F], ZF|NF));
        cpu.sbc8(0x01);
        assert!(0xFF == cpu.reg[A]);
        assert!(test_flags(cpu.reg[F], SF|HF|NF|CF));
        cpu.sbc8(0xF8);
        assert!(0x06 == cpu.reg[A]);
        assert!(test_flags(cpu.reg[F], NF));
    }
}

<commit_msg>Minor tweak<commit_after>use memory::Memory;

/// Z80 status flags
pub const CF : i32 = 1<<0;      // carry flag
pub const NF : i32 = 1<<1;      // add/subtract flag
pub const VF : i32 = 1<<2;      // parity/overflow flag
//const PF : i32 = 1<<2;      // parity/overflow flag
pub const XF : i32 = 1<<3;      // undocumented flag bit 3
pub const HF : i32 = 1<<4;      // half-carry flag
pub const YF : i32 = 1<<5;      // undocumented flag bit 5
pub const ZF : i32 = 1<<6;      // zero flag
pub const SF : i32 = 1<<7;      // sign flag

/// 8-bit register indices
pub const B : usize = 0;
pub const C : usize = 1;
pub const D : usize = 2;
pub const E : usize = 3;
pub const H : usize = 4;
pub const L : usize = 5;
pub const F : usize = 6;
pub const A : usize = 7;
pub const IXH : usize = 8;
pub const IXL : usize = 9;
pub const IYH : usize = 10;
pub const IYL : usize = 11;
pub const SPH : usize = 12;
pub const SPL : usize = 13;
pub const NUM_REGS : usize = 14;

/// 16-bit register indices
pub const BC : usize = 0;
pub const DE : usize = 2;
pub const HL : usize = 4;
pub const AF : usize = 6;
pub const IX : usize = 8;
pub const IY : usize = 10;
pub const SP : usize = 12;

/// the Z80 CPU state
pub struct CPU {
    pub reg : [u8; NUM_REGS],

    pub wz: u16,
    pub pc: u16,

    pub af_: u16,
    pub bc_: u16,
    pub de_: u16,
    pub hl_: u16,
    pub wz_: u16,

    pub i: u8,
    pub r: u8,
    pub im: u8,

    pub halt: bool,
    pub iff1: bool,
    pub iff2: bool,

    pub mem : Memory,
}

impl CPU {
    /// initialize a new Z80 CPU object
    pub fn new() -> CPU {
        CPU {
            reg: [0; NUM_REGS],
            af_: 0, bc_: 0, de_: 0, hl_: 0, wz_: 0,
            wz: 0, pc: 0,
            i: 0, r: 0, im: 0,
            halt: false, iff1: false, iff2: false,
            mem: Memory::new()
        }
    }

    /// reset the cpu
    pub fn reset(&mut self) {
        self.pc = 0;
        self.wz = 0;
        self.im = 0;
        self.halt = false;
        self.iff1 = false;
        self.iff2 = false;
        self.i = 0;
        self.r = 0;
    }

    /// write 16-bit register value
    pub fn w16(&mut self, r: usize, val: u16) {
        self.reg[r]   = (val>>8) as u8;
        self.reg[r+1] = (val&0xFF) as u8;
    }

    /// read 16-bit register value
    pub fn r16(&self, r: usize) -> u16 {
        let h : u16 = (self.reg[r] as u16) <<8;
        let l : u16 = self.reg[r+1] as u16;
        h | l
    }

    /// go into HALT state
    pub fn halt(&mut self) {
        self.halt = true;
        self.pc -= 1;
    }

    /// push a value on the stack
    pub fn push(&mut self, val: u16) {
        let sp = self.r16(SP).wrapping_sub(2);
        self.w16(SP, sp);
        self.mem.w16(sp, val);
    }

    /// implement RST instruction
    pub fn rst(&mut self, val: u8) {
        // workaround for https://github.com/rust-lang/rust/issues/29975
        let pc = self.pc;
        self.push(pc);
        self.pc = val as u16;
        self.wz = self.pc;
    }

    /// compute flags for add/adc
    pub fn flags_add(acc: i32, add: i32, res: i32) -> u8 {
        let f : i32 =
            (if (res & 0xFF)==0 { ZF } else { res & SF }) |
            (res & (YF|XF)) |
            ((res>>8) & CF) |
            ((acc^add^res) & HF) |
            ((((acc^add^0x80) & (add^res))>>5) & VF);
        f as u8
    }

    /// compute flags for sub/sbc
    pub fn flags_sub(acc: i32, sub: i32, res: i32) -> u8 {
        let f : i32 = NF |
            (if (res & 0xFF)==0 { ZF } else { res & SF }) |
            (res & (YF|XF)) |
            ((res>>8) & CF) |
            ((acc^sub^res) & HF) |
            ((((acc^sub)&(res^acc))>>5)&VF);
        f as u8
    }

    /// 8-bit addition
    pub fn add8(&mut self, v: u8) {
        let acc = self.reg[A] as i32;
        let add = v as i32;
        let res = acc + add;
        self.reg[F] = CPU::flags_add(acc, add, res);
        self.reg[A] = (res&0xFF) as u8;
    }

    /// 8-bit addition with carry
    pub fn adc8(&mut self, v: u8) {
        let acc = self.reg[A] as i32;
        let add = v as i32;
        let res = acc + add + ((self.reg[F] as i32) & CF);
        self.reg[F] = CPU::flags_add(acc, add, res);
        self.reg[A] = (res&0xFF) as u8;
    }

    /// 8-bit subtraction
    pub fn sub8(&mut self, v: u8) {
        let acc = self.reg[A] as i32;
        let sub = v as i32;
        let res = acc - sub;
        self.reg[F] = CPU::flags_sub(acc, sub, res);
        self.reg[A] = (res&0xFF) as u8;
    }

    /// 8-bit subtraction with carry
    pub fn sbc8(&mut self, v: u8) {
        let acc = self.reg[A] as i32;
        let sub = v as i32;
        let res = acc - sub - ((self.reg[F] as i32) & CF);
        self.reg[F] = CPU::flags_sub(acc, sub, res);
        self.reg[A] = (res&0xFF) as u8;
    }
}

#[cfg(test)]
mod tests {

    use super::*;

    #[test]
    fn new() {
        let cpu = CPU::new();
        assert!((0 == cpu.reg[A]) && (0 == cpu.reg[F]));
        assert!((0 == cpu.reg[B]) && (0 == cpu.reg[C]));
        assert!((0 == cpu.reg[D]) && (0 == cpu.reg[E]));
        assert!((0 == cpu.reg[H]) && (0 == cpu.reg[L]));
        assert!((0 == cpu.reg[IXH]) && (0 == cpu.reg[IXL]));
        assert!((0 == cpu.reg[IYH]) && (0 == cpu.reg[IYL]));
        assert!((0 == cpu.reg[SPH]) && (0 == cpu.reg[SPL]));
        assert!(0 == cpu.wz);
        assert!(0 == cpu.pc);
        assert!((0 == cpu.af_) && (0 == cpu.bc_));
        assert!((0 == cpu.de_) && (0 == cpu.hl_));
        assert!(0 == cpu.wz_);
        assert!((0 == cpu.i) && (0 == cpu.r));
        assert!(0 == cpu.im);
        assert!(!cpu.halt);
        assert!(!cpu.iff1);
        assert!(!cpu.iff2);
    }

    #[test]
    fn reset() {
        let mut cpu = CPU::new();
        cpu.pc = 0x1234;
        cpu.wz = 1234;
        cpu.im = 45;
        cpu.halt = true;
        cpu.iff1 = true;
        cpu.iff2 = true;
        cpu.i = 2;
        cpu.r = 3;
        cpu.reset();
        assert!(0 == cpu.pc);
        assert!(0 == cpu.wz);
        assert!(0 == cpu.im);
        assert!(!cpu.halt);
        assert!(!cpu.iff1);
        assert!(!cpu.iff2);
        assert!(0 == cpu.i);
        assert!(0 == cpu.r);
    }

    #[test]
    fn reg16_rw() {
        let mut cpu = CPU::new();
        cpu.w16(BC, 0x1234);
        cpu.w16(DE, 0x5678);
        cpu.w16(HL, 0x1357);
        cpu.w16(AF, 0x1122);
        assert!(0x12 == cpu.reg[B]);
        assert!(0x34 == cpu.reg[C]);
        assert!(0x1234 == cpu.r16(BC));
        assert!(0x56 == cpu.reg[D]);
        assert!(0x78 == cpu.reg[E]);
        assert!(0x5678 == cpu.r16(DE));
        assert!(0x13 == cpu.reg[H]);
        assert!(0x57 == cpu.reg[L]);
        assert!(0x1357 == cpu.r16(HL));
        assert!(0x11 == cpu.reg[F]);
        assert!(0x22 == cpu.reg[A]);
    }

    #[test]
    fn halt() {
        let mut cpu = CPU::new();
        cpu.pc = 0x1234;
        cpu.halt();
        assert!(cpu.halt);
        assert!(0x1233 == cpu.pc);
    }

    #[test]
    fn rst() {
        let mut cpu = CPU::new();
        cpu.pc = 0x123;
        cpu.w16(SP, 0x100);
        cpu.rst(0x38);
        assert!(0xFE == cpu.r16(SP));
        assert!(cpu.mem.r16(cpu.r16(SP)) == 0x123);
        assert!(0x38 == cpu.pc);
        assert!(0x38 == cpu.wz);
    }

    #[test]
    fn push() {
        let mut cpu = CPU::new();
        cpu.w16(SP, 0x100);
        cpu.push(0x1234);
        assert!(0xFE == cpu.r16(SP));
        assert!(cpu.mem.r16(cpu.r16(SP)) == 0x1234);
    }

    fn test_flags(flags: u8, mask: i32) -> bool {
        (((flags as i32) & !(XF|YF)) & mask) == mask
    }

    #[test]
    fn add8() {
        let mut cpu = CPU::new();
        cpu.reg[A] = 0xF;
        cpu.add8(0xF);
        assert!(0x1E == cpu.reg[A]);
        assert!(test_flags(cpu.reg[F], HF));
        cpu.add8(0xE0);
        assert!(0xFE == cpu.reg[A]);
        assert!(test_flags(cpu.reg[F], SF));
        cpu.reg[A] = 0x81;
        cpu.add8(0x80);
        assert!(0x01 == cpu.reg[A]);
        assert!(test_flags(cpu.reg[F], VF|CF));
        cpu.add8(0xFF);
        assert!(0x00 == cpu.reg[A]);
        assert!(test_flags(cpu.reg[F], ZF|HF|CF));
    }

    #[test]
    fn adc8() {
        let mut cpu = CPU::new();
        cpu.reg[A] = 0x00;
        cpu.adc8(0x00);
        assert!(0x00 == cpu.reg[A]);
        assert!(test_flags(cpu.reg[F], ZF));
        cpu.adc8(0x41);
        assert!(0x41 == cpu.reg[A]);
        assert!(test_flags(cpu.reg[F], 0));
        cpu.adc8(0x61);
        assert!(0xA2 == cpu.reg[A]);
        assert!(test_flags(cpu.reg[F], SF|VF));
        cpu.adc8(0x81);
        assert!(0x23 == cpu.reg[A]);
        assert!(test_flags(cpu.reg[F], VF|CF));
        cpu.adc8(0x41);
        assert!(0x65 == cpu.reg[A]);
        assert!(test_flags(cpu.reg[F], 0));
    }

    #[test]
    fn sub8() {
        let mut cpu = CPU::new();
        cpu.reg[A] = 0x04;
        cpu.sub8(0x04);
        assert!(0x00 == cpu.reg[A]);
        assert!(test_flags(cpu.reg[F], ZF|NF));
        cpu.sub8(0x01);
        assert!(0xFF == cpu.reg[A]);
        assert!(test_flags(cpu.reg[F], SF|HF|NF|CF));
        cpu.sub8(0xF8);
        assert!(0x07 == cpu.reg[A]);
        assert!(test_flags(cpu.reg[F], NF));
        cpu.sub8(0x0F);
        assert!(0xF8 == cpu.reg[A]);
        assert!(test_flags(cpu.reg[F], SF|HF|NF|CF));
    }

    #[test]
    fn sbc8() {
        let mut cpu = CPU::new();
        cpu.reg[A] = 0x04;
        cpu.sbc8(0x04);
        assert!(0x00 == cpu.reg[A]);
        assert!(test_flags(cpu.reg[F], ZF|NF));
        cpu.sbc8(0x01);
        assert!(0xFF == cpu.reg[A]);
        assert!(test_flags(cpu.reg[F], SF|HF|NF|CF));
        cpu.sbc8(0xF8);
        assert!(0x06 == cpu.reg[A]);
        assert!(test_flags(cpu.reg[F], NF));
    }
}

<|endoftext|>


<commit_before>use std::fmt;
use super::interconnect::Interconnect;
use super::reg_status::RegStatus;

pub struct Cpu {
    reg_a: u8,
    reg_x: u8,
    reg_y: u8,
    reg_pc: u16,
    reg_s: u8,
    reg_status: RegStatus,

    instr_pc: u16,
    opcode: u8,
    time: u8,  // cycle of opcode we're executing next
    fetch: u8, // in the real 6502 this would sit on the data bus
}

impl Default for Cpu {
    fn default() -> Cpu {
        Cpu{
            reg_a: 0,
            reg_x: 0,
            reg_y: 0,
            reg_pc: 0xc000, // TODO - this should possibly start at the reset vector
            reg_s: 0xfd, // TODO - this should start at ff, probably wrong because of the reset
            reg_status: 0x24.into(),

            instr_pc: 0,
            opcode: 0,
            time: 0,
            fetch: 0,
        }
    }
}

impl fmt::Debug for Cpu {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let rs: u8 = self.reg_status.clone().into();
        write!(
            f,
            "A: {:02X} X: {:02X} Y: {:02X} P: {:02X} SP: {:02X}",
            self.reg_a,
            self.reg_x,
            self.reg_y,
            rs,
            self.reg_s)
    }
}

impl Cpu {
    pub fn run(&mut self, interconnect: &mut Interconnect) {
        loop {
            self.execute_cycle(interconnect);
        }
    }

    fn execute_cycle(&mut self, interconnect: &mut Interconnect) {
        self.time += 1;
        if self.time == 1 {
            // T1 - All instructions
            self.instr_pc = self.reg_pc;
            self.opcode = interconnect.read_byte(self.reg_pc);
            self.reg_pc += 1;
            return
        }

        if self.opcode == 0x4c && self.time == 2 {
            // T2 - JMP
            self.fetch = interconnect.read_byte(self.reg_pc);
            self.reg_pc += 1;
            return;
        }
        if self.opcode == 0x4c && self.time == 3 {
            // T3 - JMP
            let pch = interconnect.read_byte(self.reg_pc);
            self.reg_pc = ((pch as u16) << 8) | (self.fetch as u16);
            println!("{:04X}  {:02X} {:02X} {:02X}   JMP ${:04X}     {:?}", self.instr_pc, self.opcode, self.fetch, pch, self.reg_pc, self);
            self.time = 0;
            return;
        }

        // LDX
        if self.opcode == 0xa2 && self.time == 2 {
            // T2 - LDX Immediate
            let value = interconnect.read_byte(self.reg_pc);
            self.reg_x = value;
            self.reg_pc += 1;
            self.reg_status.zero = value == 0;
            self.reg_status.negative = (value & (1 << 7)) != 0;

            println!("{:04X}  {:02X} {:02X}      LDX #${:02X}      {:?}", self.instr_pc, self.opcode, self.reg_x, self.reg_x, self);
            self.time = 0;
            return;
        }

        // LDA
        if self.opcode == 0xa9 && self.time == 2 {
            let value = interconnect.read_byte(self.reg_pc);
            self.reg_a = value;
            self.reg_pc += 1;
            self.reg_status.zero = value == 0;
            self.reg_status.negative = (value & (1 << 7)) != 0;

            println!("{:04X}  {:02X} {:02X}      LDA #${:02X}      {:?}", self.instr_pc, self.opcode, self.reg_a, self.reg_a, self);
            self.time = 0;
            return;
        }

        if self.opcode == 0x86 && self.time == 2 {
            // T2 - STX Zero Page
            self.fetch = interconnect.read_byte(self.reg_pc);
            self.reg_pc += 1;
            return;
        }
        if self.opcode == 0x86 && self.time == 3 {
            interconnect.write_byte(self.fetch as u16, self.reg_x);
            println!("{:04X}  {:02X} {:02X}      STX ${:02X} = {:02X}  {:?}", self.instr_pc, self.opcode, self.fetch, self.fetch, self.reg_x, self);
            self.time = 0;
            return;
        }

        // JSR
        if self.opcode == 0x20 && self.time == 2{
            self.fetch = interconnect.read_byte(self.reg_pc);
            self.reg_pc += 1;
            return;
        }
        if self.opcode == 0x20 && self.time == 3{
            // internal operation (predecrement S?)
            return;
        }
        if self.opcode == 0x20 && self.time == 4{
            let byte = (self.reg_pc >> 8) as u8;
            self.push_byte(interconnect, byte);
            return;
        }
        if self.opcode == 0x20 && self.time == 5{
            let byte = self.reg_pc as u8;
            self.push_byte(interconnect, byte);
            return;
        }
        if self.opcode == 0x20 && self.time == 6{
            let pch = interconnect.read_byte(self.reg_pc);
            self.reg_pc = ((pch as u16) << 8) | (self.fetch as u16);
            println!("{:04X}  {:02X} {:02X} {:02X}   JSR ${:04X}    {:?}", self.instr_pc, self.opcode, self.fetch, pch, self.reg_pc, self);
            self.time = 0;
            return;
        }

        // NOP
        if self.opcode == 0xea && self.time == 2 {
            println!("{:04X}  {:02X}         NOP          {:?}", self.instr_pc, self.opcode, self);
            self.time = 0;
            return;
        }
        // SEC
        if self.opcode == 0x38 && self.time == 2 {
            self.reg_status.carry = true;
            println!("{:04X}  {:02X}         SEC          {:?}", self.instr_pc, self.opcode, self);
            self.time = 0;
            return;
        }
        // CLC
        if self.opcode == 0x18 && self.time == 2 {
            self.reg_status.carry = false;
            println!("{:04X}  {:02X}         SEC          {:?}", self.instr_pc, self.opcode, self);
            self.time = 0;
            return;
        }

        // Relative branch - based on reg_status values
        if self.is_branch(self.opcode) && self.time == 2 {
            self.fetch = interconnect.read_byte(self.reg_pc);
            self.reg_pc += 1;
            return;
        }
        if self.is_branch(self.opcode) && self.time == 3 {
            if self.take_branch(self.opcode) {
                // TODO - this operation should take 2 cycles
                // we should increment the low byte of pc in this cycle
                // and increment the high bytes in the next if necessary
                self.reg_pc += self.fetch as u16;
            } else {
            println!("{:04X}  {:02X} {:02X}      {} ${:04X}    {:?}", self.instr_pc, self.opcode, self.fetch, self.opcode_name(self.opcode), self.reg_pc + self.fetch as u16, self);
            self.time = 0;
            }
            return;
        }
        if self.is_branch(self.opcode) && self.time == 4 {
            // TODO - we should increment the high order byte of pc here if the
            // low order addition overflowed, and we should run for 5 cycles if
            // that happens
            println!("{:04X}  {:02X} {:02X}      {} ${:04X}    {:?}", self.instr_pc, self.opcode, self.fetch, self.opcode_name(self.opcode), self.reg_pc, self);
            self.time = 0;
            return;
        }
        if self.is_branch(self.opcode) && self.time == 5 {
        }

        panic!("Unmatched opcode/time pair {:x}/{}", self.opcode, self.time);
    }

    fn push_byte(&mut self, interconnect: &mut Interconnect, val: u8) {
        interconnect.write_byte(self.reg_s as u16, val);
        self.reg_s -= 1;
    }

    fn is_branch(&self, opcode: u8) -> bool {
        opcode & 16 == 16 && !opcode & 15 == 15
    }
    fn take_branch(&self, opcode: u8) -> bool {
        match opcode {
            0xb0 => self.reg_status.carry,
            0x90 => !self.reg_status.carry,
            0xd0 => !self.reg_status.zero,
            0xf0 => self.reg_status.zero,
            _ => panic!("Unrecognised branch instruction {:02X}", opcode),
        }
    }

    fn opcode_name(&self, opcode: u8) -> &'static str {
        match opcode {
            0xb0 => "BCS",
            0x90 => "BCC",
            0xd0 => "BNE",
            0xf0 => "BEQ",
            _ => panic!("Unrecognised instruction {:02X}", opcode),
        }
    }
}
<commit_msg>STA, BIT, BPL, BVC, BVS, BCS implemented<commit_after>use std::fmt;
use super::interconnect::Interconnect;
use super::reg_status::RegStatus;

pub struct Cpu {
    reg_a: u8,
    reg_x: u8,
    reg_y: u8,
    reg_pc: u16,
    reg_s: u8,
    reg_status: RegStatus,

    instr_pc: u16,
    opcode: u8,
    time: u8,  // cycle of opcode we're executing next
    fetch: u8, // in the real 6502 this would sit on the data bus
}

impl Default for Cpu {
    fn default() -> Cpu {
        Cpu{
            reg_a: 0,
            reg_x: 0,
            reg_y: 0,
            reg_pc: 0xc000, // TODO - this should possibly start at the reset vector
            reg_s: 0xfd, // TODO - this should start at ff, probably wrong because of the reset
            reg_status: 0x24.into(),

            instr_pc: 0,
            opcode: 0,
            time: 0,
            fetch: 0,
        }
    }
}

impl fmt::Debug for Cpu {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let rs: u8 = self.reg_status.clone().into();
        write!(
            f,
            "A: {:02X} X: {:02X} Y: {:02X} P: {:02X} SP: {:02X}",
            self.reg_a,
            self.reg_x,
            self.reg_y,
            rs,
            self.reg_s)
    }
}

impl Cpu {
    pub fn run(&mut self, interconnect: &mut Interconnect) {
        loop {
            self.execute_cycle(interconnect);
        }
    }

    fn execute_cycle(&mut self, interconnect: &mut Interconnect) {
        self.time += 1;
        if self.time == 1 {
            // T1 - All instructions
            self.instr_pc = self.reg_pc;
            self.opcode = interconnect.read_byte(self.reg_pc);
            self.reg_pc += 1;
            return
        }

        if self.opcode == 0x4c && self.time == 2 {
            // T2 - JMP
            self.fetch = interconnect.read_byte(self.reg_pc);
            self.reg_pc += 1;
            return;
        }
        if self.opcode == 0x4c && self.time == 3 {
            // T3 - JMP
            let pch = interconnect.read_byte(self.reg_pc);
            self.reg_pc = ((pch as u16) << 8) | (self.fetch as u16);
            println!("{:04X}  {:02X} {:02X} {:02X}   JMP ${:04X}     {:?}", self.instr_pc, self.opcode, self.fetch, pch, self.reg_pc, self);
            self.time = 0;
            return;
        }

        // LDX
        if self.opcode == 0xa2 && self.time == 2 {
            // T2 - LDX Immediate
            let value = interconnect.read_byte(self.reg_pc);
            self.reg_x = value;
            self.reg_pc += 1;
            self.reg_status.zero = value == 0;
            self.reg_status.negative = (value & (1 << 7)) != 0;

            println!("{:04X}  {:02X} {:02X}      LDX #${:02X}      {:?}", self.instr_pc, self.opcode, self.reg_x, self.reg_x, self);
            self.time = 0;
            return;
        }

        // LDA
        if self.opcode == 0xa9 && self.time == 2 {
            let value = interconnect.read_byte(self.reg_pc);
            self.reg_a = value;
            self.reg_pc += 1;
            self.reg_status.zero = value == 0;
            self.reg_status.negative = (value & (1 << 7)) != 0;

            println!("{:04X}  {:02X} {:02X}      LDA #${:02X}      {:?}", self.instr_pc, self.opcode, self.reg_a, self.reg_a, self);
            self.time = 0;
            return;
        }

        if (self.opcode == 0x86 || self.opcode == 0x85 || self.opcode == 0x24) && self.time == 2 {
            // TODO - All Zero Page instructions should share this
            // T2 - STX/STA/BIT Zero Page
            self.fetch = interconnect.read_byte(self.reg_pc);
            self.reg_pc += 1;
            return;
        }
        if self.opcode == 0x86 && self.time == 3 {
            interconnect.write_byte(self.fetch as u16, self.reg_x);
            println!("{:04X}  {:02X} {:02X}      STX ${:02X} = {:02X}  {:?}", self.instr_pc, self.opcode, self.fetch, self.fetch, self.reg_x, self);
            self.time = 0;
            return;
        }
        if self.opcode == 0x85 && self.time == 3 {
            interconnect.write_byte(self.fetch as u16, self.reg_a);
            println!("{:04X}  {:02X} {:02X}      STA ${:02X} = {:02X}  {:?}", self.instr_pc, self.opcode, self.fetch, self.fetch, self.reg_a, self);
            self.time = 0;
            return;
        }
        if self.opcode == 0x24 && self.time == 3 {
            let mask = interconnect.read_byte(self.fetch as u16);
            let value = self.reg_a & mask;

            self.reg_status.zero = value == 0;
            self.reg_status.overflow = (mask & (1 << 6)) != 0;
            self.reg_status.negative = (mask & (1 << 7)) != 0;

            println!("{:04X}  {:02X} {:02X}      BIT ${:02X} = {:02X}  {:?}", self.instr_pc, self.opcode, self.fetch, self.fetch, mask, self);
            self.time = 0;
            return;
        }

        // JSR
        if self.opcode == 0x20 && self.time == 2{
            self.fetch = interconnect.read_byte(self.reg_pc);
            self.reg_pc += 1;
            return;
        }
        if self.opcode == 0x20 && self.time == 3{
            // internal operation (predecrement S?)
            return;
        }
        if self.opcode == 0x20 && self.time == 4{
            let byte = (self.reg_pc >> 8) as u8;
            self.push_byte(interconnect, byte);
            return;
        }
        if self.opcode == 0x20 && self.time == 5{
            let byte = self.reg_pc as u8;
            self.push_byte(interconnect, byte);
            return;
        }
        if self.opcode == 0x20 && self.time == 6{
            let pch = interconnect.read_byte(self.reg_pc);
            self.reg_pc = ((pch as u16) << 8) | (self.fetch as u16);
            println!("{:04X}  {:02X} {:02X} {:02X}   JSR ${:04X}    {:?}", self.instr_pc, self.opcode, self.fetch, pch, self.reg_pc, self);
            self.time = 0;
            return;
        }

        // NOP
        if self.opcode == 0xea && self.time == 2 {
            println!("{:04X}  {:02X}         NOP          {:?}", self.instr_pc, self.opcode, self);
            self.time = 0;
            return;
        }
        // SEC
        if self.opcode == 0x38 && self.time == 2 {
            self.reg_status.carry = true;
            println!("{:04X}  {:02X}         SEC          {:?}", self.instr_pc, self.opcode, self);
            self.time = 0;
            return;
        }
        // CLC
        if self.opcode == 0x18 && self.time == 2 {
            self.reg_status.carry = false;
            println!("{:04X}  {:02X}         SEC          {:?}", self.instr_pc, self.opcode, self);
            self.time = 0;
            return;
        }

        // Relative branch - based on reg_status values
        if self.is_branch(self.opcode) && self.time == 2 {
            self.fetch = interconnect.read_byte(self.reg_pc);
            self.reg_pc += 1;
            return;
        }
        if self.is_branch(self.opcode) && self.time == 3 {
            if self.take_branch(self.opcode) {
                // TODO - this operation should take 2 cycles
                // we should increment the low byte of pc in this cycle
                // and increment the high bytes in the next if necessary
                self.reg_pc += self.fetch as u16;
            } else {
            println!("{:04X}  {:02X} {:02X}      {} ${:04X}    {:?}", self.instr_pc, self.opcode, self.fetch, self.opcode_name(self.opcode), self.reg_pc + self.fetch as u16, self);
            self.time = 0;
            }
            return;
        }
        if self.is_branch(self.opcode) && self.time == 4 {
            // TODO - we should increment the high order byte of pc here if the
            // low order addition overflowed, and we should run for 5 cycles if
            // that happens
            println!("{:04X}  {:02X} {:02X}      {} ${:04X}    {:?}", self.instr_pc, self.opcode, self.fetch, self.opcode_name(self.opcode), self.reg_pc, self);
            self.time = 0;
            return;
        }
        if self.is_branch(self.opcode) && self.time == 5 {
        }

        panic!("Unmatched opcode/time pair {:x}/{}", self.opcode, self.time);
    }

    fn push_byte(&mut self, interconnect: &mut Interconnect, val: u8) {
        interconnect.write_byte(self.reg_s as u16, val);
        self.reg_s -= 1;
    }

    fn is_branch(&self, opcode: u8) -> bool {
        opcode & 16 == 16 && !opcode & 15 == 15
    }
    fn take_branch(&self, opcode: u8) -> bool {
        match opcode {
            0x10 => !self.reg_status.negative,
            0x50 => !self.reg_status.overflow,
            0x70 => self.reg_status.overflow,
            0x90 => !self.reg_status.carry,
            0xb0 => self.reg_status.carry,
            0xd0 => !self.reg_status.zero,
            0xf0 => self.reg_status.zero,
            _ => panic!("Unrecognised branch instruction {:02X}", opcode),
        }
    }

    fn opcode_name(&self, opcode: u8) -> &'static str {
        match opcode {
            0x10 => "BPL",
            0x50 => "BVC",
            0x70 => "BVS",
            0x90 => "BCC",
            0xb0 => "BCS",
            0xd0 => "BNE",
            0xf0 => "BEQ",
            _ => panic!("Unrecognised instruction {:02X}", opcode),
        }
    }
}
<|endoftext|>


<commit_before>use std::env;
use std::path::PathBuf;
use std::fs;
use std::io;

use errors::*;

pub fn resolve(path: PathBuf) -> Result<PathBuf> {
    let absolute = match env::current_dir() {
        Ok(mut value) => {
            value.push(path.to_path_buf());
            value
        }
        Err(_) => bail!(ErrorKind::ResolveError(path)),
    };

    match absolute.canonicalize() {
        Ok(value) => Ok(value),
        Err(_) => bail!(ErrorKind::ResolveError(path)),
    }
}

pub fn basename(path: &PathBuf) -> Result<String> {
    match path.file_stem() {
        None => bail!(ErrorKind::BasenameError(path.to_path_buf())),
        Some(stem) => {
            let os_string = stem.to_os_string();

            match os_string.into_string() {
                Ok(string) => Ok(string),
                Err(_) => bail!(ErrorKind::BasenameError(path.to_path_buf())),
            }
        }
    }
}

/// Get the default config directory.
///
/// ```
/// use std::env;
/// use to::dir;
///
/// let mut directory = env::home_dir().unwrap();
///         directory.push(".to");
///
/// assert_eq!(dir::config(), Some(directory));
/// ```
pub fn config() -> Option<PathBuf> {
    env::home_dir().map(|mut home| {
        home.push(".to");
        home
    })
}

/// A function that acts like `mkdir -p`.
pub fn mkdirp(directory: &PathBuf) -> Result<()> {
    match fs::create_dir(&directory) {
        Ok(_) => Ok(()),
        Err(ref err) if exists(err) => Ok(()),
        Err(_) => bail!(ErrorKind::DirError(directory.to_path_buf())),
    }
}

fn exists(err: &io::Error) -> bool {
    err.kind() == io::ErrorKind::AlreadyExists
}
<commit_msg>remove unused code<commit_after>use std::env;
use std::path::PathBuf;
use std::fs;
use std::io;

use errors::*;

pub fn resolve(path: PathBuf) -> Result<PathBuf> {
    let absolute = match env::current_dir() {
        Ok(mut value) => {
            value.push(path.to_path_buf());
            value
        }
        Err(_) => bail!(ErrorKind::ResolveError(path)),
    };

    match absolute.canonicalize() {
        Ok(value) => Ok(value),
        Err(_) => bail!(ErrorKind::ResolveError(path)),
    }
}

pub fn basename(path: &PathBuf) -> Result<String> {
    match path.file_stem() {
        None => bail!(ErrorKind::BasenameError(path.to_path_buf())),
        Some(stem) => {
            let os_string = stem.to_os_string();

            match os_string.into_string() {
                Ok(string) => Ok(string),
                Err(_) => bail!(ErrorKind::BasenameError(path.to_path_buf())),
            }
        }
    }
}

/// A function that acts like `mkdir -p`.
pub fn mkdirp(directory: &PathBuf) -> Result<()> {
    match fs::create_dir(&directory) {
        Ok(_) => Ok(()),
        Err(ref err) if exists(err) => Ok(()),
        Err(_) => bail!(ErrorKind::DirError(directory.to_path_buf())),
    }
}

fn exists(err: &io::Error) -> bool {
    err.kind() == io::ErrorKind::AlreadyExists
}
<|endoftext|>


<commit_before>#![feature(phase, globs)]
extern crate regex;
#[phase(plugin)] extern crate regex_macros;
extern crate ansi_term;
extern crate unicode;

use std::os;
use std::io::fs;
use std::io::FileType::TypeDirectory;

use file::File;
use dir::Dir;
use column::Column;
use column::Alignment::Left;
use options::{Options, View};
use unix::Unix;

use ansi_term::{Plain, strip_formatting};

pub mod column;
pub mod dir;
pub mod format;
pub mod file;
pub mod filetype;
pub mod unix;
pub mod options;
pub mod sort;
pub mod term;

fn main() {
    let args: Vec<String> = os::args();

    match Options::getopts(args) {
        Err(error_code) => os::set_exit_status(error_code),
        Ok(options) => exa(&options),
    };
}

fn exa(opts: &Options) {
	let mut dirs: Vec<String> = vec![];
	let mut files: Vec<File> = vec![];

	// Separate the user-supplied paths into directories and files.
	// Files are shown first, and then each directory is expanded
	// and listed second.
	for file in opts.path_strs.iter() {
		let path = Path::new(file);
		match fs::stat(&path) {
			Ok(stat) => {
				if !opts.list_dirs && stat.kind == TypeDirectory {
					dirs.push(file.clone());
				}
				else {
					// May as well reuse the stat result from earlier
					// instead of just using File::from_path().
					files.push(File::with_stat(stat, path, None));
				}
			}
			Err(e) => println!("{}: {}", file, e),
		}
	}

    // It's only worth printing out directory names if the user supplied
    // more than one of them.
    let print_dir_names = opts.path_strs.len() > 1;
	let mut first = files.is_empty();

	if !files.is_empty() {
		view(opts, files);
	}

    for dir_name in dirs.into_iter() {
        if first {
            first = false;
        }
        else {
            print!("\n");
        }

        match Dir::readdir(Path::new(dir_name.clone())) {
            Ok(dir) => {
                let unsorted_files = dir.files();
                let files: Vec<File> = opts.transform_files(unsorted_files);

                if print_dir_names {
                    println!("{}:", dir_name);
                }

				view(opts, files);
            }
            Err(e) => {
                println!("{}: {}", dir_name, e);
                return;
            }
        };
    }
}

fn view(options: &Options, files: Vec<File>) {
	match options.view {
		View::Details(ref cols) => details_view(options, cols, files),
		View::Lines => lines_view(files),
		View::Grid(across, width) => grid_view(across, width, files),
	}
}

fn lines_view(files: Vec<File>) {
    for file in files.iter() {
        println!("{}", file.file_name());
    }
}

fn grid_view(across: bool, console_width: uint, files: Vec<File>) {
    let max_column_length = files.iter().map(|f| f.file_name_width()).max().unwrap_or(0);
    let num_columns = (console_width + 1) / (max_column_length + 1);
    let count = files.len();

    let mut num_rows = count / num_columns;
    if count % num_columns != 0 {
        num_rows += 1;
    }

    for y in range(0, num_rows) {
        for x in range(0, num_columns) {
            let num = if across {
                y * num_columns + x
            }
            else {
                y + num_rows * x
            };

            if num >= count {
                continue;
            }

            let ref file = files[num];
            let file_name = file.name.clone();
            let styled_name = file.file_colour().paint(file_name.as_slice()).to_string();
            if x == num_columns - 1 {
                print!("{}", styled_name);
            }
            else {
                print!("{}", Left.pad_string(&styled_name, max_column_length - file_name.len() + 1));
            }
        }
        print!("\n");
    }
}

fn details_view(options: &Options, columns: &Vec<Column>, files: Vec<File>) {
    // The output gets formatted into columns, which looks nicer. To
    // do this, we have to write the results into a table, instead of
    // displaying each file immediately, then calculating the maximum
    // width of each column based on the length of the results and
    // padding the fields during output.

    let mut cache = Unix::empty_cache();

    let mut table: Vec<Vec<String>> = files.iter()
        .map(|f| columns.iter().map(|c| f.display(c, &mut cache)).collect())
        .collect();

    if options.header {
        table.insert(0, columns.iter().map(|c| Plain.underline().paint(c.header()).to_string()).collect());
    }

    // Each column needs to have its invisible colour-formatting
    // characters stripped before it has its width calculated, or the
    // width will be incorrect and the columns won't line up properly.
    // This is fairly expensive to do (it uses a regex), so the
    // results are cached.

    let lengths: Vec<Vec<uint>> = table.iter()
        .map(|row| row.iter().map(|col| strip_formatting(col.clone()).len()).collect())
        .collect();

    let column_widths: Vec<uint> = range(0, columns.len())
        .map(|n| lengths.iter().map(|row| row[n]).max().unwrap_or(0))
        .collect();

    for (field_widths, row) in lengths.iter().zip(table.iter()) {
        for (num, column) in columns.iter().enumerate() {
            if num != 0 {
                print!(" ");
            }

            if num == columns.len() - 1 {
                print!("{}", row[num]);
            }
            else {
                let padding = column_widths[num] - field_widths[num];
                print!("{}", column.alignment().pad_string(&row[num], padding));
            }
        }
        print!("\n");
    }
}
<commit_msg>Display files on a single line if possible<commit_after>#![feature(phase, globs)]
extern crate regex;
#[phase(plugin)] extern crate regex_macros;
extern crate ansi_term;
extern crate unicode;

use std::os;
use std::io::fs;
use std::io::FileType::TypeDirectory;
use std::iter::AdditiveIterator;
use std::str::StrVector;

use file::File;
use dir::Dir;
use column::Column;
use column::Alignment::Left;
use options::{Options, View};
use unix::Unix;

use ansi_term::{Plain, strip_formatting};

pub mod column;
pub mod dir;
pub mod format;
pub mod file;
pub mod filetype;
pub mod unix;
pub mod options;
pub mod sort;
pub mod term;

fn main() {
    let args: Vec<String> = os::args();

    match Options::getopts(args) {
        Err(error_code) => os::set_exit_status(error_code),
        Ok(options) => exa(&options),
    };
}

fn exa(opts: &Options) {
    let mut dirs: Vec<String> = vec![];
    let mut files: Vec<File> = vec![];

    // Separate the user-supplied paths into directories and files.
    // Files are shown first, and then each directory is expanded
    // and listed second.
    for file in opts.path_strs.iter() {
        let path = Path::new(file);
        match fs::stat(&path) {
            Ok(stat) => {
                if !opts.list_dirs && stat.kind == TypeDirectory {
                    dirs.push(file.clone());
                }
                else {
                    // May as well reuse the stat result from earlier
                    // instead of just using File::from_path().
                    files.push(File::with_stat(stat, path, None));
                }
            }
            Err(e) => println!("{}: {}", file, e),
        }
    }

    // It's only worth printing out directory names if the user supplied
    // more than one of them.
    let print_dir_names = opts.path_strs.len() > 1;
    let mut first = files.is_empty();

    if !files.is_empty() {
        view(opts, files);
    }

    for dir_name in dirs.into_iter() {
        if first {
            first = false;
        }
        else {
            print!("\n");
        }

        match Dir::readdir(Path::new(dir_name.clone())) {
            Ok(dir) => {
                let unsorted_files = dir.files();
                let files: Vec<File> = opts.transform_files(unsorted_files);

                if print_dir_names {
                    println!("{}:", dir_name);
                }

                view(opts, files);
            }
            Err(e) => {
                println!("{}: {}", dir_name, e);
                return;
            }
        };
    }
}

fn view(options: &Options, files: Vec<File>) {
    match options.view {
        View::Details(ref cols) => details_view(options, cols, files),
        View::Lines => lines_view(files),
        View::Grid(across, width) => grid_view(across, width, files),
    }
}

fn lines_view(files: Vec<File>) {
    for file in files.iter() {
        println!("{}", file.file_name());
    }
}

fn grid_view(across: bool, console_width: uint, files: Vec<File>) {
    // Check if all the files can be displayed on one line, and do
    // that if possible.
    let count = files.len();
    let spacing = 2;
    if files.iter().map(|ref f| f.name.len() + spacing).sum() + (count - 1) * spacing <= console_width {
        let names: Vec<String> = files.iter().map(|ref f| f.file_name().to_string()).collect();
        println!("{}", names.connect("  "));
        return;
    }

    // Otherwise, contort them into a grid.
    let max_column_length = files.iter().map(|f| f.file_name_width()).max().unwrap_or(0);
    let num_columns = (console_width + 1) / (max_column_length + 1);

    let mut num_rows = count / num_columns;
    if count % num_columns != 0 {
        num_rows += 1;
    }

    for y in range(0, num_rows) {
        for x in range(0, num_columns) {
            let num = if across {
                y * num_columns + x
            }
            else {
                y + num_rows * x
            };

            if num >= count {
                continue;
            }

            let ref file = files[num];
            let file_name = file.name.clone();
            let styled_name = file.file_colour().paint(file_name.as_slice()).to_string();
            if x == num_columns - 1 {
                print!("{}", styled_name);
            }
            else {
                print!("{}", Left.pad_string(&styled_name, max_column_length - file_name.len() + 1));
            }
        }
        print!("\n");
    }
}

fn details_view(options: &Options, columns: &Vec<Column>, files: Vec<File>) {
    // The output gets formatted into columns, which looks nicer. To
    // do this, we have to write the results into a table, instead of
    // displaying each file immediately, then calculating the maximum
    // width of each column based on the length of the results and
    // padding the fields during output.

    let mut cache = Unix::empty_cache();

    let mut table: Vec<Vec<String>> = files.iter()
        .map(|f| columns.iter().map(|c| f.display(c, &mut cache)).collect())
        .collect();

    if options.header {
        table.insert(0, columns.iter().map(|c| Plain.underline().paint(c.header()).to_string()).collect());
    }

    // Each column needs to have its invisible colour-formatting
    // characters stripped before it has its width calculated, or the
    // width will be incorrect and the columns won't line up properly.
    // This is fairly expensive to do (it uses a regex), so the
    // results are cached.

    let lengths: Vec<Vec<uint>> = table.iter()
        .map(|row| row.iter().map(|col| strip_formatting(col.clone()).len()).collect())
        .collect();

    let column_widths: Vec<uint> = range(0, columns.len())
        .map(|n| lengths.iter().map(|row| row[n]).max().unwrap_or(0))
        .collect();

    for (field_widths, row) in lengths.iter().zip(table.iter()) {
        for (num, column) in columns.iter().enumerate() {
            if num != 0 {
                print!(" ");
            }

            if num == columns.len() - 1 {
                print!("{}", row[num]);
            }
            else {
                let padding = column_widths[num] - field_widths[num];
                print!("{}", column.alignment().pad_string(&row[num], padding));
            }
        }
        print!("\n");
    }
}
<|endoftext|>


<commit_before>extern crate libc;

use std::slice;
use libc::{c_int, c_uchar, c_void, close, pipe, read, write};

extern "C" {
    fn fork () -> c_int;
    fn kill (pid: c_int, sig: c_int) -> c_int;
}

pub fn new_pipe() -> [c_int; 2] {
    let mut fds : [c_int; 2] = [0; 2];
    #[cfg(target_os="windows")]
    unsafe { pipe(fds.as_mut_ptr(), 2048, libc::O_BINARY) };
    #[cfg(not(target_os="windows"))]
    unsafe { pipe(fds.as_mut_ptr()) };
    fds
}

pub fn fork_process() -> c_int {
    unsafe { fork() }
}

pub fn kill_process(pid: c_int) -> c_int {
    unsafe { kill(pid, 9) }
}

pub fn set_non_blocking(fd: c_int) {
    #[cfg(not(target_os="windows"))]
    unsafe { libc::fcntl(fd, libc::F_SETFL, libc::O_NONBLOCK) };
}

pub fn close_fd(fd: c_int) {
    unsafe { close(fd) };
}

pub fn send_message(fd: c_int, command: &str) {
    let mut arr = ::neovim::Array::new();
    arr.add_string(command);
    let msg = ::neovim::serialize_message(1, "vim_command", &arr);
    let msg_ref: &str = msg.as_ref();
    let msg_ptr = msg_ref.as_ptr() as *const c_void;
    #[cfg(target_os="windows")]
    let len = msg.len() as libc::c_uint;
    #[cfg(not(target_os="windows"))]
    let len = msg.len() as libc::size_t;
    unsafe { write(fd, msg_ptr, len) };
}

pub fn recv_message(fd: c_int) -> Option<::neovim::Array> {
    let mut buf : [c_uchar; 1024] = [0; 1024];
    let n = unsafe { read(fd, buf.as_mut_ptr() as *mut c_void, 1024) };
    if n < 0 {
        return None;
    }
    unsafe {
        let v = slice::from_raw_parts(buf.as_ptr(), n as usize).to_vec();
        let s = String::from_utf8_unchecked(v);
        Some(::neovim::deserialize_message(&s))
    }
}
<commit_msg>Hide more posix stuff from windows<commit_after>extern crate libc;

use std::slice;
use libc::{c_int, c_uchar, c_void, close, pipe, read, write};

#[cfg(not(target_os="windows"))]
extern "C" {
    fn fork () -> c_int;
    fn kill (pid: c_int, sig: c_int) -> c_int;
}

pub fn new_pipe() -> [c_int; 2] {
    let mut fds : [c_int; 2] = [0; 2];
    #[cfg(target_os="windows")]
    unsafe { pipe(fds.as_mut_ptr(), 2048, libc::O_BINARY) };
    #[cfg(not(target_os="windows"))]
    unsafe { pipe(fds.as_mut_ptr()) };
    fds
}

pub fn fork_process() -> c_int {
    #[cfg(target_os="windows")]
    return 1;
    #[cfg(not(target_os="windows"))]
    return unsafe { fork() };
}

pub fn kill_process(pid: c_int) -> c_int {
    #[cfg(target_os="windows")]
    return 1;
    #[cfg(not(target_os="windows"))]
    return unsafe { kill(pid, 9) };
}

pub fn set_non_blocking(fd: c_int) {
    #[cfg(not(target_os="windows"))]
    unsafe { libc::fcntl(fd, libc::F_SETFL, libc::O_NONBLOCK) };
}

pub fn close_fd(fd: c_int) {
    unsafe { close(fd) };
}

pub fn send_message(fd: c_int, command: &str) {
    let mut arr = ::neovim::Array::new();
    arr.add_string(command);
    let msg = ::neovim::serialize_message(1, "vim_command", &arr);
    let msg_ref: &str = msg.as_ref();
    let msg_ptr = msg_ref.as_ptr() as *const c_void;
    #[cfg(target_os="windows")]
    let len = msg.len() as libc::c_uint;
    #[cfg(not(target_os="windows"))]
    let len = msg.len() as libc::size_t;
    unsafe { write(fd, msg_ptr, len) };
}

pub fn recv_message(fd: c_int) -> Option<::neovim::Array> {
    let mut buf : [c_uchar; 1024] = [0; 1024];
    let n = unsafe { read(fd, buf.as_mut_ptr() as *mut c_void, 1024) };
    if n < 0 {
        return None;
    }
    unsafe {
        let v = slice::from_raw_parts(buf.as_ptr(), n as usize).to_vec();
        let s = String::from_utf8_unchecked(v);
        Some(::neovim::deserialize_message(&s))
    }
}
<|endoftext|>


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

use std::error::Error;

use dbus;
use dbus::MessageItem;
use dbus::arg::{ArgType, Iter, IterAppend};
use dbus::tree::{MethodErr, MTFn, PropInfo};

use engine;
use engine::EngineError;

use super::types::{DbusErrorEnum, TData};

pub const STRATIS_BASE_PATH: &'static str = "/org/storage/stratis1";
pub const STRATIS_BASE_SERVICE: &'static str = "org.storage.stratis1";

/// Convert a tuple as option to an Option type
pub fn tuple_to_option<T>(value: (bool, T)) -> Option<T> {
    if value.0 { Some(value.1) } else { None }
}

/// Get the next argument off the bus
pub fn get_next_arg<'a, T>(iter: &mut Iter<'a>, loc: u16) -> Result<T, MethodErr>
    where T: dbus::arg::Get<'a> + dbus::arg::Arg
{
    if iter.arg_type() == ArgType::Invalid {
        return Err(MethodErr::no_arg());
    };
    let value: T = try!(iter.read::<T>()
                            .map_err(|_| MethodErr::invalid_arg(&loc)));
    Ok(value)
}

/// Translates an engine error to a dbus error.
pub fn engine_to_dbus_err(err: &EngineError) -> (DbusErrorEnum, String) {
    let error = match *err {
        EngineError::Engine(ref e, _) => {
            match *e {
                engine::ErrorEnum::Error => DbusErrorEnum::ERROR,
                engine::ErrorEnum::AlreadyExists => DbusErrorEnum::ALREADY_EXISTS,
                engine::ErrorEnum::Busy => DbusErrorEnum::BUSY,
                engine::ErrorEnum::Invalid => DbusErrorEnum::ERROR,
                engine::ErrorEnum::NotFound => DbusErrorEnum::NOTFOUND,
            }
        }
        EngineError::Io(_) => DbusErrorEnum::IO_ERROR,
        EngineError::Nix(_) => DbusErrorEnum::NIX_ERROR,
        EngineError::Uuid(_) => DbusErrorEnum::INTERNAL_ERROR,
        EngineError::Utf8(_) => DbusErrorEnum::INTERNAL_ERROR,
        EngineError::Serde(_) => DbusErrorEnum::INTERNAL_ERROR,
        EngineError::DM(_) => DbusErrorEnum::INTERNAL_ERROR,
    };
    (error, err.description().to_owned())
}

/// Convenience function to convert a return code and a string to
/// appropriately typed MessageItems.
pub fn code_to_message_items(code: DbusErrorEnum, mes: String) -> (MessageItem, MessageItem) {
    (MessageItem::UInt16(code.into()), MessageItem::Str(mes))
}

/// Convenience function to directly yield MessageItems for OK code and message.
pub fn ok_message_items() -> (MessageItem, MessageItem) {
    let code = DbusErrorEnum::OK;
    code_to_message_items(code, code.get_error_string().into())
}

pub fn default_object_path<'a>() -> dbus::Path<'a> {
    dbus::Path::new("/").expect("'/' is guaranteed to be a valid Path.")
}

/// Similar to Option::ok_or, but unpacks a reference to a reference.
pub fn ref_ok_or<'a, E, T>(opt: &'a Option<T>, err: E) -> Result<&'a T, E> {
    match opt {
        &Some(ref t) => Ok(t),
        &None => Err(err),
    }
}

/// Get the UUID for an object path.
pub fn get_uuid(i: &mut IterAppend, p: &PropInfo<MTFn<TData>, TData>) -> Result<(), MethodErr> {
    let object_path = p.path.get_name();
    let path = p.tree
        .get(&object_path)
        .expect("implicit argument must be in tree");
    let data = try!(ref_ok_or(path.get_data(),
                              MethodErr::failed(&format!("no data for object path {}",
                                                         &object_path))));
    i.append(MessageItem::Str(format!("{}", data.uuid.simple())));
    Ok(())
}

/// Get the parent object path for an object path.
pub fn get_parent(i: &mut IterAppend, p: &PropInfo<MTFn<TData>, TData>) -> Result<(), MethodErr> {
    let object_path = p.path.get_name();
    let path = p.tree
        .get(&object_path)
        .expect("implicit argument must be in tree");
    let data = try!(ref_ok_or(path.get_data(),
                              MethodErr::failed(&format!("no data for object path {}",
                                                         &object_path))));
    i.append(MessageItem::ObjectPath(data.parent.clone()));
    Ok(())
}
<commit_msg>Get rid of unnecessary engine import<commit_after>// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

use std::error::Error;

use dbus;
use dbus::MessageItem;
use dbus::arg::{ArgType, Iter, IterAppend};
use dbus::tree::{MethodErr, MTFn, PropInfo};

use engine::{EngineError, ErrorEnum};

use super::types::{DbusErrorEnum, TData};

pub const STRATIS_BASE_PATH: &'static str = "/org/storage/stratis1";
pub const STRATIS_BASE_SERVICE: &'static str = "org.storage.stratis1";

/// Convert a tuple as option to an Option type
pub fn tuple_to_option<T>(value: (bool, T)) -> Option<T> {
    if value.0 { Some(value.1) } else { None }
}

/// Get the next argument off the bus
pub fn get_next_arg<'a, T>(iter: &mut Iter<'a>, loc: u16) -> Result<T, MethodErr>
    where T: dbus::arg::Get<'a> + dbus::arg::Arg
{
    if iter.arg_type() == ArgType::Invalid {
        return Err(MethodErr::no_arg());
    };
    let value: T = try!(iter.read::<T>()
                            .map_err(|_| MethodErr::invalid_arg(&loc)));
    Ok(value)
}

/// Translates an engine error to a dbus error.
pub fn engine_to_dbus_err(err: &EngineError) -> (DbusErrorEnum, String) {
    let error = match *err {
        EngineError::Engine(ref e, _) => {
            match *e {
                ErrorEnum::Error => DbusErrorEnum::ERROR,
                ErrorEnum::AlreadyExists => DbusErrorEnum::ALREADY_EXISTS,
                ErrorEnum::Busy => DbusErrorEnum::BUSY,
                ErrorEnum::Invalid => DbusErrorEnum::ERROR,
                ErrorEnum::NotFound => DbusErrorEnum::NOTFOUND,
            }
        }
        EngineError::Io(_) => DbusErrorEnum::IO_ERROR,
        EngineError::Nix(_) => DbusErrorEnum::NIX_ERROR,
        EngineError::Uuid(_) => DbusErrorEnum::INTERNAL_ERROR,
        EngineError::Utf8(_) => DbusErrorEnum::INTERNAL_ERROR,
        EngineError::Serde(_) => DbusErrorEnum::INTERNAL_ERROR,
        EngineError::DM(_) => DbusErrorEnum::INTERNAL_ERROR,
    };
    (error, err.description().to_owned())
}

/// Convenience function to convert a return code and a string to
/// appropriately typed MessageItems.
pub fn code_to_message_items(code: DbusErrorEnum, mes: String) -> (MessageItem, MessageItem) {
    (MessageItem::UInt16(code.into()), MessageItem::Str(mes))
}

/// Convenience function to directly yield MessageItems for OK code and message.
pub fn ok_message_items() -> (MessageItem, MessageItem) {
    let code = DbusErrorEnum::OK;
    code_to_message_items(code, code.get_error_string().into())
}

pub fn default_object_path<'a>() -> dbus::Path<'a> {
    dbus::Path::new("/").expect("'/' is guaranteed to be a valid Path.")
}

/// Similar to Option::ok_or, but unpacks a reference to a reference.
pub fn ref_ok_or<'a, E, T>(opt: &'a Option<T>, err: E) -> Result<&'a T, E> {
    match opt {
        &Some(ref t) => Ok(t),
        &None => Err(err),
    }
}

/// Get the UUID for an object path.
pub fn get_uuid(i: &mut IterAppend, p: &PropInfo<MTFn<TData>, TData>) -> Result<(), MethodErr> {
    let object_path = p.path.get_name();
    let path = p.tree
        .get(&object_path)
        .expect("implicit argument must be in tree");
    let data = try!(ref_ok_or(path.get_data(),
                              MethodErr::failed(&format!("no data for object path {}",
                                                         &object_path))));
    i.append(MessageItem::Str(format!("{}", data.uuid.simple())));
    Ok(())
}

/// Get the parent object path for an object path.
pub fn get_parent(i: &mut IterAppend, p: &PropInfo<MTFn<TData>, TData>) -> Result<(), MethodErr> {
    let object_path = p.path.get_name();
    let path = p.tree
        .get(&object_path)
        .expect("implicit argument must be in tree");
    let data = try!(ref_ok_or(path.get_data(),
                              MethodErr::failed(&format!("no data for object path {}",
                                                         &object_path))));
    i.append(MessageItem::ObjectPath(data.parent.clone()));
    Ok(())
}
<|endoftext|>


<commit_before>//! Procedurally generating the game world.
//!

use super::super::world::{Chunk, ChunkIndex, Provider};

/// Main type to generate the game world. Implements the `WorldProvider` trait
/// (TODO, see #8).
pub struct WorldGenerator {
    seed: u64,
}

impl WorldGenerator {
    /// Creates the generator with the given seed.
    pub fn with_seed(seed: u64) -> Self {
        WorldGenerator { seed: seed }
    }

    /// Returns the seed of this world generator.
    pub fn seed(&self) -> u64 {
        self.seed
    }
}

impl Provider for WorldGenerator {
    /// Returns requested `Chunk`.
    fn load_chunk(&self, _: ChunkIndex) -> Option<Chunk> {
        // for the moment returns dummy chunk
        Some(Chunk::dummy())
    }

    /// Returns wether `Chunk` load is possible
    fn is_chunk_loadable(&self, _: ChunkIndex) -> bool {
        // for the moment returns true
        true
    }
}
<commit_msg>edit use path<commit_after>//! Procedurally generating the game world.
//!

use world::{Chunk, ChunkIndex, Provider};

/// Main type to generate the game world. Implements the `WorldProvider` trait
/// (TODO, see #8).
pub struct WorldGenerator {
    seed: u64,
}

impl WorldGenerator {
    /// Creates the generator with the given seed.
    pub fn with_seed(seed: u64) -> Self {
        WorldGenerator { seed: seed }
    }

    /// Returns the seed of this world generator.
    pub fn seed(&self) -> u64 {
        self.seed
    }
}

impl Provider for WorldGenerator {
    /// Returns requested `Chunk`.
    fn load_chunk(&self, _: ChunkIndex) -> Option<Chunk> {
        // for the moment returns dummy chunk
        Some(Chunk::dummy())
    }

    /// Returns wether `Chunk` load is possible
    fn is_chunk_loadable(&self, _: ChunkIndex) -> bool {
        // for the moment returns true
        true
    }
}
<|endoftext|>


<commit_before>//! Tokenization of source code.

use std::result::Result::{Ok, Err};
use std::str::Chars;
use std::{error, result};
use std::fmt;
use std::error::Error as StdError;

/// Result of a lexer operation.
pub type Result<T> = result::Result<T, Error>;

/// Possible errors due to a lexer operation.
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
pub enum Error {
    UnknownEscape,
    IncompleteEscape,
    UnknownToken,
    UnclosedComment,
    UnclosedString,
    MalformedNumber,
}

impl Error {
    /// Whether this error recoverable if more input is provided.
    pub fn is_recoverable(&self) -> bool {
        match *self {
            Error::IncompleteEscape
                | Error::UnclosedComment
                | Error::UnclosedString => true,
            _ => false,
        }
    }
}

impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", self.description())
    }
}

impl error::Error for Error {
    fn description(&self) -> &str {
        match *self {
            Error::UnknownEscape => "Unknown character escape",
            Error::IncompleteEscape => "Incomplete character escape",
            Error::UnknownToken => "Unknown token",
            Error::UnclosedComment => "Unclosed comment",
            Error::UnclosedString => "Unclosed string",
            Error::MalformedNumber => "Malformed integer or float",
        }
    }
}

/// Yield chars while allowing one char to be "replaced" to be yielded again.
///
/// In the case that a char must be popped in order to determine
/// if it belongs in the current token, replace can be used to
/// remember it for the start of the next token. This works
/// as long as the lexer grammar needs only one char of lookahead.
struct ReplaceOneChars<'a> {
    chars: Chars<'a>,
    replaced: Option<char>,
}

impl<'a> ReplaceOneChars<'a> {
    pub fn new(chars: Chars<'a>) -> ReplaceOneChars<'a> {
        ReplaceOneChars {
            chars: chars,
            replaced: None,
        }
    }

    pub fn replace(&mut self, c: char) {
        self.replaced = Some(c);
    }
}

impl<'a> Iterator for ReplaceOneChars<'a> {
    type Item = char;

    fn next(&mut self) -> Option<char> {
        match self.replaced.take() {
            Some(c) => Some(c),
            None => self.chars.next(),
        }
    }
}

/// Input tokens produced by lexer.
#[derive(PartialEq, Eq, Clone, Debug)]
pub enum Token {
    Integer(String),
    Float(String),
    String(String),
    Symbol(String),
    Call(String),
    OpenBrace,
    CloseBrace,
    Whitespace,
    Comment,
}

/// The lexer is a tokenizer implemented as an iterator over a string.
/// Iteration  proceeds until the source is successfully tokenized,
/// or an error is encountered.
pub struct Lexer<'a> {
    chars: ReplaceOneChars<'a>,
}

const DECIMAL: u32 = 10u32;

const SPECIAL_CHARS: [char; 7] = ['#', '(', ')', '"', '{', '}', ':'];

impl<'a> Lexer<'a> {
    /// Create a new lexer over the provided source code.
    pub fn new(src: &'a str) -> Lexer<'a> {
        Lexer {
            chars: ReplaceOneChars::new(src.chars()),
        }
    }

    /// Consume any remaining chars in underlying source.
    fn consume(&mut self) {
        for _ in &mut self.chars { }
    }

    fn whitespace(&mut self) -> Result<Token> {
        loop {
            match self.chars.next() {
                Some(c) => if !c.is_whitespace() {
                    self.chars.replace(c);
                    return Ok(Token::Whitespace);
                },
                None => return Ok(Token::Whitespace),
            }
        }
    }

    fn number(&mut self) -> Result<Token> {
        let mut s = String::new();
        let mut is_float = false;
        loop {
            match self.chars.next() {
                Some(c) => if c.is_digit(DECIMAL) {
                    s.push(c);
                } else if !is_float && c == '.' {
                    is_float = true;
                    s.push(c);
                } else if c.is_whitespace() || SPECIAL_CHARS.contains(&c) {
                    self.chars.replace(c);
                    break;
                } else {
                    return Err(Error::MalformedNumber);
                },
                None => break,
            }
        }
        Ok(if is_float {
            Token::Float(s)
        } else {
            Token::Integer(s)
        })
    }

    fn comment(&mut self) -> Result<Token> {
        loop {
            match self.chars.next() {
                Some('\n') | None => return Ok(Token::Comment),
                _ => continue,
            }
        }
    }

    fn multi_comment(&mut self) -> Result<Token> {
        loop {
            match self.chars.next() {
                Some(c) => if c == ')' {
                    return Ok(Token::Comment);
                },
                None => return Err(Error::UnclosedComment),
            }
        }
    }

    fn escape(&mut self) -> Result<char> {
        match self.chars.next() {
            Some(c) => return match c {
                '"' => Ok('"'),
                'n' => Ok('\n'),
                'r' => Ok('\r'),
                't' => Ok('\t'),
                _ => Err(Error::UnknownEscape),
            },
            None => return Err(Error::IncompleteEscape),
        }
    }

    fn string(&mut self) -> Result<Token> {
        let mut s = String::new();
        loop {
            match self.chars.next() {
                Some(c) => match c {
                    '\\' => s.push(try!(self.escape())),
                    '"' => return Ok(Token::String(s)),
                    _ => s.push(c),
                },
                None => return Err(Error::UnclosedString),
            }
        }
    }

    fn symbol(&mut self) -> Result<String> {
        let mut s = String::new();
        loop {
            match self.chars.next() {
                Some(c) => if c.is_whitespace() || SPECIAL_CHARS.contains(&c) {
                    self.chars.replace(c);
                    return Ok(s);
                } else {
                    s.push(c);
                },
                None => return Ok(s),
            }
        }
    }
}

impl<'a> Iterator for Lexer<'a> {
    type Item = Result<Token>;

    fn next(&mut self) -> Option<Result<Token>> {
        loop {
            let c = match self.chars.next() {
                Some(c) => c,
                None => return None,
            };
            let result = if c.is_whitespace() {
                self.whitespace()
            } else if c.is_digit(DECIMAL) {
                self.chars.replace(c);
                self.number()
            } else if c == '#' {
                self.comment()
            } else if c == '(' {
                self.multi_comment()
            } else if c == '"' {
                self.string()
            } else if c == '{' {
                Ok(Token::OpenBrace)
            } else if c == '}' {
                Ok(Token::CloseBrace)
            } else if c == ':' {
                self.symbol().map(|s| Token::Symbol(s))
            } else {
                self.chars.replace(c);
                self.symbol().map(|s| Token::Call(s))
            };
            // If we have errored, the rest of the source is invalid.
            if result.is_err() {
                self.consume();
            }
            return Some(result);
        }
    }
}

#[cfg(test)]
mod tests {
    use super::{Lexer, Token, Error};

    #[test]
    fn test_empty_string_is_none() {
        assert_eq!(Lexer::new("").next(), None);
    }

    #[test]
    fn test_whitespace() {
        assert_eq!(Lexer::new(" ").collect::<Vec<_>>(),
            vec![Ok(Token::Whitespace)]);
        assert_eq!(Lexer::new("  \t\t\n\n").collect::<Vec<_>>(),
            vec![Ok(Token::Whitespace)]);
    }

    #[test]
    fn test_number() {
        assert_eq!(Lexer::new("0").collect::<Vec<_>>(),
            vec![Ok(Token::Integer("0".to_string()))]);
        assert_eq!(Lexer::new("1.0").collect::<Vec<_>>(),
            vec![Ok(Token::Float("1.0".to_string()))]);
    }

    #[test]
    fn test_comment() {
        assert_eq!(Lexer::new("(this is a comment)").collect::<Vec<_>>(),
            vec![Ok(Token::Comment)]);
        assert_eq!(Lexer::new("(this is an unclosed comment")
                   .collect::<Vec<_>>(),
            vec![Err(Error::UnclosedComment)]);
    }
    #[test]
    fn test_string() {
        assert_eq!(Lexer::new("\"this is a string\"").collect::<Vec<_>>(),
            vec![Ok(Token::String("this is a string".to_string()))]);
        assert_eq!(Lexer::new("\"this is an unclosed string").collect::<Vec<_>>(),
            vec![Err(Error::UnclosedString)]);
    }

    #[test]
    fn test_call() {
        assert_eq!(Lexer::new("this-is-a-call").collect::<Vec<_>>(),
            vec![Ok(Token::Call("this-is-a-call".to_string()))]);
    }
}
<commit_msg>Reformat, reorder, and extend lexer tests<commit_after>//! Tokenization of source code.

use std::result::Result::{Ok, Err};
use std::str::Chars;
use std::{error, result};
use std::fmt;
use std::error::Error as StdError;

/// Result of a lexer operation.
pub type Result<T> = result::Result<T, Error>;

/// Possible errors due to a lexer operation.
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
pub enum Error {
    UnknownEscape,
    IncompleteEscape,
    UnknownToken,
    UnclosedComment,
    UnclosedString,
    MalformedNumber,
}

impl Error {
    /// Whether this error recoverable if more input is provided.
    pub fn is_recoverable(&self) -> bool {
        match *self {
            Error::IncompleteEscape
                | Error::UnclosedComment
                | Error::UnclosedString => true,
            _ => false,
        }
    }
}

impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", self.description())
    }
}

impl error::Error for Error {
    fn description(&self) -> &str {
        match *self {
            Error::UnknownEscape => "Unknown character escape",
            Error::IncompleteEscape => "Incomplete character escape",
            Error::UnknownToken => "Unknown token",
            Error::UnclosedComment => "Unclosed comment",
            Error::UnclosedString => "Unclosed string",
            Error::MalformedNumber => "Malformed integer or float",
        }
    }
}

/// Yield chars while allowing one char to be "replaced" to be yielded again.
///
/// In the case that a char must be popped in order to determine
/// if it belongs in the current token, replace can be used to
/// remember it for the start of the next token. This works
/// as long as the lexer grammar needs only one char of lookahead.
struct ReplaceOneChars<'a> {
    chars: Chars<'a>,
    replaced: Option<char>,
}

impl<'a> ReplaceOneChars<'a> {
    pub fn new(chars: Chars<'a>) -> ReplaceOneChars<'a> {
        ReplaceOneChars {
            chars: chars,
            replaced: None,
        }
    }

    pub fn replace(&mut self, c: char) {
        self.replaced = Some(c);
    }
}

impl<'a> Iterator for ReplaceOneChars<'a> {
    type Item = char;

    fn next(&mut self) -> Option<char> {
        match self.replaced.take() {
            Some(c) => Some(c),
            None => self.chars.next(),
        }
    }
}

/// Input tokens produced by lexer.
#[derive(PartialEq, Eq, Clone, Debug)]
pub enum Token {
    Integer(String),
    Float(String),
    String(String),
    Symbol(String),
    Call(String),
    OpenBrace,
    CloseBrace,
    Whitespace,
    Comment,
}

/// The lexer is a tokenizer implemented as an iterator over a string.
/// Iteration  proceeds until the source is successfully tokenized,
/// or an error is encountered.
pub struct Lexer<'a> {
    chars: ReplaceOneChars<'a>,
}

const DECIMAL: u32 = 10u32;

const SPECIAL_CHARS: [char; 7] = ['#', '(', ')', '"', '{', '}', ':'];

impl<'a> Lexer<'a> {
    /// Create a new lexer over the provided source code.
    pub fn new(src: &'a str) -> Lexer<'a> {
        Lexer {
            chars: ReplaceOneChars::new(src.chars()),
        }
    }

    /// Consume any remaining chars in underlying source.
    fn consume(&mut self) {
        for _ in &mut self.chars { }
    }

    fn whitespace(&mut self) -> Result<Token> {
        loop {
            match self.chars.next() {
                Some(c) => if !c.is_whitespace() {
                    self.chars.replace(c);
                    return Ok(Token::Whitespace);
                },
                None => return Ok(Token::Whitespace),
            }
        }
    }

    fn number(&mut self) -> Result<Token> {
        let mut s = String::new();
        let mut is_float = false;
        loop {
            match self.chars.next() {
                Some(c) => if c.is_digit(DECIMAL) {
                    s.push(c);
                } else if !is_float && c == '.' {
                    is_float = true;
                    s.push(c);
                } else if c.is_whitespace() || SPECIAL_CHARS.contains(&c) {
                    self.chars.replace(c);
                    break;
                } else {
                    return Err(Error::MalformedNumber);
                },
                None => break,
            }
        }
        Ok(if is_float {
            Token::Float(s)
        } else {
            Token::Integer(s)
        })
    }

    fn comment(&mut self) -> Result<Token> {
        loop {
            match self.chars.next() {
                Some('\n') | None => return Ok(Token::Comment),
                _ => continue,
            }
        }
    }

    fn multi_comment(&mut self) -> Result<Token> {
        loop {
            match self.chars.next() {
                Some(c) => if c == ')' {
                    return Ok(Token::Comment);
                },
                None => return Err(Error::UnclosedComment),
            }
        }
    }

    fn escape(&mut self) -> Result<char> {
        match self.chars.next() {
            Some(c) => return match c {
                '"' => Ok('"'),
                'n' => Ok('\n'),
                'r' => Ok('\r'),
                't' => Ok('\t'),
                _ => Err(Error::UnknownEscape),
            },
            None => return Err(Error::IncompleteEscape),
        }
    }

    fn string(&mut self) -> Result<Token> {
        let mut s = String::new();
        loop {
            match self.chars.next() {
                Some(c) => match c {
                    '\\' => s.push(try!(self.escape())),
                    '"' => return Ok(Token::String(s)),
                    _ => s.push(c),
                },
                None => return Err(Error::UnclosedString),
            }
        }
    }

    fn symbol(&mut self) -> Result<String> {
        let mut s = String::new();
        loop {
            match self.chars.next() {
                Some(c) => if c.is_whitespace() || SPECIAL_CHARS.contains(&c) {
                    self.chars.replace(c);
                    return Ok(s);
                } else {
                    s.push(c);
                },
                None => return Ok(s),
            }
        }
    }
}

impl<'a> Iterator for Lexer<'a> {
    type Item = Result<Token>;

    fn next(&mut self) -> Option<Result<Token>> {
        loop {
            let c = match self.chars.next() {
                Some(c) => c,
                None => return None,
            };
            let result = if c.is_whitespace() {
                self.whitespace()
            } else if c.is_digit(DECIMAL) {
                self.chars.replace(c);
                self.number()
            } else if c == '#' {
                self.comment()
            } else if c == '(' {
                self.multi_comment()
            } else if c == '"' {
                self.string()
            } else if c == '{' {
                Ok(Token::OpenBrace)
            } else if c == '}' {
                Ok(Token::CloseBrace)
            } else if c == ':' {
                self.symbol().map(|s| Token::Symbol(s))
            } else {
                self.chars.replace(c);
                self.symbol().map(|s| Token::Call(s))
            };
            // If we have errored, the rest of the source is invalid.
            if result.is_err() {
                self.consume();
            }
            return Some(result);
        }
    }
}

#[cfg(test)]
mod tests {
    use super::{Lexer, Token, Error};

    #[test]
    fn test_empty_string_is_none() {
        assert_eq!(Lexer::new("").next(), None);
    }

    #[test]
    fn test_number() {
        assert_eq!(Lexer::new("0").collect::<Vec<_>>(),
            vec![Ok(Token::Integer("0".into()))]);
        assert_eq!(Lexer::new("1.0").collect::<Vec<_>>(),
            vec![Ok(Token::Float("1.0".into()))]);
    }

    #[test]
    fn test_string() {
        assert_eq!(Lexer::new("\"this is a string\"").collect::<Vec<_>>(),
            vec![Ok(Token::String("this is a string".into()))]);
        assert_eq!(Lexer::new("\"this is an unclosed string").collect::<Vec<_>>(),
            vec![Err(Error::UnclosedString)]);
    }

    #[test]
    fn test_symbol() {
        assert_eq!(Lexer::new(":this-is-a-symbol").collect::<Vec<_>>(),
            vec![Ok(Token::Symbol("this-is-a-symbol".into()))]);
    }

    #[test]
    fn test_call() {
        assert_eq!(Lexer::new("this-is-a-call").collect::<Vec<_>>(),
            vec![Ok(Token::Call("this-is-a-call".into()))]);
    }

    #[test]
    fn test_braces() {
        assert_eq!(Lexer::new("{").collect::<Vec<_>>(),
            vec![Ok(Token::OpenBrace)]);
        assert_eq!(Lexer::new("}").collect::<Vec<_>>(),
            vec![Ok(Token::CloseBrace)]);
    }

    #[test]
    fn test_whitespace() {
        assert_eq!(Lexer::new(" ").collect::<Vec<_>>(),
            vec![Ok(Token::Whitespace)]);
        assert_eq!(Lexer::new("  \t\t\n\n").collect::<Vec<_>>(),
            vec![Ok(Token::Whitespace)]);
    }

    #[test]
    fn test_comment() {
        assert_eq!(Lexer::new("(this is a comment)").collect::<Vec<_>>(),
            vec![Ok(Token::Comment)]);
        assert_eq!(Lexer::new("(this is an unclosed comment")
                   .collect::<Vec<_>>(),
            vec![Err(Error::UnclosedComment)]);
    }
}
<|endoftext|>


<commit_before>extern crate getopts;

use getopts::{Options, ParsingStyle};
use std::io::{stdout, Write};
use std::fmt;
use std::iter::repeat;

/// Version of this crate. It is parsed from the environment variable that
/// cargo sets when building the crate.
pub const VERSION: &'static str         = env!("CARGO_PKG_VERSION");

/// Name of this tool.
pub const NAME: &'static str            = "yes";

/// A description of this tool.
pub const DESCRIPTION: &'static str     = "Repeats a given string and a newline \
character infinitely on stdout.";

/// The default string to repeat.
pub const DEFAULT_STRING: &'static str  = "y";

/// The buffer size to use when printing the repeated string.
pub const BUFSIZE: usize                = 16384;

/// Represents a request, this tells the yes utility what it has to do.
/// Depending on the options passed on the command line, we might take
/// different actions, which are represented here.
#[derive(Debug, PartialEq)]
pub enum Request {
    /// Main purpose of this utility: send a given string followed by a newline
    /// repeatedly to `stdout`.
    RepeatString(String),

    /// Print help information to `stdout`.
    ShowHelp,

    /// Print the utility’s version number to `stdout`.
    ShowVersion
}

/// These are all the errors that can occur in this utility.
#[derive(Debug, PartialEq)]
pub enum Error {

    /// An invalid option was encountered while parsing the options.
    InvalidOption(getopts::Fail)
}

use Request::*;
use Error::*;

impl Error {
    /// Returns the error message associated with the error.
    pub fn message(&self) -> String {
        match *self {
            InvalidOption(ref fail) => fail.to_string()
        }
    }

    /// Prints the error to stderr.
    pub fn show(&self) {
        eprintln!("{}", self.message());
    }
}

impl fmt::Display for Request {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            ShowHelp            => write!(f, "{}", help_text()),
            ShowVersion         => write!(f, "{}", version_text()),
            RepeatString(ref s) => write!(f, "{}", s)
        }
    }
}

impl Request {
    /// Do the request. This performs the action suggested by the request.
    pub fn do_it(&self) {
        match *self {
            ShowHelp => println!("{}", self),
            ShowVersion => println!("{}", self),
            RepeatString(ref s) => repeat_string(s)
        }
    }
}

/// Create an options object.
fn options() -> Options {
    let mut opts = Options::new();
    opts.parsing_style(ParsingStyle::StopAtFirstFree);

    // command-line options that this utility supports
    opts.optflag("h", "help", "display help text and exit");
    opts.optflag("v", "version", "output version information and exit");

    opts
}

/// Parse the command line arguments as options, returning either the parsed
/// `Request` or an `Error`, depending on the arguments.
pub fn parse_args(args: Vec<String>) -> Result<Request, Error> {
    // parse the command-line arguments, and return on error.
    let matches = match options().parse(args) {
        Ok(matches)  => matches,
        Err(failure) => return Err(InvalidOption(failure))
    };

    // if the help flag is set, we want to show the help text.
    if matches.opt_present("help") {
        return Ok(ShowHelp);
    }

    // if the version flag is set, we want to show the version.
    if matches.opt_present("version") {
        return Ok(ShowVersion);
    }

    // if there is no string set, we use the default one, otherwise we take the
    // free strings and join them together.
    let string = if matches.free.is_empty() {
        DEFAULT_STRING.to_owned()
    } else {
        matches.free.join(" ")
    };

    Ok(RepeatString(string))
}

#[test]
fn test_parse_args() {
    // test help options
    assert_eq!(parse_args(vec!["-h".to_string()]), Ok(ShowHelp));
    assert_eq!(parse_args(vec!["--help".to_string()]), Ok(ShowHelp));

    // test version options
    assert_eq!(parse_args(vec!["-v".to_string()]), Ok(ShowVersion));
    assert_eq!(parse_args(vec!["--version".to_string()]), Ok(ShowVersion));

    // use the DEAFULT_STRING if nothing is specified
    assert_eq!(parse_args(vec![]), Ok(RepeatString(DEFAULT_STRING.to_string())));

    // use whatever is given on command line
    assert_eq!(parse_args(vec!["str".to_string()]),
               Ok(RepeatString("str".to_string())));

    // join multiple arguments together
    assert_eq!(parse_args(vec!["str".to_string(), "dup".to_string()]),
               Ok(RepeatString("str dup".to_string())));

    // allow command line flags as string
    assert_eq!(parse_args(vec!["--".to_string(), "-h".to_string()]),
               Ok(RepeatString("-h".to_string())));
}

/// Generates a help text using the `getopts` crate. It is derived from the
/// `NAME`, the `DESCRIPTION`, as well as from the options.
pub fn help_text() -> String {
    let opts = options();
    let short_usage = format!("{} [STRING]", opts.short_usage(NAME));
    opts.usage(&short_usage)
}

/// Generates a version text, uses the `VERSION` as well as the `NAME` consts.
pub fn version_text() -> String {
    format!("{} version {}", NAME, VERSION)
}

/// Print a string repeatedly to `stdout`.
pub fn repeat_string(string: &str) {
    // creates a buffer, containing a lot of repetitions of our string
    let buf = repeat_buffer(string);

    // create a handle to stdout
    let stdout = stdout();
    let mut stdout = stdout.lock();

    // continuously print to stdout
    loop {
        stdout.write_all(buf.as_bytes());
    }
}

/// Generates a buffer, which is maximally `BUFSIZE` big, consisting of the
/// string repeated as many times as will fit into the buffer, joined with
/// newline characters.
pub fn repeat_buffer(string: &str) -> String {
    // append newline to string
    let mut s = string.to_owned();
    s.push('\n');

    // calculate how many times our string fits into a bufsize
    let times = BUFSIZE / s.len();

    // repeat the string this many times and join it together
    repeat(s).take(times).collect::<Vec<String>>().join("")
}
<commit_msg>fixes to use result<commit_after>extern crate getopts;

use getopts::{Options, ParsingStyle};
use std::io::{stdout, Write};
use std::fmt;
use std::iter::repeat;

/// Version of this crate. It is parsed from the environment variable that
/// cargo sets when building the crate.
pub const VERSION: &'static str         = env!("CARGO_PKG_VERSION");

/// Name of this tool.
pub const NAME: &'static str            = "yes";

/// A description of this tool.
pub const DESCRIPTION: &'static str     = "Repeats a given string and a newline \
character infinitely on stdout.";

/// The default string to repeat.
pub const DEFAULT_STRING: &'static str  = "y";

/// The buffer size to use when printing the repeated string.
pub const BUFSIZE: usize                = 16384;

/// Represents a request, this tells the yes utility what it has to do.
/// Depending on the options passed on the command line, we might take
/// different actions, which are represented here.
#[derive(Debug, PartialEq)]
pub enum Request {
    /// Main purpose of this utility: send a given string followed by a newline
    /// repeatedly to `stdout`.
    RepeatString(String),

    /// Print help information to `stdout`.
    ShowHelp,

    /// Print the utility’s version number to `stdout`.
    ShowVersion
}

/// These are all the errors that can occur in this utility.
#[derive(Debug, PartialEq)]
pub enum Error {

    /// An invalid option was encountered while parsing the options.
    InvalidOption(getopts::Fail)
}

use Request::*;
use Error::*;

impl Error {
    /// Returns the error message associated with the error.
    pub fn message(&self) -> String {
        match *self {
            InvalidOption(ref fail) => fail.to_string()
        }
    }

    /// Prints the error to stderr.
    pub fn show(&self) {
        eprintln!("{}", self.message());
    }
}

impl fmt::Display for Request {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            ShowHelp            => write!(f, "{}", help_text()),
            ShowVersion         => write!(f, "{}", version_text()),
            RepeatString(ref s) => write!(f, "{}", s)
        }
    }
}

impl Request {
    /// Do the request. This performs the action suggested by the request.
    pub fn do_it(&self) {
        match *self {
            ShowHelp => println!("{}", self),
            ShowVersion => println!("{}", self),
            RepeatString(ref s) => repeat_string(s)
        }
    }
}

/// Create an options object.
fn options() -> Options {
    let mut opts = Options::new();
    opts.parsing_style(ParsingStyle::StopAtFirstFree);

    // command-line options that this utility supports
    opts.optflag("h", "help", "display help text and exit");
    opts.optflag("v", "version", "output version information and exit");

    opts
}

/// Parse the command line arguments as options, returning either the parsed
/// `Request` or an `Error`, depending on the arguments.
pub fn parse_args(args: Vec<String>) -> Result<Request, Error> {
    // parse the command-line arguments, and return on error.
    let matches = match options().parse(args) {
        Ok(matches)  => matches,
        Err(failure) => return Err(InvalidOption(failure))
    };

    // if the help flag is set, we want to show the help text.
    if matches.opt_present("help") {
        return Ok(ShowHelp);
    }

    // if the version flag is set, we want to show the version.
    if matches.opt_present("version") {
        return Ok(ShowVersion);
    }

    // if there is no string set, we use the default one, otherwise we take the
    // free strings and join them together.
    let string = if matches.free.is_empty() {
        DEFAULT_STRING.to_owned()
    } else {
        matches.free.join(" ")
    };

    Ok(RepeatString(string))
}

#[test]
fn test_parse_args() {
    // test help options
    assert_eq!(parse_args(vec!["-h".to_string()]), Ok(ShowHelp));
    assert_eq!(parse_args(vec!["--help".to_string()]), Ok(ShowHelp));

    // test version options
    assert_eq!(parse_args(vec!["-v".to_string()]), Ok(ShowVersion));
    assert_eq!(parse_args(vec!["--version".to_string()]), Ok(ShowVersion));

    // use the DEAFULT_STRING if nothing is specified
    assert_eq!(parse_args(vec![]), Ok(RepeatString(DEFAULT_STRING.to_string())));

    // use whatever is given on command line
    assert_eq!(parse_args(vec!["str".to_string()]),
               Ok(RepeatString("str".to_string())));

    // join multiple arguments together
    assert_eq!(parse_args(vec!["str".to_string(), "dup".to_string()]),
               Ok(RepeatString("str dup".to_string())));

    // allow command line flags as string
    assert_eq!(parse_args(vec!["--".to_string(), "-h".to_string()]),
               Ok(RepeatString("-h".to_string())));
}

/// Generates a help text using the `getopts` crate. It is derived from the
/// `NAME`, the `DESCRIPTION`, as well as from the options.
pub fn help_text() -> String {
    let opts = options();
    let short_usage = format!("{} [STRING]", opts.short_usage(NAME));
    opts.usage(&short_usage)
}

/// Generates a version text, uses the `VERSION` as well as the `NAME` consts.
pub fn version_text() -> String {
    format!("{} version {}", NAME, VERSION)
}

/// Print a string repeatedly to `stdout`.
pub fn repeat_string(string: &str) {
    // creates a buffer, containing a lot of repetitions of our string
    let buf = repeat_buffer(string);

    // create a handle to stdout
    let stdout = stdout();
    let mut stdout = stdout.lock();

    // continuously print to stdout
    loop {
        match stdout.write_all(buf.as_bytes()) {
            Ok(_) => continue,
            Err(_) => break
        }
    }
}

/// Generates a buffer, which is maximally `BUFSIZE` big, consisting of the
/// string repeated as many times as will fit into the buffer, joined with
/// newline characters.
pub fn repeat_buffer(string: &str) -> String {
    // append newline to string
    let mut s = string.to_owned();
    s.push('\n');

    // calculate how many times our string fits into a bufsize
    let times = BUFSIZE / s.len();

    // repeat the string this many times and join it together
    repeat(s).take(times).collect::<Vec<String>>().join("")
}
<|endoftext|>