<commit_before>use std::fmt;

use super::condition_register::ConditionRegister;
use super::exception::Exception;
use super::instruction::Instruction;
use super::mmu;
use super::machine_status::MachineStatus;
use super::time_base_register::TimeBaseRegister;
use super::super::interconnect::Interconnect;

const NUM_GPR: usize = 32;
const NUM_SPR: usize = 1023;
const NUM_SR : usize = 16;

const XER : usize = 1;
const HID0: usize = 1008;

pub struct Cpu {
    pub interconnect: Interconnect,
    pub mmu: mmu::Mmu,
    cia: u32,
    nia: u32,
    ctr: u32,
    gpr: [u32; NUM_GPR],
    spr: [u32; NUM_SPR], // ToDo phase out
    pub msr: MachineStatus,
    sr: [u32; NUM_SR],
    cr: ConditionRegister,
    lr: u32,
    tb: TimeBaseRegister
}

impl Cpu {
    pub fn new(interconnect: Interconnect) -> Cpu {
        let mut cpu = Cpu {
            interconnect: interconnect,
            mmu: mmu::Mmu::new(),
            cia: 0,
            nia: 0,
            ctr: 0,
            gpr: [0; NUM_GPR],
            spr: [0; NUM_SPR],
            msr: MachineStatus::default(),
            sr: [0; NUM_SR],
            cr: ConditionRegister::default(),
            lr: 0,
            tb: TimeBaseRegister::default()
        };

        cpu.exception(Exception::SystemReset); // power on reset
        cpu
    }

    pub fn run_instruction(&mut self) {
        let instr = self.read_instruction();

        println!("{:#x} {}", self.cia, instr.opcode());

        self.nia = self.cia + 4;

        match instr.opcode() {
            10 => self.cmpli(instr),
            14 => self.addi(instr),
            15 => self.addis(instr),
            16 => self.bcx(instr),
            18 => self.bx(instr),
            19 => {
                match instr.subopcode() {
                    16 => self.bclrx(instr),
                    150 => self.isync(instr),
                    _ => panic!("Unrecognized instruction subopcode {} {}", instr.opcode(), instr.subopcode())
                }
            },
            21 => self.rlwinm(instr),
            24 => { // ori - OR immediate
                self.gpr[instr.a()] = self.gpr[instr.s()] | instr.uimm();
            },
            31 => {

                match instr.subopcode() {
                     28 => self.andx(instr),
                     32 => self.cmpl(instr),
                     40 => self.subfx(instr),
                     83 => self.mfmsr(instr),
                    146 => self.mtmsr(instr),
                    210 => self.mtsr(instr),
                    339 => self.mfspr(instr),
                    371 => self.mftb(instr),
                    467 => self.mtspr(instr),
                    _   => panic!("Unrecognized instruction subopcode {} {}", instr.opcode(), instr.subopcode())
                }

            },
            32 => self.lwz(instr),
            36 => self.stw(instr),
            44 => self.sth(instr),
            _  => panic!("Unrecognized instruction {:#x} {:#b}", instr.0, instr.opcode())
        }

        self.cia = self.nia;

        // tick timer
        self.tb.advance();
    }

    fn read_instruction(&mut self) -> Instruction {
        let addr = self.mmu.translate_address(mmu::BatType::Instruction, &self.msr, self.cia);

        Instruction(self.interconnect.read_word(addr))
    }

    // FixMe: handle exceptions properly
    pub fn exception(&mut self, e: Exception) {
        let nia = match e {
            Exception::SystemReset => 0x00100
        };

        if self.msr.exception_prefix {
            self.cia = nia ^ 0xFFF00000
        } else {
            self.cia = nia
        }

        println!("{:#x} exception occurred, nia {:#x}", nia, self.cia);
    }

    // complare logic immediate
    fn cmpli(&mut self, instr: Instruction) {
        let a = self.gpr[instr.a()];
        let b = instr.uimm();

        let mut c:u8 = if a < b {
            0b1000
        } else if a > b {
            0b0100
        } else {
            0b0010
        };

        c |= self.spr[XER] as u8 & 0b1;

        self.cr.set_field(instr.crfd(), c);
    }

    // add immediate
    fn addi(&mut self, instr: Instruction) {
        let a = instr.a();
        if a == 0 {
            self.gpr[instr.d()] = instr.uimm();
        } else {
            self.gpr[instr.d()] = self.gpr[a] + instr.uimm();
        }
    }

    // add immediate shifted
    fn addis(&mut self, instr: Instruction) {
        if instr.a() == 0 { // lis
            self.gpr[instr.d()] = instr.uimm() << 16;
        } else { // subis
            self.gpr[instr.d()] = self.gpr[instr.a()] + (instr.uimm() << 16);
        }
    }

    // branch conditional
    fn bcx(&mut self, instr: Instruction) {
        let bo = instr.bo();

        let ctr_ok = if bon(bo, 2) == 0 {
            self.ctr = self.ctr.wrapping_sub(1);

            if bon(bo, 3) != 0 {
                self.ctr == 0
            } else {
                self.ctr != 0
            }
        } else {
            true
        };

        let cond_ok = if bon(bo, 0) == 0 {
            (bon(bo, 1) == (self.cr.get_bit(instr.bi())))
        } else {
            true
        };

        if ctr_ok && cond_ok {
            if instr.aa() == 1 {
                self.nia = sign_ext_16(instr.bd() << 2) as u32;
            } else {
                self.nia = self.cia.wrapping_add(sign_ext_16(instr.bd() << 2) as u32);
            }

            if instr.lk() == 1 {
                self.lr = self.cia + 4;
            }
        }
    }

    // branch
    fn bx(&mut self, instr: Instruction) {
        if instr.aa() == 1 {
            self.nia = sign_ext_26(instr.li() << 2) as u32;
        } else {
            self.nia = self.cia.wrapping_add(sign_ext_26(instr.li() << 2) as u32);
        }

        if instr.lk() == 1 {
            self.lr = self.cia + 4;
        }
    }

    // branch conditional to link register
    fn bclrx(&mut self, instr: Instruction) {
        let bo = instr.bo();

        let ctr_ok = if bon(bo, 2) == 0 {
            self.ctr = self.ctr.wrapping_sub(1);

            if bon(bo, 3) != 0 {
                self.ctr == 0
            } else {
                self.ctr != 0
            }
        } else {
            true
        };

        let cond_ok = if bon(bo, 0) == 0 {
            (bon(bo, 1) == (self.cr.get_bit(instr.bi())))
        } else {
            true
        };

        if ctr_ok && cond_ok {
            self.nia = self.lr & 0xFFFFFFFC;

            if instr.lk() == 1 {
                self.lr = self.cia + 4;
            }
        }
    }

    #[allow(unused_variables)]
    // isync - instruction synchronize
    fn isync(&mut self, instr: Instruction) {
        // don't do anything
    }

    // rotate word immediate then AND with mask
    fn rlwinm(&mut self, instr: Instruction) {
        let r = self.gpr[instr.s()] << instr.sh();
        let m = mask(instr.mb(), instr.me());

        self.gpr[instr.a()] = r & m;
    }

    fn andx(&mut self, instr: Instruction) {
        self.gpr[instr.a()] = self.gpr[instr.d()] & self.gpr[instr.b()];

        if instr.rc() {
            self.cr.set_field(0, self.gpr[instr.a()] as u8);
        }
    }

    // ToDo: if L = 1, instruction form is invalid
    fn cmpl(&mut self, instr: Instruction) {
        let a = self.gpr[instr.a()];
        let b = self.gpr[instr.b()];

        let mut c:u8 = if a < b {
            0b1000
        } else if a > b {
            0b0100
        } else {
            0b0010
        };

        c |= self.spr[XER] as u8 & 0b1;

        self.cr.set_field(instr.crfd(), c);
    }

    // subtract from
    fn subfx(&mut self, instr: Instruction) {
        self.gpr[instr.d()] = self.gpr[instr.a()].wrapping_sub(self.gpr[instr.b()]);

        if instr.rc() {
            self.cr.set_field(0, self.gpr[instr.d()] as u8);
        }

        // TODO: update XER if OE = 1
    }

    // move from machine state register
    fn mfmsr(&mut self, instr: Instruction) {
        self.gpr[instr.d()] = self.msr.as_u32();

        // TODO: check privelege level
    }

    // move to machine state register
    fn mtmsr(&mut self, instr: Instruction) {
        self.msr = self.gpr[instr.s()].into();

        // TODO: check privelege level
    }

    // move to segment register
    fn mtsr(&mut self, instr: Instruction) {
        self.sr[instr.sr()] = self.gpr[instr.s()];

        // TODO: check privelege level -> supervisor level instruction
    }

    // move from special purpose register
    fn mfspr(&mut self, instr: Instruction) {
        let n = ((instr.spr_upper() << 5) | (instr.spr_lower() & 0b1_1111)) as usize;

        match n {
            8 => self.gpr[instr.s()] = self.lr,
            9 => self.gpr[instr.s()] = self.ctr,
            _ => {
                println!("FIXME: spr {} not implemented", n);
                self.gpr[instr.s()] = self.spr[n];
            }
        }

        // TODO: check privelege level
    }

    // move from time base
    fn mftb(&mut self, instr: Instruction) {
        let n = (instr.spr_upper() << 5) | (instr.spr_lower() & 0b1_1111);

        match n {
            268 => self.gpr[instr.d()] = self.tb.l(),
            269 => self.gpr[instr.d()] = self.tb.u(),
            _ => panic!("Unrecognized TBR {}", n) // FixMe: invoke error handler
        }
    }

    // move special purpose register
    fn mtspr(&mut self, instr: Instruction) {
        let n = ((instr.spr_upper() << 5) | (instr.spr_lower() & 0b1_1111)) as usize;

        match n {
            8 => self.lr = self.gpr[instr.s()],
            9 => self.ctr = self.gpr[instr.s()],
            528 ... 543 => { // if IBAT or DBAT, write to MMU register
                self.mmu.write_bat_reg(n, self.gpr[instr.s()]);
                //panic!("FixMe: write to BAT registers");
            },
            _ => {
                println!("FIXME: mtspr {} not implemented", n);
                self.spr[n] = self.gpr[instr.s()];
            }
        }

        // TODO: check privelege level
    }

    // load word and zero
    fn lwz(&mut self, instr: Instruction) {
        let ea = if instr.a() == 0 {
            instr.simm()
        } else {
            self.gpr[instr.a()] + instr.simm()
        };

        let addr = self.mmu.translate_address(mmu::BatType::Data, &self.msr, ea);

        self.gpr[instr.d()] = self.interconnect.read_word(addr);
    }

    // store word
    fn stw(&mut self, instr: Instruction) {
        let ea = if instr.a() == 0 {
            instr.simm()
        } else {
            self.gpr[instr.a()] + instr.simm()
        };

        let addr = self.mmu.translate_address(mmu::BatType::Data, &self.msr, ea);

        self.interconnect.write_word(addr, self.gpr[instr.s()]);
    }

    // store half word
    fn sth(&mut self, instr: Instruction) {
        let ea = if instr.a() == 0 {
            instr.simm()
        } else {
            self.gpr[instr.a()] + instr.simm()
        };

        let addr = self.mmu.translate_address(mmu::BatType::Data, &self.msr, ea);

        self.interconnect.write_halfword(addr, self.gpr[instr.s()] as u16);
    }
}

//((1 << (x - y +1)) - 1) << y
// FixMe: not sure if this is correct
// actually I think this might be backwards
fn mask(x: u8, y: u8) -> u32 {
    let mut mask:u32 = 0xFFFFFFFF >> y;

    if x >= 31 {
        mask ^= 0;
    } else {
        mask ^= 0xFFFFFFFF >> (x + 1);
    }

    if y > x {
        !mask
    } else {
        mask
    }
}

// Note: A cast from a signed value widens with signed-extension
//       A cast from an unsigned value widens with zero-extension
fn sign_ext_16(x: u16) -> i32 {
    (x as i16) as i32
}

fn sign_ext_26(x: u32) -> i32 {
    if x & 0x2000000 != 0 {
        (x | 0xFC000000) as i32
    } else {
        x as i32
    }
}

fn bon(bo: u8, n: u8) -> u8 {
    (bo >> (4-n)) & 1
}

impl fmt::Debug for Cpu {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "MSR: {:?} gpr: {:?}, sr: {:?}, cr:{:?}, HID0: {}", self.msr, self.gpr, self.sr, self.cr, self.spr[HID0])
    }
}
<commit_msg>implemented orx instruction<commit_after>use std::fmt;

use super::condition_register::ConditionRegister;
use super::exception::Exception;
use super::instruction::Instruction;
use super::mmu;
use super::machine_status::MachineStatus;
use super::time_base_register::TimeBaseRegister;
use super::super::interconnect::Interconnect;

const NUM_GPR: usize = 32;
const NUM_SPR: usize = 1023;
const NUM_SR : usize = 16;

const XER : usize = 1;
const HID0: usize = 1008;

pub struct Cpu {
    pub interconnect: Interconnect,
    pub mmu: mmu::Mmu,
    cia: u32,
    nia: u32,
    ctr: u32,
    gpr: [u32; NUM_GPR],
    spr: [u32; NUM_SPR], // ToDo phase out
    pub msr: MachineStatus,
    sr: [u32; NUM_SR],
    cr: ConditionRegister,
    lr: u32,
    tb: TimeBaseRegister
}

impl Cpu {
    pub fn new(interconnect: Interconnect) -> Cpu {
        let mut cpu = Cpu {
            interconnect: interconnect,
            mmu: mmu::Mmu::new(),
            cia: 0,
            nia: 0,
            ctr: 0,
            gpr: [0; NUM_GPR],
            spr: [0; NUM_SPR],
            msr: MachineStatus::default(),
            sr: [0; NUM_SR],
            cr: ConditionRegister::default(),
            lr: 0,
            tb: TimeBaseRegister::default()
        };

        cpu.exception(Exception::SystemReset); // power on reset
        cpu
    }

    pub fn run_instruction(&mut self) {
        let instr = self.read_instruction();

        println!("{:#x} {}", self.cia, instr.opcode());

        self.nia = self.cia + 4;

        match instr.opcode() {
            10 => self.cmpli(instr),
            14 => self.addi(instr),
            15 => self.addis(instr),
            16 => self.bcx(instr),
            18 => self.bx(instr),
            19 => {
                match instr.subopcode() {
                    16 => self.bclrx(instr),
                    150 => self.isync(instr),
                    _ => panic!("Unrecognized instruction subopcode {} {}", instr.opcode(), instr.subopcode())
                }
            },
            21 => self.rlwinm(instr),
            24 => { // ori - OR immediate
                self.gpr[instr.a()] = self.gpr[instr.s()] | instr.uimm();
            },
            31 => {

                match instr.subopcode() {
                     28 => self.andx(instr),
                     32 => self.cmpl(instr),
                     40 => self.subfx(instr),
                     83 => self.mfmsr(instr),
                    146 => self.mtmsr(instr),
                    210 => self.mtsr(instr),
                    339 => self.mfspr(instr),
                    371 => self.mftb(instr),
                    444 => self.orx(instr),
                    467 => self.mtspr(instr),
                    _   => panic!("Unrecognized instruction subopcode {} {}", instr.opcode(), instr.subopcode())
                }

            },
            32 => self.lwz(instr),
            36 => self.stw(instr),
            44 => self.sth(instr),
            _  => panic!("Unrecognized instruction {:#x} {:#b}", instr.0, instr.opcode())
        }

        self.cia = self.nia;

        // tick timer
        self.tb.advance();
    }

    fn read_instruction(&mut self) -> Instruction {
        let addr = self.mmu.translate_address(mmu::BatType::Instruction, &self.msr, self.cia);

        Instruction(self.interconnect.read_word(addr))
    }

    // FixMe: handle exceptions properly
    pub fn exception(&mut self, e: Exception) {
        let nia = match e {
            Exception::SystemReset => 0x00100
        };

        if self.msr.exception_prefix {
            self.cia = nia ^ 0xFFF00000
        } else {
            self.cia = nia
        }

        println!("{:#x} exception occurred, nia {:#x}", nia, self.cia);
    }

    // complare logic immediate
    fn cmpli(&mut self, instr: Instruction) {
        let a = self.gpr[instr.a()];
        let b = instr.uimm();

        let mut c:u8 = if a < b {
            0b1000
        } else if a > b {
            0b0100
        } else {
            0b0010
        };

        c |= self.spr[XER] as u8 & 0b1;

        self.cr.set_field(instr.crfd(), c);
    }

    // add immediate
    fn addi(&mut self, instr: Instruction) {
        let a = instr.a();
        if a == 0 {
            self.gpr[instr.d()] = instr.uimm();
        } else {
            self.gpr[instr.d()] = self.gpr[a] + instr.uimm();
        }
    }

    // add immediate shifted
    fn addis(&mut self, instr: Instruction) {
        if instr.a() == 0 { // lis
            self.gpr[instr.d()] = instr.uimm() << 16;
        } else { // subis
            self.gpr[instr.d()] = self.gpr[instr.a()] + (instr.uimm() << 16);
        }
    }

    // branch conditional
    fn bcx(&mut self, instr: Instruction) {
        let bo = instr.bo();

        let ctr_ok = if bon(bo, 2) == 0 {
            self.ctr = self.ctr.wrapping_sub(1);

            if bon(bo, 3) != 0 {
                self.ctr == 0
            } else {
                self.ctr != 0
            }
        } else {
            true
        };

        let cond_ok = if bon(bo, 0) == 0 {
            (bon(bo, 1) == (self.cr.get_bit(instr.bi())))
        } else {
            true
        };

        if ctr_ok && cond_ok {
            if instr.aa() == 1 {
                self.nia = sign_ext_16(instr.bd() << 2) as u32;
            } else {
                self.nia = self.cia.wrapping_add(sign_ext_16(instr.bd() << 2) as u32);
            }

            if instr.lk() == 1 {
                self.lr = self.cia + 4;
            }
        }
    }

    // branch
    fn bx(&mut self, instr: Instruction) {
        if instr.aa() == 1 {
            self.nia = sign_ext_26(instr.li() << 2) as u32;
        } else {
            self.nia = self.cia.wrapping_add(sign_ext_26(instr.li() << 2) as u32);
        }

        if instr.lk() == 1 {
            self.lr = self.cia + 4;
        }
    }

    // branch conditional to link register
    fn bclrx(&mut self, instr: Instruction) {
        let bo = instr.bo();

        let ctr_ok = if bon(bo, 2) == 0 {
            self.ctr = self.ctr.wrapping_sub(1);

            if bon(bo, 3) != 0 {
                self.ctr == 0
            } else {
                self.ctr != 0
            }
        } else {
            true
        };

        let cond_ok = if bon(bo, 0) == 0 {
            (bon(bo, 1) == (self.cr.get_bit(instr.bi())))
        } else {
            true
        };

        if ctr_ok && cond_ok {
            self.nia = self.lr & 0xFFFFFFFC;

            if instr.lk() == 1 {
                self.lr = self.cia + 4;
            }
        }
    }

    #[allow(unused_variables)]
    // isync - instruction synchronize
    fn isync(&mut self, instr: Instruction) {
        // don't do anything
    }

    // rotate word immediate then AND with mask
    fn rlwinm(&mut self, instr: Instruction) {
        let r = self.gpr[instr.s()] << instr.sh();
        let m = mask(instr.mb(), instr.me());

        self.gpr[instr.a()] = r & m;
    }

    fn andx(&mut self, instr: Instruction) {
        self.gpr[instr.a()] = self.gpr[instr.d()] & self.gpr[instr.b()];

        if instr.rc() {
            self.cr.set_field(0, self.gpr[instr.a()] as u8);
        }
    }

    // ToDo: if L = 1, instruction form is invalid
    fn cmpl(&mut self, instr: Instruction) {
        let a = self.gpr[instr.a()];
        let b = self.gpr[instr.b()];

        let mut c:u8 = if a < b {
            0b1000
        } else if a > b {
            0b0100
        } else {
            0b0010
        };

        c |= self.spr[XER] as u8 & 0b1;

        self.cr.set_field(instr.crfd(), c);
    }

    // subtract from
    fn subfx(&mut self, instr: Instruction) {
        self.gpr[instr.d()] = self.gpr[instr.a()].wrapping_sub(self.gpr[instr.b()]);

        if instr.rc() {
            self.cr.set_field(0, self.gpr[instr.d()] as u8);
        }

        // TODO: update XER if OE = 1
    }

    // move from machine state register
    fn mfmsr(&mut self, instr: Instruction) {
        self.gpr[instr.d()] = self.msr.as_u32();

        // TODO: check privelege level
    }

    // move to machine state register
    fn mtmsr(&mut self, instr: Instruction) {
        self.msr = self.gpr[instr.s()].into();

        // TODO: check privelege level
    }

    // move to segment register
    fn mtsr(&mut self, instr: Instruction) {
        self.sr[instr.sr()] = self.gpr[instr.s()];

        // TODO: check privelege level -> supervisor level instruction
    }

    // move from special purpose register
    fn mfspr(&mut self, instr: Instruction) {
        let n = ((instr.spr_upper() << 5) | (instr.spr_lower() & 0b1_1111)) as usize;

        match n {
            8 => self.gpr[instr.s()] = self.lr,
            9 => self.gpr[instr.s()] = self.ctr,
            _ => {
                println!("FIXME: spr {} not implemented", n);
                self.gpr[instr.s()] = self.spr[n];
            }
        }

        // TODO: check privelege level
    }

    // move from time base
    fn mftb(&mut self, instr: Instruction) {
        let n = (instr.spr_upper() << 5) | (instr.spr_lower() & 0b1_1111);

        match n {
            268 => self.gpr[instr.d()] = self.tb.l(),
            269 => self.gpr[instr.d()] = self.tb.u(),
            _ => panic!("Unrecognized TBR {}", n) // FixMe: invoke error handler
        }
    }

    fn orx(&mut self, instr: Instruction) {
        self.gpr[instr.a()] = self.gpr[instr.s()] | self.gpr[instr.b()];

        if instr.rc() {
            self.cr.set_field(0, self.gpr[instr.a()] as u8);
        }
    }

    // move special purpose register
    fn mtspr(&mut self, instr: Instruction) {
        let n = ((instr.spr_upper() << 5) | (instr.spr_lower() & 0b1_1111)) as usize;

        match n {
            8 => self.lr = self.gpr[instr.s()],
            9 => self.ctr = self.gpr[instr.s()],
            528 ... 543 => { // if IBAT or DBAT, write to MMU register
                self.mmu.write_bat_reg(n, self.gpr[instr.s()]);
                //panic!("FixMe: write to BAT registers");
            },
            _ => {
                println!("FIXME: mtspr {} not implemented", n);
                self.spr[n] = self.gpr[instr.s()];
            }
        }

        // TODO: check privelege level
    }

    // load word and zero
    fn lwz(&mut self, instr: Instruction) {
        let ea = if instr.a() == 0 {
            instr.simm()
        } else {
            self.gpr[instr.a()] + instr.simm()
        };

        let addr = self.mmu.translate_address(mmu::BatType::Data, &self.msr, ea);

        self.gpr[instr.d()] = self.interconnect.read_word(addr);
    }

    // store word
    fn stw(&mut self, instr: Instruction) {
        let ea = if instr.a() == 0 {
            instr.simm()
        } else {
            self.gpr[instr.a()] + instr.simm()
        };

        let addr = self.mmu.translate_address(mmu::BatType::Data, &self.msr, ea);

        self.interconnect.write_word(addr, self.gpr[instr.s()]);
    }

    // store half word
    fn sth(&mut self, instr: Instruction) {
        let ea = if instr.a() == 0 {
            instr.simm()
        } else {
            self.gpr[instr.a()] + instr.simm()
        };

        let addr = self.mmu.translate_address(mmu::BatType::Data, &self.msr, ea);

        self.interconnect.write_halfword(addr, self.gpr[instr.s()] as u16);
    }
}

//((1 << (x - y +1)) - 1) << y
// FixMe: not sure if this is correct
// actually I think this might be backwards
fn mask(x: u8, y: u8) -> u32 {
    let mut mask:u32 = 0xFFFFFFFF >> y;

    if x >= 31 {
        mask ^= 0;
    } else {
        mask ^= 0xFFFFFFFF >> (x + 1);
    }

    if y > x {
        !mask
    } else {
        mask
    }
}

// Note: A cast from a signed value widens with signed-extension
//       A cast from an unsigned value widens with zero-extension
fn sign_ext_16(x: u16) -> i32 {
    (x as i16) as i32
}

fn sign_ext_26(x: u32) -> i32 {
    if x & 0x2000000 != 0 {
        (x | 0xFC000000) as i32
    } else {
        x as i32
    }
}

fn bon(bo: u8, n: u8) -> u8 {
    (bo >> (4-n)) & 1
}

impl fmt::Debug for Cpu {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "MSR: {:?} gpr: {:?}, sr: {:?}, cr:{:?}, HID0: {}", self.msr, self.gpr, self.sr, self.cr, self.spr[HID0])
    }
}
<|endoftext|>


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

use std::io::Result as IoResult;
use std::io::Read;
use std::io::Error as IoError;
use std::io::ErrorKind;
use std::fmt;
use std::error::Error;

/// Reads HTTP chunks and sends back real data.
///
/// # Example
///
/// ```
/// use chunked_transfer::Decoder;
/// use std::io::Read;
///
/// let encoded = b"3\r\nhel\r\nb\r\nlo world!!!\r\n0\r\n\r\n";
/// let mut decoded = String::new();
///
/// let mut decoder = Decoder::new(encoded as &[u8]);
/// decoder.read_to_string(&mut decoded);
///
/// assert_eq!(decoded, "hello world!!!");
/// ```
pub struct Decoder<R> {
    // where the chunks come from
    source: R,

    // remaining size of the chunk being read
    // none if we are not in a chunk
    remaining_chunks_size: Option<usize>,

    // data from the start of the current chunk
    buffer: Vec<u8>,
}

impl<R> Decoder<R> where R: Read {
    pub fn new(source: R) -> Decoder<R> {
        Decoder {
            source: source,
            remaining_chunks_size: None,
            buffer: Vec::with_capacity(128),
        }
    }
}

impl<R> Read for Decoder<R> where R: Read {
    fn read(&mut self, buf: &mut [u8]) -> IoResult<usize> {
        // first possibility: we are not in a chunk
        if self.remaining_chunks_size.is_none() {
            // trying the read the chunk size
            let mut chunk_size = Vec::new();

            loop {
                let byte = match self.source.by_ref().bytes().next() {
                    Some(b) => try!(b),
                    None => return Err(IoError::new(ErrorKind::InvalidInput, DecoderError)),
                };

                if byte == b'\r' {
                    break;
                }

                chunk_size.push(byte);
            }

            match self.source.by_ref().bytes().next() {
                Some(Ok(b'\n')) => (),
                _ => return Err(IoError::new(ErrorKind::InvalidInput, DecoderError)),
            }

            let chunk_size = match String::from_utf8(chunk_size) {
                Ok(c) => c,
                Err(_) => return Err(IoError::new(ErrorKind::InvalidInput, DecoderError))
            };

            let chunk_size = match usize::from_str_radix(&chunk_size, 16) {
                Ok(c) => c,
                Err(_) => return Err(IoError::new(ErrorKind::InvalidInput, DecoderError))
            };

            // if the chunk size is 0, we are at EOF
            if chunk_size == 0 {
                if try!(self.source.by_ref().bytes().next().unwrap_or(Ok(0))) != b'\r' {
                    return Err(IoError::new(ErrorKind::InvalidInput, DecoderError));
                }
                if try!(self.source.by_ref().bytes().next().unwrap_or(Ok(0))) != b'\n' {
                    return Err(IoError::new(ErrorKind::InvalidInput, DecoderError));
                }
                return Ok(0);
            }

            // now that we now the current chunk size, calling ourselves recursively
            self.remaining_chunks_size = Some(chunk_size);
            return self.read(buf);
        }

        assert!(self.remaining_chunks_size.is_some());

        // second possibility: we continue reading from a chunk
        if buf.len() < *self.remaining_chunks_size.as_ref().unwrap() {
            let read = try!(self.source.read(buf));
            *self.remaining_chunks_size.as_mut().unwrap() -= read;
            return Ok(read);
        }

        // third possibility: the read request goes further than the current chunk
        // we simply read until the end of the chunk and return
        assert!(buf.len() >= *self.remaining_chunks_size.as_ref().unwrap());

        let remaining_chunks_size = *self.remaining_chunks_size.as_ref().unwrap();

        let buf = &mut buf[.. remaining_chunks_size];
        let read = try!(self.source.read(buf));
        *self.remaining_chunks_size.as_mut().unwrap() -= read;

        if read == remaining_chunks_size {
            self.remaining_chunks_size = None;

            if try!(self.source.by_ref().bytes().next().unwrap_or(Ok(0))) != b'\r' {
                return Err(IoError::new(ErrorKind::InvalidInput, DecoderError));
            }
            if try!(self.source.by_ref().bytes().next().unwrap_or(Ok(0))) != b'\n' {
                return Err(IoError::new(ErrorKind::InvalidInput, DecoderError));
            }
        }

        return Ok(read);
    }
}

#[derive(Debug, Copy, Clone)]
pub struct DecoderError;

impl fmt::Display for DecoderError {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        write!(fmt, "Error while decoding chunks")
    }
}

impl Error for DecoderError {
    fn description(&self) -> &str {
        "Error while decoding chunks"
    }
}

#[cfg(test)]
mod test {
    use super::Decoder;
    use std::io;
    use std::io::Read;

    #[test]
    fn test_valid_chunk_decode() {
        let source = io::Cursor::new("3\r\nhel\r\nb\r\nlo world!!!\r\n0\r\n\r\n".to_string().into_bytes());
        let mut decoded = Decoder::new(source);

        let mut string = String::new();
        decoded.read_to_string(&mut string).unwrap();

        assert_eq!(string, "hello world!!!");
    }

    #[test]
    #[should_panic]
    fn invalid_input1() {
        let source = io::Cursor::new("2\r\nhel\r\nb\r\nlo world!!!\r\n0\r\n".to_string().into_bytes());
        let mut decoded = Decoder::new(source);

        let mut string = String::new();
        decoded.read_to_string(&mut string).unwrap();
    }

    #[test]
    #[should_panic]
    fn invalid_input2() {
        let source = io::Cursor::new("3\rhel\r\nb\r\nlo world!!!\r\n0\r\n".to_string().into_bytes());
        let mut decoded = Decoder::new(source);

        let mut string = String::new();
        decoded.read_to_string(&mut string).unwrap();
    }
}
<commit_msg>Remove unused field on Decoder<commit_after>// Copyright 2015 The tiny-http Contributors
// Copyright 2015 The rust-chunked-transfer-coding Contributors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//	http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use std::io::Result as IoResult;
use std::io::Read;
use std::io::Error as IoError;
use std::io::ErrorKind;
use std::fmt;
use std::error::Error;

/// Reads HTTP chunks and sends back real data.
///
/// # Example
///
/// ```
/// use chunked_transfer::Decoder;
/// use std::io::Read;
///
/// let encoded = b"3\r\nhel\r\nb\r\nlo world!!!\r\n0\r\n\r\n";
/// let mut decoded = String::new();
///
/// let mut decoder = Decoder::new(encoded as &[u8]);
/// decoder.read_to_string(&mut decoded);
///
/// assert_eq!(decoded, "hello world!!!");
/// ```
pub struct Decoder<R> {
    // where the chunks come from
    source: R,

    // remaining size of the chunk being read
    // none if we are not in a chunk
    remaining_chunks_size: Option<usize>,
}

impl<R> Decoder<R> where R: Read {
    pub fn new(source: R) -> Decoder<R> {
        Decoder {
            source: source,
            remaining_chunks_size: None,
        }
    }
}

impl<R> Read for Decoder<R> where R: Read {
    fn read(&mut self, buf: &mut [u8]) -> IoResult<usize> {
        // first possibility: we are not in a chunk
        if self.remaining_chunks_size.is_none() {
            // trying the read the chunk size
            let mut chunk_size = Vec::new();

            loop {
                let byte = match self.source.by_ref().bytes().next() {
                    Some(b) => try!(b),
                    None => return Err(IoError::new(ErrorKind::InvalidInput, DecoderError)),
                };

                if byte == b'\r' {
                    break;
                }

                chunk_size.push(byte);
            }

            match self.source.by_ref().bytes().next() {
                Some(Ok(b'\n')) => (),
                _ => return Err(IoError::new(ErrorKind::InvalidInput, DecoderError)),
            }

            let chunk_size = match String::from_utf8(chunk_size) {
                Ok(c) => c,
                Err(_) => return Err(IoError::new(ErrorKind::InvalidInput, DecoderError))
            };

            let chunk_size = match usize::from_str_radix(&chunk_size, 16) {
                Ok(c) => c,
                Err(_) => return Err(IoError::new(ErrorKind::InvalidInput, DecoderError))
            };

            // if the chunk size is 0, we are at EOF
            if chunk_size == 0 {
                if try!(self.source.by_ref().bytes().next().unwrap_or(Ok(0))) != b'\r' {
                    return Err(IoError::new(ErrorKind::InvalidInput, DecoderError));
                }
                if try!(self.source.by_ref().bytes().next().unwrap_or(Ok(0))) != b'\n' {
                    return Err(IoError::new(ErrorKind::InvalidInput, DecoderError));
                }
                return Ok(0);
            }

            // now that we now the current chunk size, calling ourselves recursively
            self.remaining_chunks_size = Some(chunk_size);
            return self.read(buf);
        }

        assert!(self.remaining_chunks_size.is_some());

        // second possibility: we continue reading from a chunk
        if buf.len() < *self.remaining_chunks_size.as_ref().unwrap() {
            let read = try!(self.source.read(buf));
            *self.remaining_chunks_size.as_mut().unwrap() -= read;
            return Ok(read);
        }

        // third possibility: the read request goes further than the current chunk
        // we simply read until the end of the chunk and return
        assert!(buf.len() >= *self.remaining_chunks_size.as_ref().unwrap());

        let remaining_chunks_size = *self.remaining_chunks_size.as_ref().unwrap();

        let buf = &mut buf[.. remaining_chunks_size];
        let read = try!(self.source.read(buf));
        *self.remaining_chunks_size.as_mut().unwrap() -= read;

        if read == remaining_chunks_size {
            self.remaining_chunks_size = None;

            if try!(self.source.by_ref().bytes().next().unwrap_or(Ok(0))) != b'\r' {
                return Err(IoError::new(ErrorKind::InvalidInput, DecoderError));
            }
            if try!(self.source.by_ref().bytes().next().unwrap_or(Ok(0))) != b'\n' {
                return Err(IoError::new(ErrorKind::InvalidInput, DecoderError));
            }
        }

        return Ok(read);
    }
}

#[derive(Debug, Copy, Clone)]
pub struct DecoderError;

impl fmt::Display for DecoderError {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        write!(fmt, "Error while decoding chunks")
    }
}

impl Error for DecoderError {
    fn description(&self) -> &str {
        "Error while decoding chunks"
    }
}

#[cfg(test)]
mod test {
    use super::Decoder;
    use std::io;
    use std::io::Read;

    #[test]
    fn test_valid_chunk_decode() {
        let source = io::Cursor::new("3\r\nhel\r\nb\r\nlo world!!!\r\n0\r\n\r\n".to_string().into_bytes());
        let mut decoded = Decoder::new(source);

        let mut string = String::new();
        decoded.read_to_string(&mut string).unwrap();

        assert_eq!(string, "hello world!!!");
    }

    #[test]
    #[should_panic]
    fn invalid_input1() {
        let source = io::Cursor::new("2\r\nhel\r\nb\r\nlo world!!!\r\n0\r\n".to_string().into_bytes());
        let mut decoded = Decoder::new(source);

        let mut string = String::new();
        decoded.read_to_string(&mut string).unwrap();
    }

    #[test]
    #[should_panic]
    fn invalid_input2() {
        let source = io::Cursor::new("3\rhel\r\nb\r\nlo world!!!\r\n0\r\n".to_string().into_bytes());
        let mut decoded = Decoder::new(source);

        let mut string = String::new();
        decoded.read_to_string(&mut string).unwrap();
    }
}
<|endoftext|>


<commit_before>use std::char;
use std::cmp::{max, min};
use std::collections::VecDeque;
use num::{Complex, Float};
use rustty;
use rustty::{Attr, Color, Terminal, Cell, CellAccessor, HasSize};
use rustty::ui::{Alignable, Widget, VerticalAlign};
use itertools::{Itertools, EitherOrBoth};

pub struct Canvas {
    term: Terminal,
    spectrum: Widget,
    waterfall: Widget,
    history: VecDeque<Vec<f32>>,
}

impl Canvas {
    pub fn new() -> Result<Self, rustty::Error> {
        let term = try!(Terminal::new());
        let (cols, rows) = term.size();

        let spectrum_height = rows / 2;
        let waterfall_height = if rows % 2 != 0 { rows / 2 } else { rows / 2 + 1 };

        let mut spectrum = Widget::new(cols, spectrum_height);
        spectrum.valign(&term, VerticalAlign::Top, 0);

        let mut waterfall = Widget::new(cols, waterfall_height);
        waterfall.valign(&term, VerticalAlign::Bottom, 0);

        Ok(Canvas {
            term: term,
            spectrum: spectrum,
            waterfall: waterfall,
            history: VecDeque::with_capacity(waterfall_height * 2),
        })
    }

    /// Adds a spectrum to the history and draws it on the waterfall
    /// and the spectrum view.
    pub fn add_spectrum(&mut self, spec: Vec<Complex<f32>>) {
        draw_spectrum(&mut self.spectrum, &spec);

        let normalized = normalize_spectrum(&spec, 50.0);
        let averaged = normalized.chunks(2).map(|v| (v[0] + v[1]) / 2.0).collect();
        self.history.push_front(averaged);

        draw_waterfall(&mut self.waterfall, &self.history);

        self.spectrum.draw_into(&mut self.term);
        self.waterfall.draw_into(&mut self.term);
        self.term.swap_buffers().unwrap();
    }

    pub fn get_term(&mut self) -> &mut Terminal {
        &mut self.term
    }
}

fn draw_waterfall<T: CellAccessor + HasSize>(canvas: &mut T, spectra: &VecDeque<Vec<f32>>) {
    let (cols, rows) = canvas.size();
    for (row, mut specs) in (0..rows).zip(&spectra.iter().chunks_lazy(2)) {
        if let Some(upper_heights) = specs.next().map(|vec| vec.iter()) {
            match specs.next().map(|vec| vec.iter()) {
                Some(lower_heights) => {
                    for (c, heights) in (0..cols).zip(upper_heights.zip_longest(lower_heights)) {
                        let (u, l) = match heights {
                            EitherOrBoth::Both(&upper, &lower) => (upper, lower),
                            EitherOrBoth::Left(&upper) => (upper, 0.0),
                            EitherOrBoth::Right(&lower) => (0.0, lower),
                        };
                        *canvas.get_mut(c, row).unwrap() = spectrum_heights_to_waterfall_cell(u, l);
                    }
                },
                None => {
                    for (c, u) in (0..cols).zip(upper_heights) {
                        *canvas.get_mut(c, row).unwrap() = spectrum_heights_to_waterfall_cell(*u, 0.0);
                    }
                }
            }
        }
    }
}

fn spectrum_heights_to_waterfall_cell(upper: f32, lower: f32) -> Cell {
    Cell::new('▀',
              Color::Byte(color_mapping(upper)),
              Color::Byte(color_mapping(lower)),
              Attr::Default)
}

/// Assumes `f` is between 0 and 1. Anything outside of this range
/// will be clamped.
fn color_mapping(f: f32) -> u8 {
    let mapping = [16, 17, 18, 19, 21, 27, 33, 39, 45, 51,
                   50, 49, 48, 47, 46, 82, 118, 154, 190, 226];
    let idx = (f * (mapping.len() as f32)) as i32;
    if idx < 0 {
        mapping[0]
    } else if idx >= mapping.len() as i32 {
        mapping[mapping.len() - 1]
    } else {
        mapping[idx as usize]
    }
    //let lower = 232.0;
    //let upper = 255.0;
    //let mapped = f * (upper - lower) + lower;
    //if mapped < lower {
        //lower as u8
    //} else if mapped > upper {
        //upper as u8
    //} else {
        //mapped as u8
    //}
}

fn normalize_spectrum(spec: &[Complex<f32>], max_db: f32) -> Vec<f32> {
    // FFT shift
    let (first_half, last_half) = spec.split_at((spec.len() + 1) / 2);
    let shifted_spec = last_half.iter().chain(first_half.iter());

    // normalize and take the log
    shifted_spec.map(Complex::norm)
                .map(Float::log10)
                .map(|x| 10.0 * x)
                .map(|x| x / max_db)
                .collect()
}

// indexing is from the top of the cell
fn pixel_nums_to_braille(p1: Option<u8>, p2: Option<u8>) -> char {
    let pixel_map = [[0x01, 0x08],
                     [0x02, 0x10],
                     [0x04, 0x20],
                     [0x40, 0x80]];

    let mut c = 0;
    if let Some(p) = p1 {
        for i in p..4 {
            c |= pixel_map[i as usize][0];
        }
    }

    if let Some(p) = p2 {
        for i in p..4 {
            c |= pixel_map[i as usize][1];
        }
    }

    char::from_u32((0x2800 + c) as u32).unwrap()
}

fn fft_shift<T: Clone>(spec: &mut [T]) {
    let spec_copy = spec.to_owned();

    let (first_half, last_half) = spec_copy.split_at((spec_copy.len() + 1) / 2);
    let shifted_spec = last_half.iter().chain(first_half.iter());
    for (x, y) in spec.iter_mut().zip(shifted_spec) {
        *x = y.clone();
    }
}

fn spectrum_to_bin_heights(spec: &[Complex<f32>], dest: &mut [f32]) {
    //TODO should be plotting in log scale

    // subsample
    let mut last_idx = -1isize;
    for (i, x) in spec.iter().map(Complex::norm).enumerate() {
        if (i * dest.len() / spec.len()) as isize > last_idx {
            last_idx += 1;
            dest[last_idx as usize] = x;
        }
    }

    //TODO unnecessary allocation
    fft_shift(dest);
}

fn char_to_cell(c: char) -> Cell {
    let mut cell = Cell::with_char(c);
    cell.set_attrs(Attr::Bold);
    cell
}

fn draw_pixel_pair<T>(canvas: &mut T, col_idx: usize, p1: usize, p2: usize)
    where T: CellAccessor + HasSize
{
    let (_, rows) = canvas.size();
    let max_pixel_height = 4 * rows;

    // clamp heights
    let p1 = if p1 >= max_pixel_height { max_pixel_height - 1} else { p1 };
    let p2 = if p2 >= max_pixel_height { max_pixel_height - 1} else { p2 };

    // Reverse it, since the terminal indexing is from the top
    let p1 = max_pixel_height - p1 - 1;
    let p2 = max_pixel_height - p2 - 1;

    // cell indices
    let c1 = p1 / 4;
    let c2 = p2 / 4;

    // Fill in full height cells.
    let full_cell_char = pixel_nums_to_braille(Some(0), Some(0));
    for row_idx in max(c1, c2)..rows {
        *canvas.get_mut(col_idx, row_idx).unwrap() = char_to_cell(full_cell_char);
    }

    let left_fill_cell_char = pixel_nums_to_braille(Some(0), None);
    for row_idx in min(c1, c2)..c2 {
        *canvas.get_mut(col_idx, row_idx).unwrap() = char_to_cell(left_fill_cell_char);
    }

    let right_fill_cell_char = pixel_nums_to_braille(None, Some(0));
    for row_idx in min(c1, c2)..c1 {
        *canvas.get_mut(col_idx, row_idx).unwrap() = char_to_cell(right_fill_cell_char);
    }

    // Now fill in partial height cells.
    if c1 == c2 {
        // top pixels are in the same cell
        *canvas.get_mut(col_idx, c1).unwrap() = char_to_cell(
            pixel_nums_to_braille(Some((p1 % 4) as u8), Some((p2 % 4) as u8)));
    } else if c1 > c2 {
        // right pixel is in a higher cell.
        *canvas.get_mut(col_idx, c1).unwrap() = char_to_cell(
            pixel_nums_to_braille(Some((p1 % 4) as u8), Some(0)));
        *canvas.get_mut(col_idx, c2).unwrap() = char_to_cell(
            pixel_nums_to_braille(None, Some((p2 % 4) as u8)));
    } else {
        // left pixel is in a higher cell.
        *canvas.get_mut(col_idx, c1).unwrap() = char_to_cell(
            pixel_nums_to_braille(Some((p1 % 4) as u8), None));
        *canvas.get_mut(col_idx, c2).unwrap() = char_to_cell(
            pixel_nums_to_braille(Some(0), Some((p2 % 4) as u8)));
    }
}

fn draw_spectrum<T: CellAccessor + HasSize>(canvas: &mut T, spec: &[Complex<f32>]) {
    canvas.clear(Cell::default());
    let (num_cols, num_rows) = canvas.size();
    let pixel_height = num_rows * 4;
    let pixel_width = num_cols * 2;
    // TODO what should this value be?
    let max_height = 500.0;

    let mut bins = vec![0.0; pixel_width];
    spectrum_to_bin_heights(spec, &mut bins[..]);

    for col_idx in 0..num_cols {
        // height in float between 0 and 1.
        let h1 = bins[col_idx * 2] / max_height;
        let h2 = bins[col_idx * 2 + 1] / max_height;

        // The "pixel" height of each point.
        let p1 = (h1 * pixel_height as f32).floor() as usize;
        let p2 = (h2 * pixel_height as f32).floor() as usize;

        draw_pixel_pair(canvas, col_idx, p1, p2);
    }
}

#[cfg(test)]
mod tests {
    use super::{pixel_nums_to_braille, fft_shift, draw_pixel_pair};
    use rustty::Terminal;

    #[test]
    fn test_fft_shift_dc() {
        let len = 9;
        let mut spec = vec![0; len];
        spec[0] = 1;
        fft_shift(&mut spec[..]);
        assert_eq!(spec[len / 2], 1);
    }

    #[test]
    fn test_fft_shift_even() {
        let mut before: Vec<usize> = (0..10).collect();
        let after = vec![5, 6, 7, 8, 9, 0, 1, 2, 3, 4];
        fft_shift(&mut before[..]);
        assert_eq!(before, after);
    }

    #[test]
    fn test_pixel_nums() {
        assert_eq!(pixel_nums_to_braille(Some(0), Some(0)), '⣿');
        assert_eq!(pixel_nums_to_braille(Some(1), Some(2)), '⣦');
        assert_eq!(pixel_nums_to_braille(None, Some(3)), '⢀');
        assert_eq!(pixel_nums_to_braille(Some(2), None), '⡄');
        assert_eq!(pixel_nums_to_braille(None, None), '⠀');
    }

    #[test]
    fn test_draw_pixel_pair() {
        let mut term = Terminal::new().unwrap();

        // Test drawing with the same top cell
        draw_pixel_pair(&mut term, 0, 4, 6);
        assert_eq!(term[(0, term.rows() - 3)].ch(), ' ');
        assert_eq!(term[(0, term.rows() - 2)].ch(), '⣰');
        assert_eq!(term[(0, term.rows() - 1)].ch(), '⣿');
        term.clear().unwrap();

        // Test drawing with the top pixel in each column being in
        // different cells
        draw_pixel_pair(&mut term, 0, 4, 8);
        assert_eq!(term[(0, term.rows() - 4)].ch(), ' ');
        assert_eq!(term[(0, term.rows() - 3)].ch(), '⢀');
        assert_eq!(term[(0, term.rows() - 2)].ch(), '⣸');
        assert_eq!(term[(0, term.rows() - 1)].ch(), '⣿');
        term.clear().unwrap();

        draw_pixel_pair(&mut term, 1, 13, 2);
        assert_eq!(term[(1, term.rows() - 5)].ch(), ' ');
        assert_eq!(term[(1, term.rows() - 4)].ch(), '⡄');
        assert_eq!(term[(1, term.rows() - 3)].ch(), '⡇');
        assert_eq!(term[(1, term.rows() - 2)].ch(), '⡇');
        assert_eq!(term[(1, term.rows() - 1)].ch(), '⣷');
        term.clear().unwrap();
    }
}
<commit_msg>clean up draw_waterfall by using flat_map.<commit_after>use std::char;
use std::cmp::{max, min};
use std::collections::VecDeque;
use num::{Complex, Float};
use rustty;
use rustty::{Attr, Color, Terminal, Cell, CellAccessor, HasSize};
use rustty::ui::{Alignable, Widget, VerticalAlign};
use itertools::{Itertools, EitherOrBoth};

pub struct Canvas {
    term: Terminal,
    spectrum: Widget,
    waterfall: Widget,
    history: VecDeque<Vec<f32>>,
}

impl Canvas {
    pub fn new() -> Result<Self, rustty::Error> {
        let term = try!(Terminal::new());
        let (cols, rows) = term.size();

        let spectrum_height = rows / 2;
        let waterfall_height = if rows % 2 != 0 { rows / 2 } else { rows / 2 + 1 };

        let mut spectrum = Widget::new(cols, spectrum_height);
        spectrum.valign(&term, VerticalAlign::Top, 0);

        let mut waterfall = Widget::new(cols, waterfall_height);
        waterfall.valign(&term, VerticalAlign::Bottom, 0);

        Ok(Canvas {
            term: term,
            spectrum: spectrum,
            waterfall: waterfall,
            history: VecDeque::with_capacity(waterfall_height * 2),
        })
    }

    /// Adds a spectrum to the history and draws it on the waterfall
    /// and the spectrum view.
    pub fn add_spectrum(&mut self, spec: Vec<Complex<f32>>) {
        draw_spectrum(&mut self.spectrum, &spec);

        let normalized = normalize_spectrum(&spec, 50.0);
        let averaged = normalized.chunks(2).map(|v| (v[0] + v[1]) / 2.0).collect();
        self.history.push_front(averaged);

        draw_waterfall(&mut self.waterfall, &self.history);

        self.spectrum.draw_into(&mut self.term);
        self.waterfall.draw_into(&mut self.term);
        self.term.swap_buffers().unwrap();
    }

    pub fn get_term(&mut self) -> &mut Terminal {
        &mut self.term
    }
}

fn draw_waterfall<T: CellAccessor + HasSize>(canvas: &mut T, spectra: &VecDeque<Vec<f32>>) {
    let (cols, rows) = canvas.size();
    for (row, mut specs) in (0..rows).zip(&spectra.iter().chunks_lazy(2)) {
        let upper_heights = specs.next().into_iter().flat_map(|x| x);
        let lower_heights = specs.next().into_iter().flat_map(|x| x);
        for (c, heights) in (0..cols).zip(upper_heights.zip_longest(lower_heights)) {
            let (u, l) = match heights {
                EitherOrBoth::Both(&upper, &lower) => (upper, lower),
                EitherOrBoth::Left(&upper) => (upper, 0.0),
                EitherOrBoth::Right(&lower) => (0.0, lower),
            };
            *canvas.get_mut(c, row).unwrap() = spectrum_heights_to_waterfall_cell(u, l);
        }
    }
}

fn spectrum_heights_to_waterfall_cell(upper: f32, lower: f32) -> Cell {
    Cell::new('▀',
              Color::Byte(color_mapping(upper)),
              Color::Byte(color_mapping(lower)),
              Attr::Default)
}

/// Assumes `f` is between 0 and 1. Anything outside of this range
/// will be clamped.
fn color_mapping(f: f32) -> u8 {
    let mapping = [16, 17, 18, 19, 21, 27, 33, 39, 45, 51,
                   50, 49, 48, 47, 46, 82, 118, 154, 190, 226];
    let idx = (f * (mapping.len() as f32)) as i32;
    if idx < 0 {
        mapping[0]
    } else if idx >= mapping.len() as i32 {
        mapping[mapping.len() - 1]
    } else {
        mapping[idx as usize]
    }
    //let lower = 232.0;
    //let upper = 255.0;
    //let mapped = f * (upper - lower) + lower;
    //if mapped < lower {
        //lower as u8
    //} else if mapped > upper {
        //upper as u8
    //} else {
        //mapped as u8
    //}
}

fn normalize_spectrum(spec: &[Complex<f32>], max_db: f32) -> Vec<f32> {
    // FFT shift
    let (first_half, last_half) = spec.split_at((spec.len() + 1) / 2);
    let shifted_spec = last_half.iter().chain(first_half.iter());

    // normalize and take the log
    shifted_spec.map(Complex::norm)
                .map(Float::log10)
                .map(|x| 10.0 * x)
                .map(|x| x / max_db)
                .collect()
}

// indexing is from the top of the cell
fn pixel_nums_to_braille(p1: Option<u8>, p2: Option<u8>) -> char {
    let pixel_map = [[0x01, 0x08],
                     [0x02, 0x10],
                     [0x04, 0x20],
                     [0x40, 0x80]];

    let mut c = 0;
    if let Some(p) = p1 {
        for i in p..4 {
            c |= pixel_map[i as usize][0];
        }
    }

    if let Some(p) = p2 {
        for i in p..4 {
            c |= pixel_map[i as usize][1];
        }
    }

    char::from_u32((0x2800 + c) as u32).unwrap()
}

fn fft_shift<T: Clone>(spec: &mut [T]) {
    let spec_copy = spec.to_owned();

    let (first_half, last_half) = spec_copy.split_at((spec_copy.len() + 1) / 2);
    let shifted_spec = last_half.iter().chain(first_half.iter());
    for (x, y) in spec.iter_mut().zip(shifted_spec) {
        *x = y.clone();
    }
}

fn spectrum_to_bin_heights(spec: &[Complex<f32>], dest: &mut [f32]) {
    //TODO should be plotting in log scale

    // subsample
    let mut last_idx = -1isize;
    for (i, x) in spec.iter().map(Complex::norm).enumerate() {
        if (i * dest.len() / spec.len()) as isize > last_idx {
            last_idx += 1;
            dest[last_idx as usize] = x;
        }
    }

    //TODO unnecessary allocation
    fft_shift(dest);
}

fn char_to_cell(c: char) -> Cell {
    let mut cell = Cell::with_char(c);
    cell.set_attrs(Attr::Bold);
    cell
}

fn draw_pixel_pair<T>(canvas: &mut T, col_idx: usize, p1: usize, p2: usize)
    where T: CellAccessor + HasSize
{
    let (_, rows) = canvas.size();
    let max_pixel_height = 4 * rows;

    // clamp heights
    let p1 = if p1 >= max_pixel_height { max_pixel_height - 1} else { p1 };
    let p2 = if p2 >= max_pixel_height { max_pixel_height - 1} else { p2 };

    // Reverse it, since the terminal indexing is from the top
    let p1 = max_pixel_height - p1 - 1;
    let p2 = max_pixel_height - p2 - 1;

    // cell indices
    let c1 = p1 / 4;
    let c2 = p2 / 4;

    // Fill in full height cells.
    let full_cell_char = pixel_nums_to_braille(Some(0), Some(0));
    for row_idx in max(c1, c2)..rows {
        *canvas.get_mut(col_idx, row_idx).unwrap() = char_to_cell(full_cell_char);
    }

    let left_fill_cell_char = pixel_nums_to_braille(Some(0), None);
    for row_idx in min(c1, c2)..c2 {
        *canvas.get_mut(col_idx, row_idx).unwrap() = char_to_cell(left_fill_cell_char);
    }

    let right_fill_cell_char = pixel_nums_to_braille(None, Some(0));
    for row_idx in min(c1, c2)..c1 {
        *canvas.get_mut(col_idx, row_idx).unwrap() = char_to_cell(right_fill_cell_char);
    }

    // Now fill in partial height cells.
    if c1 == c2 {
        // top pixels are in the same cell
        *canvas.get_mut(col_idx, c1).unwrap() = char_to_cell(
            pixel_nums_to_braille(Some((p1 % 4) as u8), Some((p2 % 4) as u8)));
    } else if c1 > c2 {
        // right pixel is in a higher cell.
        *canvas.get_mut(col_idx, c1).unwrap() = char_to_cell(
            pixel_nums_to_braille(Some((p1 % 4) as u8), Some(0)));
        *canvas.get_mut(col_idx, c2).unwrap() = char_to_cell(
            pixel_nums_to_braille(None, Some((p2 % 4) as u8)));
    } else {
        // left pixel is in a higher cell.
        *canvas.get_mut(col_idx, c1).unwrap() = char_to_cell(
            pixel_nums_to_braille(Some((p1 % 4) as u8), None));
        *canvas.get_mut(col_idx, c2).unwrap() = char_to_cell(
            pixel_nums_to_braille(Some(0), Some((p2 % 4) as u8)));
    }
}

fn draw_spectrum<T: CellAccessor + HasSize>(canvas: &mut T, spec: &[Complex<f32>]) {
    canvas.clear(Cell::default());
    let (num_cols, num_rows) = canvas.size();
    let pixel_height = num_rows * 4;
    let pixel_width = num_cols * 2;
    // TODO what should this value be?
    let max_height = 500.0;

    let mut bins = vec![0.0; pixel_width];
    spectrum_to_bin_heights(spec, &mut bins[..]);

    for col_idx in 0..num_cols {
        // height in float between 0 and 1.
        let h1 = bins[col_idx * 2] / max_height;
        let h2 = bins[col_idx * 2 + 1] / max_height;

        // The "pixel" height of each point.
        let p1 = (h1 * pixel_height as f32).floor() as usize;
        let p2 = (h2 * pixel_height as f32).floor() as usize;

        draw_pixel_pair(canvas, col_idx, p1, p2);
    }
}

#[cfg(test)]
mod tests {
    use super::{pixel_nums_to_braille, fft_shift, draw_pixel_pair};
    use rustty::Terminal;

    #[test]
    fn test_fft_shift_dc() {
        let len = 9;
        let mut spec = vec![0; len];
        spec[0] = 1;
        fft_shift(&mut spec[..]);
        assert_eq!(spec[len / 2], 1);
    }

    #[test]
    fn test_fft_shift_even() {
        let mut before: Vec<usize> = (0..10).collect();
        let after = vec![5, 6, 7, 8, 9, 0, 1, 2, 3, 4];
        fft_shift(&mut before[..]);
        assert_eq!(before, after);
    }

    #[test]
    fn test_pixel_nums() {
        assert_eq!(pixel_nums_to_braille(Some(0), Some(0)), '⣿');
        assert_eq!(pixel_nums_to_braille(Some(1), Some(2)), '⣦');
        assert_eq!(pixel_nums_to_braille(None, Some(3)), '⢀');
        assert_eq!(pixel_nums_to_braille(Some(2), None), '⡄');
        assert_eq!(pixel_nums_to_braille(None, None), '⠀');
    }

    #[test]
    fn test_draw_pixel_pair() {
        let mut term = Terminal::new().unwrap();

        // Test drawing with the same top cell
        draw_pixel_pair(&mut term, 0, 4, 6);
        assert_eq!(term[(0, term.rows() - 3)].ch(), ' ');
        assert_eq!(term[(0, term.rows() - 2)].ch(), '⣰');
        assert_eq!(term[(0, term.rows() - 1)].ch(), '⣿');
        term.clear().unwrap();

        // Test drawing with the top pixel in each column being in
        // different cells
        draw_pixel_pair(&mut term, 0, 4, 8);
        assert_eq!(term[(0, term.rows() - 4)].ch(), ' ');
        assert_eq!(term[(0, term.rows() - 3)].ch(), '⢀');
        assert_eq!(term[(0, term.rows() - 2)].ch(), '⣸');
        assert_eq!(term[(0, term.rows() - 1)].ch(), '⣿');
        term.clear().unwrap();

        draw_pixel_pair(&mut term, 1, 13, 2);
        assert_eq!(term[(1, term.rows() - 5)].ch(), ' ');
        assert_eq!(term[(1, term.rows() - 4)].ch(), '⡄');
        assert_eq!(term[(1, term.rows() - 3)].ch(), '⡇');
        assert_eq!(term[(1, term.rows() - 2)].ch(), '⡇');
        assert_eq!(term[(1, term.rows() - 1)].ch(), '⣷');
        term.clear().unwrap();
    }
}
<|endoftext|>


<commit_before>// Copyright 2013-2014 Jeffery Olson
//
// Licensed under the 3-Clause BSD License, see LICENSE.txt
// at the top-level of this repository.
// This file may not be copied, modified, or distributed
// except according to those terms.

use std::vec::Vec;
use std::collections::HashMap;
use sdl2::sdl;
use sdl2::render::Renderer;
use sdl2::video::Window;
use p2d::sprite::SpriteSheet;
use sdl2;
use sdl2_image;

pub mod draw;
pub mod texture;

pub struct GameDisplay {
    pub renderer: Box<Renderer<Window>>,
    pub sheets: texture::TextureSheets
}

impl GameDisplay {
    pub fn new(title: &str, screen_size: (int, int, bool), ss: Vec<SpriteSheet>) -> GameDisplay {
        // first thing we do
        sdl::init(sdl2::InitVideo);
        // and sdl2_image
        sdl2_image::init(sdl2_image::InitPng);

        let (width, height, fullscreen) = screen_size;
        let window = sdl2::video::Window::new(
            title, sdl2::video::PosCentered, sdl2::video::PosCentered,
            width, height, sdl2::video::OpenGL);
        let window = match window {
            Ok(window) => window,
            Err(err) => fail!(format!("failed to create window: {}", err))
        };
        if fullscreen {
            window.set_fullscreen(sdl2::video::FTTrue);
        }
        let renderer = Renderer::from_window(
            window, sdl2::render::DriverAuto, sdl2::render::Accelerated);
        let renderer = match renderer {
            Ok(renderer) => renderer,
            Err(err) => fail!(format!("failed to create renderer: {}", err))
        };
        let mut display = GameDisplay {
            renderer: box renderer,
            sheets: HashMap::new()
        };
        // build TextureSheets
        for s in ss.iter() {
            display.sheets.find_or_insert(s.name.clone(), texture::TextureSheet::new(
                &*display.renderer,
                &s.path,
                s.name.clone()));
        }
        display
    }

    pub fn set_draw_color(&self, rgb: (u8, u8, u8)) {
        let (r, g, b) = rgb;
        match self.renderer.set_draw_color(sdl2::pixels::RGB(r, g, b)) {
            Ok(()) => {},
            Err(e) => fail!("set_draw_color(): failure: {}", e)
        }
    }
    pub fn set_draw_sdl2_color(&self, rgb: sdl2::pixels::Color) {
        match self.renderer.set_draw_color(rgb) {
            Ok(()) => {},
            Err(e) => fail!("set_draw_sdl2_color(): failure: {}", e)
        }
    }
}

impl Drop for GameDisplay {
    fn drop(&mut self) {
        // last thing we do
        sdl2_image::quit();
        sdl::quit();
    }
}
<commit_msg>compatibility updates for rust_sdl2<commit_after>// Copyright 2013-2014 Jeffery Olson
//
// Licensed under the 3-Clause BSD License, see LICENSE.txt
// at the top-level of this repository.
// This file may not be copied, modified, or distributed
// except according to those terms.

use std::vec::Vec;
use std::collections::HashMap;
use sdl2::sdl;
use sdl2::render::Renderer;
use sdl2::video::Window;
use p2d::sprite::SpriteSheet;
use sdl2;
use sdl2_image;

pub mod draw;
pub mod texture;

pub struct GameDisplay {
    pub renderer: Box<Renderer<Window>>,
    pub sheets: texture::TextureSheets
}

impl GameDisplay {
    pub fn new(title: &str, screen_size: (int, int, bool), ss: Vec<SpriteSheet>) -> GameDisplay {
        // first thing we do
        sdl::init(sdl2::INIT_VIDEO);
        // and sdl2_image
        sdl2_image::init(sdl2_image::INIT_PNG);

        let (width, height, fullscreen) = screen_size;
        let window = sdl2::video::Window::new(
            title, sdl2::video::PosCentered, sdl2::video::PosCentered,
            width, height, sdl2::video::OPENGL);
        let window = match window {
            Ok(window) => window,
            Err(err) => fail!(format!("failed to create window: {}", err))
        };
        if fullscreen {
            window.set_fullscreen(sdl2::video::FTTrue);
        }
        let renderer = Renderer::from_window(
            window, sdl2::render::DriverAuto, sdl2::render::ACCELERATED);
        let renderer = match renderer {
            Ok(renderer) => renderer,
            Err(err) => fail!(format!("failed to create renderer: {}", err))
        };
        let mut display = GameDisplay {
            renderer: box renderer,
            sheets: HashMap::new()
        };
        // build TextureSheets
        for s in ss.iter() {
            display.sheets.find_or_insert(s.name.clone(), texture::TextureSheet::new(
                &*display.renderer,
                &s.path,
                s.name.clone()));
        }
        display
    }

    pub fn set_draw_color(&self, rgb: (u8, u8, u8)) {
        let (r, g, b) = rgb;
        match self.renderer.set_draw_color(sdl2::pixels::RGB(r, g, b)) {
            Ok(()) => {},
            Err(e) => fail!("set_draw_color(): failure: {}", e)
        }
    }
    pub fn set_draw_sdl2_color(&self, rgb: sdl2::pixels::Color) {
        match self.renderer.set_draw_color(rgb) {
            Ok(()) => {},
            Err(e) => fail!("set_draw_sdl2_color(): failure: {}", e)
        }
    }
}

impl Drop for GameDisplay {
    fn drop(&mut self) {
        // last thing we do
        sdl2_image::quit();
        sdl::quit();
    }
}
<|endoftext|>


<commit_before>use crate::{run_ping, Parser, PingResult, Pinger};
use anyhow::Context;
use regex::Regex;
use std::time::Duration;
use thiserror::Error;

#[derive(Debug, Eq, PartialEq)]
pub enum LinuxPingType {
    BusyBox,
    IPTools,
}

#[derive(Error, Debug)]
pub enum PingDetectionError {
    #[error("Could not detect ping. Stderr: {stderr:?}\nStdout: {stdout:?}")]
    UnknownPing {
        stderr: Vec<String>,
        stdout: Vec<String>,
    },
    #[error(transparent)]
    CommandError(#[from] anyhow::Error),
}

pub fn detect_linux_ping() -> Result<LinuxPingType, PingDetectionError> {
    // Err(PingDetectionError::Thing)
    let child = run_ping(vec!["-V".to_string()], true);
    let output = child
        .wait_with_output()
        .context("Error getting ping stdout/stderr")?;
    let stdout = String::from_utf8(output.stdout).context("Error decoding ping stdout")?;
    let stderr = String::from_utf8(output.stderr).context("Error decoding ping stderr")?;

    if stderr.contains("BusyBox") {
        Ok(LinuxPingType::BusyBox)
    } else if stdout.contains("iputils") {
        Ok(LinuxPingType::IPTools)
    } else {
        let first_two_lines_stderr: Vec<String> =
            stderr.lines().take(2).map(str::to_string).collect();
        let first_two_lines_stout: Vec<String> =
            stdout.lines().take(2).map(str::to_string).collect();
        Err(PingDetectionError::UnknownPing {
            stdout: first_two_lines_stout,
            stderr: first_two_lines_stderr,
        })
        // Err(anyhow!(format!("Cannot detect ping type. ")))
    }
}

#[derive(Default)]
pub struct LinuxPinger {
    interval: Duration,
}

impl Pinger for LinuxPinger {
    fn set_interval(&mut self, interval: Duration) {
        self.interval = interval;
    }

    fn ping_args(&self, target: String) -> Vec<String> {
        // The -O flag ensures we "no answer yet" messages from ping
        // See https://superuser.com/questions/270083/linux-ping-show-time-out
        vec![
            "-O".to_string(),
            format!("-i{:.1}", self.interval.as_millis() as f32 / 1_000_f32),
            target,
        ]
    }
}

#[derive(Default)]
pub struct AlpinePinger {
    interval: Duration,
}

// Alpine doesn't support timeout notifications, so we don't add the -O flag here
impl Pinger for AlpinePinger {
    fn set_interval(&mut self, interval: Duration) {
        self.interval = interval;
    }
}

lazy_static! {
    static ref UBUNTU_RE: Regex = Regex::new(r"(?i-u)time=(?P<time>\d+(?:\.\d+)?) *ms").unwrap();
}

#[derive(Default)]
pub struct LinuxParser {}

impl Parser for LinuxParser {
    fn parse(&self, line: String) -> Option<PingResult> {
        if line.starts_with("64 bytes from") {
            return self.extract_regex(&UBUNTU_RE, line);
        } else if line.starts_with("no answer yet") {
            return Some(PingResult::Timeout(line));
        }
        None
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use os_info::Type;

    #[test]
    #[cfg(unix)]
    fn test_linux_detection() {
        let ping_type = detect_linux_ping();
        match os_info::get().os_type() {
            Type::Alpine => {
                assert_eq!(
                    ping_type.expect("Error getting ping"),
                    LinuxPingType::BusyBox
                )
            }
            Type::Ubuntu => {
                assert_eq!(
                    ping_type.expect("Error getting ping"),
                    LinuxPingType::IPTools
                )
            }
            _ => {}
        }
    }
}
<commit_msg>Remove comment<commit_after>use crate::{run_ping, Parser, PingResult, Pinger};
use anyhow::Context;
use regex::Regex;
use std::time::Duration;
use thiserror::Error;

#[derive(Debug, Eq, PartialEq)]
pub enum LinuxPingType {
    BusyBox,
    IPTools,
}

#[derive(Error, Debug)]
pub enum PingDetectionError {
    #[error("Could not detect ping. Stderr: {stderr:?}\nStdout: {stdout:?}")]
    UnknownPing {
        stderr: Vec<String>,
        stdout: Vec<String>,
    },
    #[error(transparent)]
    CommandError(#[from] anyhow::Error),
}

pub fn detect_linux_ping() -> Result<LinuxPingType, PingDetectionError> {
    // Err(PingDetectionError::Thing)
    let child = run_ping(vec!["-V".to_string()], true);
    let output = child
        .wait_with_output()
        .context("Error getting ping stdout/stderr")?;
    let stdout = String::from_utf8(output.stdout).context("Error decoding ping stdout")?;
    let stderr = String::from_utf8(output.stderr).context("Error decoding ping stderr")?;

    if stderr.contains("BusyBox") {
        Ok(LinuxPingType::BusyBox)
    } else if stdout.contains("iputils") {
        Ok(LinuxPingType::IPTools)
    } else {
        let first_two_lines_stderr: Vec<String> =
            stderr.lines().take(2).map(str::to_string).collect();
        let first_two_lines_stout: Vec<String> =
            stdout.lines().take(2).map(str::to_string).collect();
        Err(PingDetectionError::UnknownPing {
            stdout: first_two_lines_stout,
            stderr: first_two_lines_stderr,
        })
    }
}

#[derive(Default)]
pub struct LinuxPinger {
    interval: Duration,
}

impl Pinger for LinuxPinger {
    fn set_interval(&mut self, interval: Duration) {
        self.interval = interval;
    }

    fn ping_args(&self, target: String) -> Vec<String> {
        // The -O flag ensures we "no answer yet" messages from ping
        // See https://superuser.com/questions/270083/linux-ping-show-time-out
        vec![
            "-O".to_string(),
            format!("-i{:.1}", self.interval.as_millis() as f32 / 1_000_f32),
            target,
        ]
    }
}

#[derive(Default)]
pub struct AlpinePinger {
    interval: Duration,
}

// Alpine doesn't support timeout notifications, so we don't add the -O flag here
impl Pinger for AlpinePinger {
    fn set_interval(&mut self, interval: Duration) {
        self.interval = interval;
    }
}

lazy_static! {
    static ref UBUNTU_RE: Regex = Regex::new(r"(?i-u)time=(?P<time>\d+(?:\.\d+)?) *ms").unwrap();
}

#[derive(Default)]
pub struct LinuxParser {}

impl Parser for LinuxParser {
    fn parse(&self, line: String) -> Option<PingResult> {
        if line.starts_with("64 bytes from") {
            return self.extract_regex(&UBUNTU_RE, line);
        } else if line.starts_with("no answer yet") {
            return Some(PingResult::Timeout(line));
        }
        None
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use os_info::Type;

    #[test]
    #[cfg(unix)]
    fn test_linux_detection() {
        let ping_type = detect_linux_ping();
        match os_info::get().os_type() {
            Type::Alpine => {
                assert_eq!(
                    ping_type.expect("Error getting ping"),
                    LinuxPingType::BusyBox
                )
            }
            Type::Ubuntu => {
                assert_eq!(
                    ping_type.expect("Error getting ping"),
                    LinuxPingType::IPTools
                )
            }
            _ => {}
        }
    }
}
<|endoftext|>


<commit_before>use std::path::{Path, PathBuf, MAIN_SEPARATOR};
use std::fs::File;
use std::collections::HashMap;
use std::io::prelude::*;
use std::env;
use toml;
use serde::Serializer;
use serde_json;
use std::result::Result as StdResult;
use semver::Version;

use errors::*;

#[derive(Serialize, Debug)]
pub struct Manifest {
    pub build: PathBuf,
    pub metadata: Metadata,
    pub src: Vec<Src>,
    pub dependencies: Vec<Dependency>,
    pub dev_dependencies: Vec<Dependency>,
    pub targets: Vec<Target>,
}

#[derive(Serialize, Debug)]
pub struct Metadata {
    pub name: String,
    #[serde(serialize_with = "version_to_str")]
    pub version: Version,
    pub authors: Vec<String>,
}

#[derive(Serialize, Debug)]
pub struct Src {
    pub name: String,
    pub path: String,
    pub optional: bool,
    pub fullpath: PathBuf,
}

#[derive(Serialize, Debug)]
pub struct Dependency {
    pub name: String,
    #[serde(serialize_with = "version_to_str")]
    pub version: Version,
    pub optional: bool,
}

#[derive(Serialize, Debug)]
pub struct Target {
    pub name: String,
    pub path: String,
    pub extension: String,
    pub fullpath: PathBuf,
    pub file: PathBuf,
}

impl Manifest {
    pub fn load<P: AsRef<Path>>(file: P) -> Result<Manifest> {
        let mut file = File::open(file).chain_err(|| "Failed to open manifest")?;
        let mut contents = String::new();
        file.read_to_string(&mut contents)
            .chain_err(|| "Failed to read manifest")?;

        let raw: RawManifest = toml::from_str(contents.as_str())
            .chain_err(|| "Failed to parse manifest")?;

        let cwd = env::current_dir()
            .chain_err(|| "Failed to load current directory")?;
        let build_dir = cwd.join("build");
        let package_name = raw.package.name;

        let mut src = vec![];
        for (name, value) in raw.src.unwrap_or(HashMap::new()) {
            match value {
                toml::Value::String(path) => {
                    let fullpath = cwd.join(normalize_path(&path));
                    src.push(Src {
                                 name,
                                 path,
                                 optional: false,
                                 fullpath,
                             });
                }
                toml::Value::Table(table) => {
                    let path =
                        value_to_string(table.get("path").ok_or("path is required for src")?)?;
                    let optional = table
                        .get("optional")
                        .unwrap_or(&toml::Value::Boolean(false))
                        .as_bool()
                        .ok_or("Failed to convert optional to boolean")?;
                    let fullpath = cwd.join(normalize_path(&path));

                    src.push(Src {
                                 name,
                                 path,
                                 optional,
                                 fullpath,
                             });
                }
                _ => bail!("Incompatible type for src"),
            }
        }

        let dependencies = vec![];
        let dev_dependencies = vec![];

        let mut targets = vec![];
        for target in raw.targets.unwrap_or(vec![]) {
            let name = target.name.unwrap_or(package_name.clone());
            let path = target.path;
            let fullpath = cwd.join(normalize_path(&path));
            let file = build_dir.join(format!("{}.{}", name, target.extension));

            targets.push(Target {
                             name,
                             path,
                             extension: target.extension,
                             fullpath,
                             file,
                         });
        }

        let parsed = Manifest {
            build: build_dir,
            metadata: Metadata {
                name: package_name,
                version: Version::parse(raw.package.version.as_str())
                    .chain_err(|| "Invalid package version")?,
                authors: raw.package.authors,
            },
            src,
            dependencies,
            dev_dependencies,
            targets,
        };
        Ok(parsed)
    }

    pub fn to_json(&self) -> Result<String> {
        let json = serde_json::to_string_pretty(&self)
            .chain_err(|| "Failed to convert manifest to JSON")?;

        Ok(json)
    }
}

// Deserialize is intentionally loose for user benefit
// toml -> RawManifest -> Manifest

#[derive(Deserialize, Debug)]
struct RawManifest {
    package: RawPackage,
    src: Option<HashMap<String, toml::Value>>,
    dependencies: Option<HashMap<String, toml::Value>>,

    #[serde(rename = "dev-dependencies")]
    dev_dependencies: Option<HashMap<String, toml::Value>>,
    targets: Option<Vec<RawTarget>>,
}

#[derive(Deserialize, Debug)]
struct RawPackage {
    name: String,
    version: String,
    authors: Vec<String>,
}

#[derive(Deserialize, Debug)]
struct RawTarget {
    name: Option<String>,
    path: String,

    // "type" is reserved, rename to extension
    #[serde(rename = "type")]
    extension: String,
}

fn value_to_string(value: &toml::Value) -> Result<String> {
    Ok(value
           .as_str()
           .ok_or("Failed to convert value to string")?
           .to_string())
}

fn version_to_str<S>(version: &Version, serializer: S) -> StdResult<S::Ok, S::Error>
    where S: Serializer
{
    serializer.serialize_str(version.to_string().as_str())
}

fn normalize_path(path: &String) -> String {
    path.replace("/", MAIN_SEPARATOR.to_string().as_str())
}
<commit_msg>Move build to end<commit_after>use std::path::{Path, PathBuf, MAIN_SEPARATOR};
use std::fs::File;
use std::collections::HashMap;
use std::io::prelude::*;
use std::env;
use toml;
use serde::Serializer;
use serde_json;
use std::result::Result as StdResult;
use semver::Version;

use errors::*;

#[derive(Serialize, Debug)]
pub struct Manifest {
    pub metadata: Metadata,
    pub src: Vec<Src>,
    pub dependencies: Vec<Dependency>,
    pub dev_dependencies: Vec<Dependency>,
    pub targets: Vec<Target>,
    pub build: PathBuf,
}

#[derive(Serialize, Debug)]
pub struct Metadata {
    pub name: String,
    #[serde(serialize_with = "version_to_str")]
    pub version: Version,
    pub authors: Vec<String>,
}

#[derive(Serialize, Debug)]
pub struct Src {
    pub name: String,
    pub path: String,
    pub optional: bool,
    pub fullpath: PathBuf,
}

#[derive(Serialize, Debug)]
pub struct Dependency {
    pub name: String,
    #[serde(serialize_with = "version_to_str")]
    pub version: Version,
    pub optional: bool,
}

#[derive(Serialize, Debug)]
pub struct Target {
    pub name: String,
    pub path: String,
    pub extension: String,
    pub fullpath: PathBuf,
    pub file: PathBuf,
}

impl Manifest {
    pub fn load<P: AsRef<Path>>(file: P) -> Result<Manifest> {
        let mut file = File::open(file).chain_err(|| "Failed to open manifest")?;
        let mut contents = String::new();
        file.read_to_string(&mut contents)
            .chain_err(|| "Failed to read manifest")?;

        let raw: RawManifest = toml::from_str(contents.as_str())
            .chain_err(|| "Failed to parse manifest")?;

        let cwd = env::current_dir()
            .chain_err(|| "Failed to load current directory")?;
        let build_dir = cwd.join("build");
        let package_name = raw.package.name;

        let mut src = vec![];
        for (name, value) in raw.src.unwrap_or(HashMap::new()) {
            match value {
                toml::Value::String(path) => {
                    let fullpath = cwd.join(normalize_path(&path));
                    src.push(Src {
                                 name,
                                 path,
                                 optional: false,
                                 fullpath,
                             });
                }
                toml::Value::Table(table) => {
                    let path =
                        value_to_string(table.get("path").ok_or("path is required for src")?)?;
                    let optional = table
                        .get("optional")
                        .unwrap_or(&toml::Value::Boolean(false))
                        .as_bool()
                        .ok_or("Failed to convert optional to boolean")?;
                    let fullpath = cwd.join(normalize_path(&path));

                    src.push(Src {
                                 name,
                                 path,
                                 optional,
                                 fullpath,
                             });
                }
                _ => bail!("Incompatible type for src"),
            }
        }

        let dependencies = vec![];
        let dev_dependencies = vec![];

        let mut targets = vec![];
        for target in raw.targets.unwrap_or(vec![]) {
            let name = target.name.unwrap_or(package_name.clone());
            let path = target.path;
            let fullpath = cwd.join(normalize_path(&path));
            let file = build_dir.join(format!("{}.{}", name, target.extension));

            targets.push(Target {
                             name,
                             path,
                             extension: target.extension,
                             fullpath,
                             file,
                         });
        }

        let parsed = Manifest {
            build: build_dir,
            metadata: Metadata {
                name: package_name,
                version: Version::parse(raw.package.version.as_str())
                    .chain_err(|| "Invalid package version")?,
                authors: raw.package.authors,
            },
            src,
            dependencies,
            dev_dependencies,
            targets,
        };
        Ok(parsed)
    }

    pub fn to_json(&self) -> Result<String> {
        let json = serde_json::to_string_pretty(&self)
            .chain_err(|| "Failed to convert manifest to JSON")?;

        Ok(json)
    }
}

// Deserialize is intentionally loose for user benefit
// toml -> RawManifest -> Manifest

#[derive(Deserialize, Debug)]
struct RawManifest {
    package: RawPackage,
    src: Option<HashMap<String, toml::Value>>,
    dependencies: Option<HashMap<String, toml::Value>>,

    #[serde(rename = "dev-dependencies")]
    dev_dependencies: Option<HashMap<String, toml::Value>>,
    targets: Option<Vec<RawTarget>>,
}

#[derive(Deserialize, Debug)]
struct RawPackage {
    name: String,
    version: String,
    authors: Vec<String>,
}

#[derive(Deserialize, Debug)]
struct RawTarget {
    name: Option<String>,
    path: String,

    // "type" is reserved, rename to extension
    #[serde(rename = "type")]
    extension: String,
}

fn value_to_string(value: &toml::Value) -> Result<String> {
    Ok(value
           .as_str()
           .ok_or("Failed to convert value to string")?
           .to_string())
}

fn version_to_str<S>(version: &Version, serializer: S) -> StdResult<S::Ok, S::Error>
    where S: Serializer
{
    serializer.serialize_str(version.to_string().as_str())
}

fn normalize_path(path: &String) -> String {
    path.replace("/", MAIN_SEPARATOR.to_string().as_str())
}
<|endoftext|>