<commit_before>use std::io::Read;
use std::fmt;

extern crate rand;
use self::rand::Rng;

use instruction::{Opcode, Instruction};
use enum_primitive::FromPrimitive;

const RAM_SIZE: usize = 4096;
const GPR_COUNT: usize = 16;

const NUMBER_OF_KEYS: usize = 16;

// 60Hz
const TIME_STEP: f32 = 1.0 / 60.0;

const FONT_SET: [u8; 80] = [
    0xf0, 0x90, 0x90, 0x90, 0xf0, // 0
    0x20, 0x60, 0x20, 0x20, 0x70, // 1
    0xf0, 0x10, 0xf0, 0x80, 0xf0, // 2
    0xf0, 0x10, 0xf0, 0x10, 0xf0, // 3
    0x90, 0x90, 0xf0, 0x10, 0x10, // 4
    0xf0, 0x80, 0xf0, 0x10, 0xf0, // 5
    0xf0, 0x80, 0xf0, 0x90, 0xf0, // 6
    0xf0, 0x10, 0x20, 0x40, 0x40, // 7
    0xf0, 0x90, 0xf0, 0x90, 0xf0, // 8
    0xf0, 0x90, 0xf0, 0x10, 0xf0, // 9
    0xf0, 0x90, 0xf0, 0x90, 0x90, // A
    0xe0, 0x90, 0xe0, 0x90, 0xe0, // B
    0xf0, 0x80, 0x80, 0x80, 0x80, // C
    0xe0, 0x90, 0x90, 0x90, 0xe0, // D
    0xf0, 0x80, 0xf0, 0x80, 0xf0, // E
    0xf0, 0x80, 0xf0, 0x80, 0x80, // F
];

pub struct CHIP8 {
    ram: Vec<u8>,

    // General Purpose Registers
    v: Vec<u8>,

    // Index Register
    i: u16,

    // Program Counter
    pc: u16,

    // Pixels
    pub gfx: Vec<u8>,

    // Timers
    delay_timer: u8,
    sound_timer: u8,

    // Stack & Stack Pointer
    stack: Vec<u16>,

    // Input state
    pub key: Vec<u8>,

    jmp: bool,

    time_acc: f32,

    instruction: Instruction,

    pub draw: bool,

    pub done: bool,
}

impl fmt::Display for CHIP8 {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "PC: {}\nInstruction: {}\nRegisters: {:?}\nDelay Timer: {}\nSound Timer: {}\nI: {}", self.pc, self.instruction, self.v, self.delay_timer, self.sound_timer, self.i)
    }
}

impl CHIP8 {
    pub fn tick(&mut self, dt: f32) {
        self.jmp = false;
        self.draw = false;

        self.instruction.parse(self.ram[self.pc as usize], self.ram[self.pc as usize + 1]);

        // println!("{}", self);

        match Opcode::from_u16(self.instruction.opcode).unwrap() {
            Opcode::sys | Opcode::noop => {},
            Opcode::cls => self.cls(),
            Opcode::ret => self.ret(),
            Opcode::jmp => self.jmp(),
            Opcode::call => self.call(),
            Opcode::se_vb => self.se_vb(),
            Opcode::sne_vb => self.sne_vb(),
            Opcode::se_vv => self.se_vv(),
            Opcode::ld_vb => self.ld_vb(),
            Opcode::add_vb => self.add_vb(),
            Opcode::ld_vv => self.ld_vv(),
            Opcode::or => self.or(),
            Opcode::and => self.and(),
            Opcode::xor => self.xor(),
            Opcode::add_vv => self.add_vv(),
            Opcode::sub => self.sub(),
            Opcode::shr => self.shr(),
            Opcode::subn => self.subn(),
            Opcode::shl => self.shl(),
            Opcode::sne_vv => self.sne_vv(),
            Opcode::ld_i => self.ld_i(),
            Opcode::jmp_v => self.jmp_v(),
            Opcode::rnd => self.rnd(),
            Opcode::drw => self.drw(),
            Opcode::skp => self.skp(),
            Opcode::sknp => self.sknp(),
            Opcode::ld_vdt => self.ld_vdt(),
            Opcode::ld_vk => self.ld_vk(),
            Opcode::ld_dtv => self.ld_dtv(),
            Opcode::ld_stv => self.ld_stv(),
            Opcode::add_iv => self.add_iv(),
            Opcode::ld_fv => self.ld_fv(),
            Opcode::ld_bv => self.ld_bv(),
            Opcode::ld_iv => self.ld_iv(),
            Opcode::ld_vi => self.ld_vi(),
            _ => panic!("Unrecognized opcode {:X}", self.instruction.opcode),
        }

        if !self.jmp {
            self.pc += 2;
        }

        self.time_acc += dt;
        while self.time_acc >= TIME_STEP {
            if self.delay_timer > 0 {
                self.delay_timer -= 1;
            }

            if self.sound_timer > 0 {
                self.sound_timer -= 1;
            }

            self.time_acc -= TIME_STEP;
        }
    }

    fn cls(&mut self) {
        self.gfx = vec![0; (::SCREEN_WIDTH * ::SCREEN_HEIGHT) as usize];
    }

    fn ret(&mut self) {
        self.pc = self.stack.pop().unwrap();
    }

    fn jmp(&mut self) {
        self.pc = self.instruction.nnn;
        self.jmp = true;
    }

    fn call(&mut self) {
        self.stack.push(self.pc);
        self.pc = self.instruction.nnn;
        self.jmp = true;
    }

    fn se_vb(&mut self) {
        if self.v[self.instruction.x] == self.instruction.kk {
            self.pc += 2;
        }
    }

    fn sne_vb(&mut self) {
        if self.v[self.instruction.x] != self.instruction.kk {
            self.pc += 2;
        }
    }

    fn se_vv(&mut self) {
        if self.v[self.instruction.x] == self.v[self.instruction.y] {
            self.pc += 2;
        }
    }

    fn ld_vb(&mut self) {
        self.v[self.instruction.x] = self.instruction.kk;
    }

    fn add_vb(&mut self) {
        self.v[self.instruction.x] = self.v[self.instruction.x].wrapping_add(self.instruction.kk);
    }

    fn ld_vv(&mut self) {
        self.v[self.instruction.x] = self.v[self.instruction.y];
    }

    fn or(&mut self) {
        self.v[self.instruction.x] |= self.v[self.instruction.y];
    }

    fn and(&mut self) {
        self.v[self.instruction.x] &= self.v[self.instruction.y];
    }

    fn xor(&mut self) {
        self.v[self.instruction.x] ^= self.v[self.instruction.y];
    }

    fn add_vv(&mut self) {
        let x = self.v[self.instruction.x] as u16;
        let y = self.v[self.instruction.y] as u16;
        let res = x + y;

        // println!("add_vv result: {}", res);

        self.v[0xF] = (res > 255) as u8;
        self.v[self.instruction.x] = res as u8;

        // println!("{} + {}; VF is {}", self.v[self.instruction.y], self.v[self.instruction.x], self.v[0xF]);
    }

    fn sub(&mut self) {
        // println!("sub x:{} y:{}", self.v[self.instruction.x], self.v[self.instruction.y])
        self.v[0xF] = (self.v[self.instruction.x] > self.v[self.instruction.y]) as u8;
        self.v[self.instruction.x] = self.v[self.instruction.x].wrapping_sub(self.v[self.instruction.y]);

        // println!("{} - {}; VF is {}", self.v[self.instruction.x], self.v[self.instruction.y], self.v[0xF]);
    }

    fn shr(&mut self) {
        self.v[0xf] = ((self.v[self.instruction.x] & 0xF0) >> 7 == 1) as u8;
        self.v[self.instruction.x] = self.v[self.instruction.x] / 2;
    }

    fn subn(&mut self) {
        self.v[0xF] = (self.v[self.instruction.y] > self.v[self.instruction.x]) as u8;
        self.v[self.instruction.x] = self.v[self.instruction.y].wrapping_sub(self.v[self.instruction.x]);
        // println!("{} - {}; VF is {}", self.v[self.instruction.y], self.v[self.instruction.x], self.v[0xF]);
    }

    fn shl(&mut self) {
        self.v[0xf] = (self.v[self.instruction.x] & 0xF == 1) as u8;
        self.v[self.instruction.x] *= 2;
    }

    fn sne_vv(&mut self) {
        if self.v[self.instruction.x] != self.v[self.instruction.y] {
            self.pc += 2;
        }
    }

    fn ld_i(&mut self) {
        self.i = self.instruction.nnn;
    }

    fn jmp_v(&mut self) {
        self.pc = (self.v[0] as u16) + self.instruction.nnn;
        self.jmp = true;
    }

    fn rnd(&mut self) {
        let mut rng = rand::thread_rng();
        self.v[self.instruction.x] = rng.gen::<u8>() & self.instruction.kk;
    }

    fn drw(&mut self) {
        let x = self.v[self.instruction.x] as usize;
        let y = self.v[self.instruction.y] as usize;
        let height = self.instruction.n as usize;

        self.v[0xF] = 0;

        for yline in 0..height {
            let pixel = self.ram[(self.i + yline as u16) as usize];
            for xline in 0..8 {
                if pixel & (0x80 >> xline) != 0 {
                    let mut index = (x + xline + (y + yline) * 64) as u16;

                    index = if index >= 64*32 {
                        64*32-1
                    } else {
                        index
                    };

                    if self.gfx[index as usize] == 1 {
                        self.v[0xF] = 1;
                    }

                    self.gfx[index as usize] ^= 1;
                }
            }
        }

        self.draw = true;
    }

    fn skp(&mut self) {
        if self.key[self.v[self.instruction.x] as usize] == 1 {
            self.pc += 2;
        }
    }

    fn sknp(&mut self) {
        if self.key[self.v[self.instruction.x] as usize] == 0 {
            self.pc += 2;
        }
    }

    fn ld_vdt(&mut self) {
        self.v[self.instruction.x] = self.delay_timer;
    }

    fn ld_vk(&mut self) {
        let mut key_pressed = false;

        for i in 0..self.key.len() {
            if self.key[i] == 1 {
                self.v[self.instruction.x] = i as u8;
                key_pressed = true;
            }
        }

        if !key_pressed {
            self.pc -= 2;
        }
    }

    fn ld_dtv(&mut self) {
        self.delay_timer = self.v[self.instruction.x];
    }

    fn ld_stv(&mut self) {
        self.sound_timer = self.v[self.instruction.x];
    }

    fn add_iv(&mut self) {
        self.i += self.v[self.instruction.x] as u16;
    }

    fn ld_fv(&mut self) {
        self.i = (self.v[self.instruction.x] * 5) as u16;
    }

    fn ld_bv(&mut self) {
        self.ram[self.i as usize] = self.v[self.instruction.x] / 100;
        self.ram[self.i as usize + 1] = (self.v[self.instruction.x] / 10) % 10;
        self.ram[self.i as usize + 2] = (self.v[self.instruction.x] % 100) % 10;
    }

    fn ld_iv(&mut self) {
        for i in 0usize..self.instruction.x {
            self.ram[self.i as usize + i] = self.v[i];
        }

        if self.instruction.x == 0 {
            self.ram[self.i as usize] = self.v[0];
        }
    }

    fn ld_vi(&mut self) {
        for i in 0usize..self.instruction.x {
            self.v[i] = self.ram[self.i as usize + i];
        }

        if self.instruction.x == 0 {
            self.v[0] = self.ram[self.i as usize];
        }
    }

    pub fn new(r: &mut Read) -> CHIP8 {
        let mut ram = vec![0; RAM_SIZE];

        // initialize font set
        for i in 0..80 {
            ram[i] = FONT_SET[i];
        }

        let mut rom: Vec<u8> = Vec::new();
        r.read_to_end(&mut rom).unwrap();

        for i in 0..rom.len() {
            ram[i + 512] = rom[i];
        }

        CHIP8 {
            ram: ram,
            v: vec![0; GPR_COUNT],
            i: 0,
            pc: 0x200,
            gfx: vec![0; (::SCREEN_WIDTH * ::SCREEN_HEIGHT) as usize],
            delay_timer: 0,
            sound_timer: 0,
            stack: Vec::new(),
            key: vec![0; NUMBER_OF_KEYS],
            jmp: false,
            time_acc: 0.0,
            instruction: Instruction::new(),
            draw: false,
            done: false,
        }
    }
}
<commit_msg>fix issue with ld_iv and ld_vi<commit_after>use std::io::Read;
use std::fmt;

extern crate rand;
use self::rand::Rng;

use instruction::{Opcode, Instruction};
use enum_primitive::FromPrimitive;

const RAM_SIZE: usize = 4096;
const GPR_COUNT: usize = 16;

const NUMBER_OF_KEYS: usize = 16;

// 60Hz
const TIME_STEP: f32 = 1.0 / 60.0;

const FONT_SET: [u8; 80] = [
    0xf0, 0x90, 0x90, 0x90, 0xf0, // 0
    0x20, 0x60, 0x20, 0x20, 0x70, // 1
    0xf0, 0x10, 0xf0, 0x80, 0xf0, // 2
    0xf0, 0x10, 0xf0, 0x10, 0xf0, // 3
    0x90, 0x90, 0xf0, 0x10, 0x10, // 4
    0xf0, 0x80, 0xf0, 0x10, 0xf0, // 5
    0xf0, 0x80, 0xf0, 0x90, 0xf0, // 6
    0xf0, 0x10, 0x20, 0x40, 0x40, // 7
    0xf0, 0x90, 0xf0, 0x90, 0xf0, // 8
    0xf0, 0x90, 0xf0, 0x10, 0xf0, // 9
    0xf0, 0x90, 0xf0, 0x90, 0x90, // A
    0xe0, 0x90, 0xe0, 0x90, 0xe0, // B
    0xf0, 0x80, 0x80, 0x80, 0x80, // C
    0xe0, 0x90, 0x90, 0x90, 0xe0, // D
    0xf0, 0x80, 0xf0, 0x80, 0xf0, // E
    0xf0, 0x80, 0xf0, 0x80, 0x80, // F
];

pub struct CHIP8 {
    ram: Vec<u8>,

    // General Purpose Registers
    v: Vec<u8>,

    // Index Register
    i: u16,

    // Program Counter
    pc: u16,

    // Pixels
    pub gfx: Vec<u8>,

    // Timers
    delay_timer: u8,
    sound_timer: u8,

    // Stack & Stack Pointer
    stack: Vec<u16>,

    // Input state
    pub key: Vec<u8>,

    jmp: bool,

    time_acc: f32,

    instruction: Instruction,

    pub draw: bool,

    pub done: bool,
}

impl fmt::Display for CHIP8 {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "PC: {}\nInstruction: {}\nRegisters: {:?}\nDelay Timer: {}\nSound Timer: {}\nI: {}", self.pc, self.instruction, self.v, self.delay_timer, self.sound_timer, self.i)
    }
}

impl CHIP8 {
    pub fn tick(&mut self, dt: f32) {
        self.jmp = false;
        self.draw = false;

        self.instruction.parse(self.ram[self.pc as usize], self.ram[self.pc as usize + 1]);

        // println!("{}", self);

        match Opcode::from_u16(self.instruction.opcode).unwrap() {
            Opcode::sys | Opcode::noop => {},
            Opcode::cls => self.cls(),
            Opcode::ret => self.ret(),
            Opcode::jmp => self.jmp(),
            Opcode::call => self.call(),
            Opcode::se_vb => self.se_vb(),
            Opcode::sne_vb => self.sne_vb(),
            Opcode::se_vv => self.se_vv(),
            Opcode::ld_vb => self.ld_vb(),
            Opcode::add_vb => self.add_vb(),
            Opcode::ld_vv => self.ld_vv(),
            Opcode::or => self.or(),
            Opcode::and => self.and(),
            Opcode::xor => self.xor(),
            Opcode::add_vv => self.add_vv(),
            Opcode::sub => self.sub(),
            Opcode::shr => self.shr(),
            Opcode::subn => self.subn(),
            Opcode::shl => self.shl(),
            Opcode::sne_vv => self.sne_vv(),
            Opcode::ld_i => self.ld_i(),
            Opcode::jmp_v => self.jmp_v(),
            Opcode::rnd => self.rnd(),
            Opcode::drw => self.drw(),
            Opcode::skp => self.skp(),
            Opcode::sknp => self.sknp(),
            Opcode::ld_vdt => self.ld_vdt(),
            Opcode::ld_vk => self.ld_vk(),
            Opcode::ld_dtv => self.ld_dtv(),
            Opcode::ld_stv => self.ld_stv(),
            Opcode::add_iv => self.add_iv(),
            Opcode::ld_fv => self.ld_fv(),
            Opcode::ld_bv => self.ld_bv(),
            Opcode::ld_iv => self.ld_iv(),
            Opcode::ld_vi => self.ld_vi(),
            _ => panic!("Unrecognized opcode {:X}", self.instruction.opcode),
        }

        if !self.jmp {
            self.pc += 2;
        }

        self.time_acc += dt;
        while self.time_acc >= TIME_STEP {
            if self.delay_timer > 0 {
                self.delay_timer -= 1;
            }

            if self.sound_timer > 0 {
                self.sound_timer -= 1;
            }

            self.time_acc -= TIME_STEP;
        }
    }

    fn cls(&mut self) {
        self.gfx = vec![0; (::SCREEN_WIDTH * ::SCREEN_HEIGHT) as usize];
    }

    fn ret(&mut self) {
        self.pc = self.stack.pop().unwrap();
    }

    fn jmp(&mut self) {
        self.pc = self.instruction.nnn;
        self.jmp = true;
    }

    fn call(&mut self) {
        self.stack.push(self.pc);
        self.pc = self.instruction.nnn;
        self.jmp = true;
    }

    fn se_vb(&mut self) {
        if self.v[self.instruction.x] == self.instruction.kk {
            self.pc += 2;
        }
    }

    fn sne_vb(&mut self) {
        if self.v[self.instruction.x] != self.instruction.kk {
            self.pc += 2;
        }
    }

    fn se_vv(&mut self) {
        if self.v[self.instruction.x] == self.v[self.instruction.y] {
            self.pc += 2;
        }
    }

    fn ld_vb(&mut self) {
        self.v[self.instruction.x] = self.instruction.kk;
    }

    fn add_vb(&mut self) {
        self.v[self.instruction.x] = self.v[self.instruction.x].wrapping_add(self.instruction.kk);
    }

    fn ld_vv(&mut self) {
        self.v[self.instruction.x] = self.v[self.instruction.y];
    }

    fn or(&mut self) {
        self.v[self.instruction.x] |= self.v[self.instruction.y];
    }

    fn and(&mut self) {
        self.v[self.instruction.x] &= self.v[self.instruction.y];
    }

    fn xor(&mut self) {
        self.v[self.instruction.x] ^= self.v[self.instruction.y];
    }

    fn add_vv(&mut self) {
        let x = self.v[self.instruction.x] as u16;
        let y = self.v[self.instruction.y] as u16;
        let res = x + y;

        // println!("add_vv result: {}", res);

        self.v[0xF] = (res > 255) as u8;
        self.v[self.instruction.x] = res as u8;

        // println!("{} + {}; VF is {}", self.v[self.instruction.y], self.v[self.instruction.x], self.v[0xF]);
    }

    fn sub(&mut self) {
        // println!("sub x:{} y:{}", self.v[self.instruction.x], self.v[self.instruction.y])
        self.v[0xF] = (self.v[self.instruction.x] > self.v[self.instruction.y]) as u8;
        self.v[self.instruction.x] = self.v[self.instruction.x].wrapping_sub(self.v[self.instruction.y]);

        // println!("{} - {}; VF is {}", self.v[self.instruction.x], self.v[self.instruction.y], self.v[0xF]);
    }

    fn shr(&mut self) {
        self.v[0xf] = ((self.v[self.instruction.x] & 0xF0) >> 7 == 1) as u8;
        self.v[self.instruction.x] = self.v[self.instruction.x] / 2;
    }

    fn subn(&mut self) {
        self.v[0xF] = (self.v[self.instruction.y] > self.v[self.instruction.x]) as u8;
        self.v[self.instruction.x] = self.v[self.instruction.y].wrapping_sub(self.v[self.instruction.x]);
        // println!("{} - {}; VF is {}", self.v[self.instruction.y], self.v[self.instruction.x], self.v[0xF]);
    }

    fn shl(&mut self) {
        self.v[0xf] = (self.v[self.instruction.x] & 0xF == 1) as u8;
        self.v[self.instruction.x] *= 2;
    }

    fn sne_vv(&mut self) {
        if self.v[self.instruction.x] != self.v[self.instruction.y] {
            self.pc += 2;
        }
    }

    fn ld_i(&mut self) {
        self.i = self.instruction.nnn;
    }

    fn jmp_v(&mut self) {
        self.pc = (self.v[0] as u16) + self.instruction.nnn;
        self.jmp = true;
    }

    fn rnd(&mut self) {
        let mut rng = rand::thread_rng();
        self.v[self.instruction.x] = rng.gen::<u8>() & self.instruction.kk;
    }

    fn drw(&mut self) {
        let x = self.v[self.instruction.x] as usize;
        let y = self.v[self.instruction.y] as usize;
        let height = self.instruction.n as usize;

        self.v[0xF] = 0;

        for yline in 0..height {
            let pixel = self.ram[(self.i + yline as u16) as usize];
            for xline in 0..8 {
                if pixel & (0x80 >> xline) != 0 {
                    let mut index = (x + xline + (y + yline) * 64) as u16;

                    index = if index >= 64*32 {
                        64*32-1
                    } else {
                        index
                    };

                    if self.gfx[index as usize] == 1 {
                        self.v[0xF] = 1;
                    }

                    self.gfx[index as usize] ^= 1;
                }
            }
        }

        self.draw = true;
    }

    fn skp(&mut self) {
        if self.key[self.v[self.instruction.x] as usize] == 1 {
            self.pc += 2;
        }
    }

    fn sknp(&mut self) {
        if self.key[self.v[self.instruction.x] as usize] == 0 {
            self.pc += 2;
        }
    }

    fn ld_vdt(&mut self) {
        self.v[self.instruction.x] = self.delay_timer;
    }

    fn ld_vk(&mut self) {
        let mut key_pressed = false;

        for i in 0..self.key.len() {
            if self.key[i] == 1 {
                self.v[self.instruction.x] = i as u8;
                key_pressed = true;
            }
        }

        if !key_pressed {
            self.pc -= 2;
        }
    }

    fn ld_dtv(&mut self) {
        self.delay_timer = self.v[self.instruction.x];
    }

    fn ld_stv(&mut self) {
        self.sound_timer = self.v[self.instruction.x];
    }

    fn add_iv(&mut self) {
        self.i += self.v[self.instruction.x] as u16;
    }

    fn ld_fv(&mut self) {
        self.i = (self.v[self.instruction.x] * 5) as u16;
    }

    fn ld_bv(&mut self) {
        self.ram[self.i as usize] = self.v[self.instruction.x] / 100;
        self.ram[self.i as usize + 1] = (self.v[self.instruction.x] / 10) % 10;
        self.ram[self.i as usize + 2] = (self.v[self.instruction.x] % 100) % 10;
    }

    fn ld_iv(&mut self) {
        for i in 0usize..self.instruction.x + 1 {
            self.ram[self.i as usize + i] = self.v[i];
        }
    }

    fn ld_vi(&mut self) {
        for i in 0usize..self.instruction.x + 1 {
            self.v[i] = self.ram[self.i as usize + i];
        }
    }

    pub fn new(r: &mut Read) -> CHIP8 {
        let mut ram = vec![0; RAM_SIZE];

        // initialize font set
        for i in 0..80 {
            ram[i] = FONT_SET[i];
        }

        let mut rom: Vec<u8> = Vec::new();
        r.read_to_end(&mut rom).unwrap();

        for i in 0..rom.len() {
            ram[i + 512] = rom[i];
        }

        CHIP8 {
            ram: ram,
            v: vec![0; GPR_COUNT],
            i: 0,
            pc: 0x200,
            gfx: vec![0; (::SCREEN_WIDTH * ::SCREEN_HEIGHT) as usize],
            delay_timer: 0,
            sound_timer: 0,
            stack: Vec::new(),
            key: vec![0; NUMBER_OF_KEYS],
            jmp: false,
            time_acc: 0.0,
            instruction: Instruction::new(),
            draw: false,
            done: false,
        }
    }
}
<|endoftext|>


<commit_before>//! This module focuses on handling errors generated when using Ardite in a
//! graceful manner.

use std::io::Error as IOError;
use std::error::Error as ErrorTrait;
use std::fmt::{Display, Formatter, Result as FmtResult};

#[cfg(test)]
use regex::Regex;
use serde_json::error::Error as JSONError;
use serde_yaml::error::Error as YAMLError;

use value::{Object, Value};

/// Any error generated by Ardite or it‘s drivers should be output using this
/// type. This allows for a comprehensive display of the error when a service
/// reports it to the user.
///
/// Information included with the error includes an `ErrorCode` (which maps to
/// an HTTP status code), a message, and an optional hint telling the user how
/// to fix the error.
///
/// Typically hints should be included for what would be considered the `4xx`
/// (in HTTP language) class of error codes.
///
/// # Tips For Writing Good Hint Messages
/// - Write in the second person (“You should…”).
/// - Always recommend a solution (“You should try…”, not “You must do…”).
/// - Be as specific as possible, if you have line numbers give them. If you
///   have file paths provide them.
/// - If it is a common error which generally confuses developers, provide a
///   link to a page which better explains the error and specific steps to
///   fix it.
#[derive(PartialEq, Debug)]
pub struct Error {
  /// A specific error code which describes the error.
  code: ErrorCode,
  /// A message providing more detail beyond the error code.
  message: String,
  /// A hint to the user on what to do next to try and avoid the error
  /// happening again. This is optional.
  hint: Option<String>
}

impl Error {
  /// Easily create a new error.
  pub fn new<S>(code: ErrorCode, message: S, hint: Option<S>) -> Self where S: Into<String> {
    Error {
      code: code,
      message: message.into(),
      hint: hint.map(|string| string.into())
    }
  }

  /// Get the code for the error.
  pub fn code(&self) -> &ErrorCode {
    &self.code
  }

  /// Get the message for the error.
  pub fn message(&self) -> &str {
    &self.message
  }

  /// Get the hint—for the error (see what I did there?).
  pub fn hint(&self) -> Option<&str> {
    self.hint.as_ref().map(|s| s.as_str())
  }

  /// Gets an object which represents the error.
  ///
  /// # Example
  /// ```rust
  /// #[macro_use(value)]
  /// extern crate ardite;
  ///
  /// use ardite::error::{Error, NotFound};
  ///
  /// # fn main() {
  ///
  /// let error = Error::new(NotFound, "Not found…", Some("Go to the light!"));
  ///
  /// let value = value!({
  ///   "error" => true,
  ///   "message" => "Not found…",
  ///   "hint" => "Go to the light!"
  /// });
  ///
  /// assert_eq!(error.to_value(), value);
  ///
  /// # }
  /// ```
  pub fn to_value(&self) -> Value {
    let mut object = Object::new();

    // TODO: implement an insert method which isn‘t as strict.
    object.insert("error".to_owned(), Value::Boolean(true));
    object.insert("message".to_owned(), Value::String(self.message.clone()));

    if let Some(ref hint) = self.hint {
      object.insert("hint".to_owned(), Value::String(hint.clone()));
    }

    Value::Object(object)
  }

  /// Convenience function for saying a thing failed validation using
  /// `ErrorCode::BadRequest`.
  ///
  /// # Example
  /// ```rust
  /// use ardite::error::{Error, ErrorCode};
  ///
  /// let error = Error::invalid("Failed validation.", "Try fixing your syntax!");
  ///
  /// assert_eq!(error, Error::new(ErrorCode::BadRequest, "Failed validation.", Some("Try fixing your syntax!")));
  /// ```
  pub fn invalid<S1, S2>(message: S1, hint: S2) -> Self where S1: Into<String>, S2: Into<String> {
    Error {
      code: ErrorCode::BadRequest,
      message: message.into(),
      hint: Some(hint.into())
    }
  }

  /// Convenience function for saying there was an internal error using
  /// `ErrorCode::Internal`.
  ///
  /// # Example
  /// ```rust
  /// use ardite::error::{Error, ErrorCode};
  ///
  /// let error = Error::internal("Something blew up.");
  ///
  /// assert_eq!(error, Error::new(ErrorCode::Internal, "Something blew up.", None));
  /// ```
  pub fn internal<S>(message: S) -> Self where S: Into<String> {
    Error {
      code: ErrorCode::Internal,
      message: message.into(),
      hint: None
    }
  }

  /// Convenience function for creating an unimplemented error with a plain
  /// message describing what is unimplemented using
  /// `ErrorCode::NotImplemented`.
  ///
  /// # Example
  /// ```rust
  /// use ardite::error::{Error, ErrorCode};
  ///
  /// let error = Error::unimplemented("Cache invalidation is hard.");
  ///
  /// assert_eq!(error, Error::new(ErrorCode::NotImplemented, "Cache invalidation is hard.", None));
  /// ```
  pub fn unimplemented<S>(message: S) -> Self where S: Into<String> {
    Error {
      code: ErrorCode::NotImplemented,
      message: message.into(),
      hint: None
    }
  }

  /// Special assertion for error messages. Takes a regular expression string
  /// argument which will automatically be constructed into a regualr
  /// expression. Only available in testing environments. Panics if the regular
  /// expression doesn’t match the error message string.
  #[cfg(test)]
  pub fn assert_message(&self, regex_str: &str) {
    if !Regex::new(regex_str).unwrap().is_match(&self.message) {
      panic!("Error message \"{}\" does not match regex /{}/", self.message, regex_str);
    }
  }
}

impl Display for Error {
  fn fmt(&self, fmt: &mut Formatter) -> FmtResult {
    try!(write!(fmt, "\ncode: {}", self.code));
    try!(write!(fmt, "\nmessage: {}", self.message));

    if let Some(ref hint) = self.hint {
      try!(write!(fmt, "\nhint: {}", hint));
    }

    Ok(())
  }
}

impl ErrorTrait for Error {
  fn description(&self) -> &str {
    self.message.as_str()
  }
}

impl From<IOError> for Error {
  fn from(error: IOError) -> Self {
    Error {
      code: ErrorCode::Internal,
      message: error.description().to_owned(),
      hint: None
    }
  }
}

impl From<JSONError> for Error {
  fn from(error: JSONError) -> Self {
    match error {
      JSONError::Syntax(_, line, column) => {
        Error {
          code: ErrorCode::BadRequest,
          message: "Syntax error.".to_owned(),
          hint: Some(format!("Max sure your JSON syntax is correct around line {} column {}.", line, column))
        }
      },
      _ => {
        Error {
          code: ErrorCode::Internal,
          message: error.description().to_owned(),
          hint: None
        }
      }
    }
  }
}

impl From<YAMLError> for Error {
  fn from(error: YAMLError) -> Self {
    match error {
      YAMLError::Custom(ref message) => {
        Error {
          code: ErrorCode::BadRequest,
          message: message.to_owned(),
          hint: Some("Make sure your YAML syntax is correct.".to_owned())
        }
      },
      _ => {
        Error {
          code: ErrorCode::Internal,
          message: error.description().to_owned(),
          hint: None
        }
      }
    }
  }
}

#[cfg(feature = "error_iron")]
mod iron {
  extern crate iron;

  use super::Error;

  use self::iron::prelude::*;
  use self::iron::headers::{ContentType, ContentLength};
  use self::iron::mime::{Mime, TopLevel, SubLevel, Attr, Value};
  use self::iron::modifiers::Header;
  use self::iron::status::Status;

  impl Into<IronError> for Error {
    fn into(self) -> IronError {
      let mut res = Response::new();

      let content = self.to_value().to_json();

      res.set_mut(Header(ContentType(Mime(TopLevel::Application, SubLevel::Json, vec![(Attr::Charset, Value::Utf8)]))));
      res.set_mut(Header(ContentLength(content.len() as u64)));
      res.set_mut(Status::from_u16(self.code().to_u16()));
      res.set_mut(content);

      IronError {
        error: Box::new(self),
        response: res
      }
    }
  }
}

/// The code of an error. Designed to easily map to [HTTP status codes][1],
/// however only a subset of these codes are supported and some custom codes
/// were added.
///
/// [1]: http://www.restapitutorial.com/httpstatuscodes.html
#[derive(PartialEq, Debug)]
pub enum ErrorCode {
  /// A bad syntax was used.
  BadRequest,
  /// Permissions do not allow this to happen.
  Forbidden,
  /// Resource was not found.
  NotFound,
  /// Whatever method (think CRUD) used is not allowed.
  MethodNotAllowed,
  /// The requested resource is not acceptable.
  NotAcceptable,
  /// Present data made the request fail.
  Conflict,
  /// There was an invalid range.
  BadRange,
  /// Something bad happened inside a driver.
  Internal,
  /// The feature has not been implemented.
  NotImplemented
}

pub use error::ErrorCode::*;

impl ErrorCode {
  pub fn to_u16(&self) -> u16 {
    match *self {
      BadRequest => 400,
      Forbidden => 403,
      NotFound => 404,
      MethodNotAllowed => 405,
      NotAcceptable => 406,
      Conflict => 409,
      BadRange => 416,
      Internal => 500,
      NotImplemented => 501
    }
  }

  pub fn reason(&self) -> &str {
    match *self {
      BadRequest => "Bad Request",
      Forbidden => "Forbidden",
      NotFound => "Not Found",
      MethodNotAllowed => "Method Not Allowed",
      NotAcceptable => "Not Acceptable",
      Conflict => "Conflict",
      BadRange => "Range Not Satisfiable",
      Internal => "Internal Error",
      NotImplemented => "Not Implemented"
    }
  }
}

impl Display for ErrorCode {
  fn fmt(&self, fmt: &mut Formatter) -> FmtResult {
    try!(write!(fmt, "{} {}", self.to_u16(), self.reason()));
    Ok(())
  }
}
<commit_msg>fix(error): fix iron error to_json usage<commit_after>//! This module focuses on handling errors generated when using Ardite in a
//! graceful manner.

use std::io::Error as IOError;
use std::error::Error as ErrorTrait;
use std::fmt::{Display, Formatter, Result as FmtResult};

#[cfg(test)]
use regex::Regex;
use serde_json::error::Error as JSONError;
use serde_yaml::error::Error as YAMLError;

use value::{Object, Value};

/// Any error generated by Ardite or it‘s drivers should be output using this
/// type. This allows for a comprehensive display of the error when a service
/// reports it to the user.
///
/// Information included with the error includes an `ErrorCode` (which maps to
/// an HTTP status code), a message, and an optional hint telling the user how
/// to fix the error.
///
/// Typically hints should be included for what would be considered the `4xx`
/// (in HTTP language) class of error codes.
///
/// # Tips For Writing Good Hint Messages
/// - Write in the second person (“You should…”).
/// - Always recommend a solution (“You should try…”, not “You must do…”).
/// - Be as specific as possible, if you have line numbers give them. If you
///   have file paths provide them.
/// - If it is a common error which generally confuses developers, provide a
///   link to a page which better explains the error and specific steps to
///   fix it.
#[derive(PartialEq, Debug)]
pub struct Error {
  /// A specific error code which describes the error.
  code: ErrorCode,
  /// A message providing more detail beyond the error code.
  message: String,
  /// A hint to the user on what to do next to try and avoid the error
  /// happening again. This is optional.
  hint: Option<String>
}

impl Error {
  /// Easily create a new error.
  pub fn new<S>(code: ErrorCode, message: S, hint: Option<S>) -> Self where S: Into<String> {
    Error {
      code: code,
      message: message.into(),
      hint: hint.map(|string| string.into())
    }
  }

  /// Get the code for the error.
  pub fn code(&self) -> &ErrorCode {
    &self.code
  }

  /// Get the message for the error.
  pub fn message(&self) -> &str {
    &self.message
  }

  /// Get the hint—for the error (see what I did there?).
  pub fn hint(&self) -> Option<&str> {
    self.hint.as_ref().map(|s| s.as_str())
  }

  /// Gets an object which represents the error.
  ///
  /// # Example
  /// ```rust
  /// #[macro_use(value)]
  /// extern crate ardite;
  ///
  /// use ardite::error::{Error, NotFound};
  ///
  /// # fn main() {
  ///
  /// let error = Error::new(NotFound, "Not found…", Some("Go to the light!"));
  ///
  /// let value = value!({
  ///   "error" => true,
  ///   "message" => "Not found…",
  ///   "hint" => "Go to the light!"
  /// });
  ///
  /// assert_eq!(error.to_value(), value);
  ///
  /// # }
  /// ```
  pub fn to_value(&self) -> Value {
    let mut object = Object::new();

    // TODO: implement an insert method which isn‘t as strict.
    object.insert("error".to_owned(), Value::Boolean(true));
    object.insert("message".to_owned(), Value::String(self.message.clone()));

    if let Some(ref hint) = self.hint {
      object.insert("hint".to_owned(), Value::String(hint.clone()));
    }

    Value::Object(object)
  }

  /// Convenience function for saying a thing failed validation using
  /// `ErrorCode::BadRequest`.
  ///
  /// # Example
  /// ```rust
  /// use ardite::error::{Error, ErrorCode};
  ///
  /// let error = Error::invalid("Failed validation.", "Try fixing your syntax!");
  ///
  /// assert_eq!(error, Error::new(ErrorCode::BadRequest, "Failed validation.", Some("Try fixing your syntax!")));
  /// ```
  pub fn invalid<S1, S2>(message: S1, hint: S2) -> Self where S1: Into<String>, S2: Into<String> {
    Error {
      code: ErrorCode::BadRequest,
      message: message.into(),
      hint: Some(hint.into())
    }
  }

  /// Convenience function for saying there was an internal error using
  /// `ErrorCode::Internal`.
  ///
  /// # Example
  /// ```rust
  /// use ardite::error::{Error, ErrorCode};
  ///
  /// let error = Error::internal("Something blew up.");
  ///
  /// assert_eq!(error, Error::new(ErrorCode::Internal, "Something blew up.", None));
  /// ```
  pub fn internal<S>(message: S) -> Self where S: Into<String> {
    Error {
      code: ErrorCode::Internal,
      message: message.into(),
      hint: None
    }
  }

  /// Convenience function for creating an unimplemented error with a plain
  /// message describing what is unimplemented using
  /// `ErrorCode::NotImplemented`.
  ///
  /// # Example
  /// ```rust
  /// use ardite::error::{Error, ErrorCode};
  ///
  /// let error = Error::unimplemented("Cache invalidation is hard.");
  ///
  /// assert_eq!(error, Error::new(ErrorCode::NotImplemented, "Cache invalidation is hard.", None));
  /// ```
  pub fn unimplemented<S>(message: S) -> Self where S: Into<String> {
    Error {
      code: ErrorCode::NotImplemented,
      message: message.into(),
      hint: None
    }
  }

  /// Special assertion for error messages. Takes a regular expression string
  /// argument which will automatically be constructed into a regualr
  /// expression. Only available in testing environments. Panics if the regular
  /// expression doesn’t match the error message string.
  #[cfg(test)]
  pub fn assert_message(&self, regex_str: &str) {
    if !Regex::new(regex_str).unwrap().is_match(&self.message) {
      panic!("Error message \"{}\" does not match regex /{}/", self.message, regex_str);
    }
  }
}

impl Display for Error {
  fn fmt(&self, fmt: &mut Formatter) -> FmtResult {
    try!(write!(fmt, "\ncode: {}", self.code));
    try!(write!(fmt, "\nmessage: {}", self.message));

    if let Some(ref hint) = self.hint {
      try!(write!(fmt, "\nhint: {}", hint));
    }

    Ok(())
  }
}

impl ErrorTrait for Error {
  fn description(&self) -> &str {
    self.message.as_str()
  }
}

impl From<IOError> for Error {
  fn from(error: IOError) -> Self {
    Error {
      code: ErrorCode::Internal,
      message: error.description().to_owned(),
      hint: None
    }
  }
}

impl From<JSONError> for Error {
  fn from(error: JSONError) -> Self {
    match error {
      JSONError::Syntax(_, line, column) => {
        Error {
          code: ErrorCode::BadRequest,
          message: "Syntax error.".to_owned(),
          hint: Some(format!("Max sure your JSON syntax is correct around line {} column {}.", line, column))
        }
      },
      _ => {
        Error {
          code: ErrorCode::Internal,
          message: error.description().to_owned(),
          hint: None
        }
      }
    }
  }
}

impl From<YAMLError> for Error {
  fn from(error: YAMLError) -> Self {
    match error {
      YAMLError::Custom(ref message) => {
        Error {
          code: ErrorCode::BadRequest,
          message: message.to_owned(),
          hint: Some("Make sure your YAML syntax is correct.".to_owned())
        }
      },
      _ => {
        Error {
          code: ErrorCode::Internal,
          message: error.description().to_owned(),
          hint: None
        }
      }
    }
  }
}

// TODO: consider moving this to `ardite-rest`
#[cfg(feature = "error_iron")]
mod iron {
  extern crate iron;

  use super::Error;

  use self::iron::prelude::*;
  use self::iron::headers::{ContentType, ContentLength};
  use self::iron::mime::{Mime, TopLevel, SubLevel, Attr, Value};
  use self::iron::modifiers::Header;
  use self::iron::status::Status;

  impl Into<IronError> for Error {
    fn into(self) -> IronError {
      let mut res = Response::new();

      res.set_mut(Status::from_u16(self.code().to_u16()));

      // Tries to send the error as a response in JSON, however, if that fails
      // the error is sent as plain text.
      if let Ok(content) = self.to_value().to_json() {
        res.set_mut(Header(ContentType(Mime(TopLevel::Application, SubLevel::Json, vec![(Attr::Charset, Value::Utf8)]))));
        res.set_mut(Header(ContentLength(content.len() as u64)));
        res.set_mut(content);
      } else {
        res.set_mut(Header(ContentType(Mime(TopLevel::Text, SubLevel::Plain, vec![(Attr::Charset, Value::Utf8)]))));
        let content = format!("{}", self);
        res.set_mut(Header(ContentLength(content.len() as u64)));
        res.set_mut(content);
      }

      IronError {
        error: Box::new(self),
        response: res
      }
    }
  }
}

/// The code of an error. Designed to easily map to [HTTP status codes][1],
/// however only a subset of these codes are supported and some custom codes
/// were added.
///
/// [1]: http://www.restapitutorial.com/httpstatuscodes.html
#[derive(PartialEq, Debug)]
pub enum ErrorCode {
  /// A bad syntax was used.
  BadRequest,
  /// Permissions do not allow this to happen.
  Forbidden,
  /// Resource was not found.
  NotFound,
  /// Whatever method (think CRUD) used is not allowed.
  MethodNotAllowed,
  /// The requested resource is not acceptable.
  NotAcceptable,
  /// Present data made the request fail.
  Conflict,
  /// There was an invalid range.
  BadRange,
  /// Something bad happened inside a driver.
  Internal,
  /// The feature has not been implemented.
  NotImplemented
}

pub use error::ErrorCode::*;

impl ErrorCode {
  pub fn to_u16(&self) -> u16 {
    match *self {
      BadRequest => 400,
      Forbidden => 403,
      NotFound => 404,
      MethodNotAllowed => 405,
      NotAcceptable => 406,
      Conflict => 409,
      BadRange => 416,
      Internal => 500,
      NotImplemented => 501
    }
  }

  pub fn reason(&self) -> &str {
    match *self {
      BadRequest => "Bad Request",
      Forbidden => "Forbidden",
      NotFound => "Not Found",
      MethodNotAllowed => "Method Not Allowed",
      NotAcceptable => "Not Acceptable",
      Conflict => "Conflict",
      BadRange => "Range Not Satisfiable",
      Internal => "Internal Error",
      NotImplemented => "Not Implemented"
    }
  }
}

impl Display for ErrorCode {
  fn fmt(&self, fmt: &mut Formatter) -> FmtResult {
    try!(write!(fmt, "{} {}", self.to_u16(), self.reason()));
    Ok(())
  }
}
<|endoftext|>


<commit_before>extern crate servo;

use self::servo::config::servo_version;
use self::servo::servo_config::opts;
use self::servo::servo_config::prefs::{PrefValue, PREFS};
use self::servo::compositing::windowing::{WindowMethods, WindowEvent, WindowNavigateMsg};
use self::servo::compositing::compositor_thread::{self, CompositorProxy, CompositorReceiver};
use self::servo::msg::constellation_msg::{self, Key};
use self::servo::servo_geometry::DeviceIndependentPixel;
use self::servo::euclid::{Point2D, Size2D};
use self::servo::euclid::scale_factor::ScaleFactor;
use self::servo::euclid::point::TypedPoint2D;
use self::servo::euclid::rect::TypedRect;
use self::servo::euclid::size::TypedSize2D;
use self::servo::script_traits::{DevicePixel, TouchEventType};
use self::servo::servo_url::ServoUrl;
use self::servo::net_traits::net_error_list::NetError;
use self::servo::webrender_traits;
use self::servo::style_traits::cursor::Cursor as ServoCursor;

use view::{DrawableGeometry, TouchPhase};
use platform::EventLoopRiser;

use std::sync::mpsc;
use std::rc::Rc;
use std::cell::{Cell, RefCell};

#[derive(Debug, Clone)]
pub enum ServoEvent {
    SetWindowInnerSize(u32, u32),
    SetWindowPosition(i32, i32),
    SetFullScreenState(bool),
    Present,
    TitleChanged(Option<String>),
    UnhandledURL(ServoUrl),
    StatusChanged(Option<String>),
    LoadStart(bool, bool),
    LoadEnd(bool, bool, bool),
    LoadError(String),
    HeadParsed(ServoUrl),
    CursorChanged(ServoCursor),
    FaviconChanged(ServoUrl),
    Key(Option<char>, Key, constellation_msg::KeyModifiers),
}

pub type CompositorChannel = (Box<CompositorProxy + Send>, Box<CompositorReceiver>);

pub enum FollowLinkPolicy {
    FollowAnyLink,
    FollowOriginalDomain,
}

pub struct Servo {
    // FIXME: it's annoying that event for servo are named "WindowEvent"
    events_for_servo: RefCell<Vec<WindowEvent>>,
    servo_browser: RefCell<servo::Browser<ServoCallbacks>>,
    callbacks: Rc<ServoCallbacks>,
}

impl Servo {

    pub fn configure(url: &str) {
        let url = ServoUrl::parse(url).ok().unwrap(); // FIXME. What if fail?
        let mut opts = opts::default_opts();
        opts.headless = false;
        opts.url = Some(url);
        opts::set_defaults(opts);
        // FIXME: Pipeline creation fails is layout_threads pref not set
        PREFS.set("layout.threads", PrefValue::Number(1.0));
    }

    pub fn version(&self) -> String {
        servo_version()
    }

    pub fn new(geometry: DrawableGeometry,
               riser: EventLoopRiser,
               url: &str,
               follow_link_policy: FollowLinkPolicy)
               -> Servo {

        let url = ServoUrl::parse(url).ok().unwrap(); // FIXME. What if fail?

        let allowed_domain = match (follow_link_policy, url.domain()) {
            (FollowLinkPolicy::FollowOriginalDomain, Some(domain)) => {
                Some(domain.clone().to_owned())
            }
            _ => None
        };

        let callbacks = Rc::new(ServoCallbacks {
            event_queue: RefCell::new(Vec::new()),
            geometry: Cell::new(geometry),
            riser: riser,
            allowed_domain: allowed_domain,
        });

        let mut servo = servo::Browser::new(callbacks.clone());

        servo.handle_events(vec![WindowEvent::InitializeCompositing]);

        Servo {
            events_for_servo: RefCell::new(Vec::new()),
            servo_browser: RefCell::new(servo),
            callbacks: callbacks,
        }
    }

    pub fn get_events(&self) -> Vec<ServoEvent> {
        self.callbacks.get_events()
    }

    pub fn reload(&self) {
        let event = WindowEvent::Reload;
        self.events_for_servo.borrow_mut().push(event);
    }

    pub fn go_back(&self) {
        let event = WindowEvent::Navigation(WindowNavigateMsg::Back);
        self.events_for_servo.borrow_mut().push(event);
    }

    pub fn go_forward(&self) {
        let event = WindowEvent::Navigation(WindowNavigateMsg::Forward);
        self.events_for_servo.borrow_mut().push(event);
    }

    // pub fn perform_mouse_move(&self, x: i32, y: i32) {
    //     let event = WindowEvent::MouseWindowMoveEventClass(TypedPoint2D::new(x as f32, y as f32));
    //     self.events_for_servo.borrow_mut().push(event);
    // }

    pub fn perform_scroll(&self, x: i32, y: i32, dx: f32, dy: f32, phase: TouchPhase) {
        let scroll_location = webrender_traits::ScrollLocation::Delta(TypedPoint2D::new(dx, dy));
        let phase = match phase {
            TouchPhase::Started => TouchEventType::Down,
            TouchPhase::Moved => TouchEventType::Move,
            TouchPhase::Ended => TouchEventType::Up,
        };
        let event = WindowEvent::Scroll(scroll_location, TypedPoint2D::new(x, y), phase);
        self.events_for_servo.borrow_mut().push(event);
    }

    pub fn update_geometry(&self, geometry: DrawableGeometry) {
        self.callbacks.update_geometry(geometry);
        let event = WindowEvent::Resize(self.callbacks.framebuffer_size());
        self.events_for_servo.borrow_mut().push(event);
    }

    // pub fn perform_click(&self,
    //              x: i32,
    //              y: i32,
    //              org_x: i32,
    //              org_y: i32,
    //              element_state: WindowElementState,
    //              mouse_button: WindowMouseButton,
    //              mouse_down_button: Option<WindowMouseButton>) {
    //     use self::servo::script_traits::MouseButton;
    //     let max_pixel_dist = 10f64;
    //     let event = match element_state {
    //         WindowElementState::Pressed => {
    //             MouseWindowEvent::MouseDown(MouseButton::Left,
    //                                         TypedPoint2D::new(x as f32, y as f32))
    //         }
    //         WindowElementState::Released => {
    //             let mouse_up_event = MouseWindowEvent::MouseUp(MouseButton::Left,
    //                                                            TypedPoint2D::new(x as f32,
    //                                                                              y as f32));
    //             match mouse_down_button {
    //                 None => mouse_up_event,
    //                 Some(but) if mouse_button == but => {
    //                     // Same button
    //                     let pixel_dist = Point2D::new(org_x, org_y) - Point2D::new(x, y);
    //                     let pixel_dist =
    //                         ((pixel_dist.x * pixel_dist.x + pixel_dist.y * pixel_dist.y) as f64)
    //                             .sqrt();
    //                     if pixel_dist < max_pixel_dist {
    //                         self.events_for_servo
    //                             .borrow_mut()
    //                             .push(WindowEvent::MouseWindowEventClass(mouse_up_event));
    //                         MouseWindowEvent::Click(MouseButton::Left,
    //                                                 TypedPoint2D::new(x as f32, y as f32))
    //                     } else {
    //                         mouse_up_event
    //                     }
    //                 }
    //                 Some(_) => mouse_up_event,
    //             }
    //         }
    //     };
    //     self.events_for_servo.borrow_mut().push(WindowEvent::MouseWindowEventClass(event));
    // }

    pub fn sync(&self) {
        let clone = self.events_for_servo.borrow().clone();
        self.events_for_servo.borrow_mut().clear();
        self.servo_browser.borrow_mut().handle_events(clone);
    }
}

struct ServoCallbacks {
    event_queue: RefCell<Vec<ServoEvent>>,
    geometry: Cell<DrawableGeometry>,
    riser: EventLoopRiser,
    allowed_domain: Option<String>,
}

impl ServoCallbacks {
    fn get_events(&self) -> Vec<ServoEvent> {
        // FIXME: ports/glutin/window.rs uses mem::replace. Should we too?
        // See: https://doc.rust-lang.org/core/mem/fn.replace.html
        // FIXME: maybe use drain
        let clone = self.event_queue.borrow().clone();
        self.event_queue.borrow_mut().clear();
        clone
    }

    fn update_geometry(&self, geometry: DrawableGeometry) {
        self.geometry.set(geometry);
    }
}

impl WindowMethods for ServoCallbacks {
    fn prepare_for_composite(&self, _width: usize, _height: usize) -> bool {
        true
    }

    fn supports_clipboard(&self) -> bool {
        // FIXME
        false
    }

    fn allow_navigation(&self, url: ServoUrl) -> bool {
        let allow = self.allowed_domain
            .as_ref()
            .map(|domain| url.domain().unwrap() == domain)
            .unwrap_or(false);
        if !allow {
            self.event_queue.borrow_mut().push(ServoEvent::UnhandledURL(url));
        }
        allow
    }

    fn create_compositor_channel(&self) -> CompositorChannel {
        let (sender, receiver) = mpsc::channel();
        (box ShellCompositorProxy {
             sender: sender,
             riser: self.riser.clone(),
         } as Box<CompositorProxy + Send>,
         box receiver as Box<CompositorReceiver>)
    }

    fn hidpi_factor(&self) -> ScaleFactor<f32, DeviceIndependentPixel, DevicePixel> {
        let scale_factor = self.geometry.get().hidpi_factor;
        ScaleFactor::new(scale_factor)
    }

    fn framebuffer_size(&self) -> TypedSize2D<u32, DevicePixel> {
        let scale_factor = self.geometry.get().hidpi_factor as u32;
        let (width, height) = self.geometry.get().view_size;
        TypedSize2D::new(scale_factor * width, scale_factor * height)
    }

    fn window_rect(&self) -> TypedRect<u32, DevicePixel> {
        let scale_factor = self.geometry.get().hidpi_factor as u32;
        let mut size = self.framebuffer_size();

        let (top, right, bottom, left) = self.geometry.get().margins;
        let top = top * scale_factor;
        let right = right * scale_factor;
        let bottom = bottom * scale_factor;
        let left = left * scale_factor;

        size.height = size.height - top - bottom;
        size.width = size.width - left - right;

        TypedRect::new(TypedPoint2D::new(left, top), size)
    }

    fn size(&self) -> TypedSize2D<f32, DeviceIndependentPixel> {
        let (width, height) = self.geometry.get().view_size;
        TypedSize2D::new(width as f32, height as f32)
    }

    fn client_window(&self) -> (Size2D<u32>, Point2D<i32>) {
        let (width, height) = self.geometry.get().view_size;
        let (x, y) = self.geometry.get().position;
        (Size2D::new(width, height), Point2D::new(x as i32, y as i32))
    }

    // Events

    fn set_inner_size(&self, size: Size2D<u32>) {
        self.event_queue
            .borrow_mut()
            .push(ServoEvent::SetWindowInnerSize(size.width as u32, size.height as u32));
    }

    fn set_position(&self, point: Point2D<i32>) {
        self.event_queue.borrow_mut().push(ServoEvent::SetWindowPosition(point.x, point.y));
    }

    fn set_fullscreen_state(&self, state: bool) {
        self.event_queue.borrow_mut().push(ServoEvent::SetFullScreenState(state))
    }

    fn present(&self) {
        // FIXME: what is that for? Aren't we supposed to flush buffer everytime
        // CompositorProxy::send is called?
        self.event_queue.borrow_mut().push(ServoEvent::Present);
    }

    fn set_page_title(&self, title: Option<String>) {
        self.event_queue.borrow_mut().push(ServoEvent::TitleChanged(title));
    }

    fn status(&self, status: Option<String>) {
        self.event_queue.borrow_mut().push(ServoEvent::StatusChanged(status));
    }

    fn load_start(&self, can_go_back: bool, can_go_forward: bool) {
        self.event_queue.borrow_mut().push(ServoEvent::LoadStart(can_go_back, can_go_forward));
    }

    fn load_end(&self, can_go_back: bool, can_go_forward: bool, root: bool) {
        self.event_queue.borrow_mut().push(ServoEvent::LoadEnd(can_go_back, can_go_forward, root));
    }

    fn load_error(&self, _: NetError, url: String) {
        // FIXME: never called by servo
        self.event_queue.borrow_mut().push(ServoEvent::LoadError(url));
    }

    fn head_parsed(&self, url: ServoUrl) {
        self.event_queue.borrow_mut().push(ServoEvent::HeadParsed(url));
    }

    fn set_cursor(&self, cursor: ServoCursor) {
        println!("set_cursor: {:?}", cursor);
        self.event_queue.borrow_mut().push(ServoEvent::CursorChanged(cursor));
    }

    fn set_favicon(&self, url: ServoUrl) {
        self.event_queue.borrow_mut().push(ServoEvent::FaviconChanged(url));
    }

    fn handle_key(&self, ch: Option<char>, key: Key, mods: constellation_msg::KeyModifiers) {
        self.event_queue.borrow_mut().push(ServoEvent::Key(ch, key, mods));
    }
}

struct ShellCompositorProxy {
    sender: mpsc::Sender<compositor_thread::Msg>,
    riser: EventLoopRiser,
}

impl CompositorProxy for ShellCompositorProxy {
    fn send(&self, msg: compositor_thread::Msg) {
        if let Err(err) = self.sender.send(msg) {
            println!("Failed to send response ({}).", err);
        }
        self.riser.rise()
    }

    fn clone_compositor_proxy(&self) -> Box<CompositorProxy + Send> {
        box ShellCompositorProxy {
            sender: self.sender.clone(),
            riser: self.riser.clone(),
        } as Box<CompositorProxy + Send>
    }
}
<commit_msg>use drain<commit_after>extern crate servo;

use self::servo::config::servo_version;
use self::servo::servo_config::opts;
use self::servo::servo_config::prefs::{PrefValue, PREFS};
use self::servo::compositing::windowing::{WindowMethods, WindowEvent, WindowNavigateMsg};
use self::servo::compositing::compositor_thread::{self, CompositorProxy, CompositorReceiver};
use self::servo::msg::constellation_msg::{self, Key};
use self::servo::servo_geometry::DeviceIndependentPixel;
use self::servo::euclid::{Point2D, Size2D};
use self::servo::euclid::scale_factor::ScaleFactor;
use self::servo::euclid::point::TypedPoint2D;
use self::servo::euclid::rect::TypedRect;
use self::servo::euclid::size::TypedSize2D;
use self::servo::script_traits::{DevicePixel, TouchEventType};
use self::servo::servo_url::ServoUrl;
use self::servo::net_traits::net_error_list::NetError;
use self::servo::webrender_traits;
use self::servo::style_traits::cursor::Cursor as ServoCursor;

use view::{DrawableGeometry, TouchPhase};
use platform::EventLoopRiser;

use std::sync::mpsc;
use std::rc::Rc;
use std::cell::{Cell, RefCell};

#[derive(Debug, Clone)]
pub enum ServoEvent {
    SetWindowInnerSize(u32, u32),
    SetWindowPosition(i32, i32),
    SetFullScreenState(bool),
    Present,
    TitleChanged(Option<String>),
    UnhandledURL(ServoUrl),
    StatusChanged(Option<String>),
    LoadStart(bool, bool),
    LoadEnd(bool, bool, bool),
    LoadError(String),
    HeadParsed(ServoUrl),
    CursorChanged(ServoCursor),
    FaviconChanged(ServoUrl),
    Key(Option<char>, Key, constellation_msg::KeyModifiers),
}

pub type CompositorChannel = (Box<CompositorProxy + Send>, Box<CompositorReceiver>);

pub enum FollowLinkPolicy {
    FollowAnyLink,
    FollowOriginalDomain,
}

pub struct Servo {
    // FIXME: it's annoying that event for servo are named "WindowEvent"
    events_for_servo: RefCell<Vec<WindowEvent>>,
    servo_browser: RefCell<servo::Browser<ServoCallbacks>>,
    callbacks: Rc<ServoCallbacks>,
}

impl Servo {

    pub fn configure(url: &str) {
        let url = ServoUrl::parse(url).ok().unwrap(); // FIXME. What if fail?
        let mut opts = opts::default_opts();
        opts.headless = false;
        opts.url = Some(url);
        opts::set_defaults(opts);
        // FIXME: Pipeline creation fails is layout_threads pref not set
        PREFS.set("layout.threads", PrefValue::Number(1.0));
    }

    pub fn version(&self) -> String {
        servo_version()
    }

    pub fn new(geometry: DrawableGeometry,
               riser: EventLoopRiser,
               url: &str,
               follow_link_policy: FollowLinkPolicy)
               -> Servo {

        let url = ServoUrl::parse(url).ok().unwrap(); // FIXME. What if fail?

        let allowed_domain = match (follow_link_policy, url.domain()) {
            (FollowLinkPolicy::FollowOriginalDomain, Some(domain)) => {
                Some(domain.clone().to_owned())
            }
            _ => None
        };

        let callbacks = Rc::new(ServoCallbacks {
            event_queue: RefCell::new(Vec::new()),
            geometry: Cell::new(geometry),
            riser: riser,
            allowed_domain: allowed_domain,
        });

        let mut servo = servo::Browser::new(callbacks.clone());

        servo.handle_events(vec![WindowEvent::InitializeCompositing]);

        Servo {
            events_for_servo: RefCell::new(Vec::new()),
            servo_browser: RefCell::new(servo),
            callbacks: callbacks,
        }
    }

    pub fn get_events(&self) -> Vec<ServoEvent> {
        self.callbacks.get_events()
    }

    pub fn reload(&self) {
        let event = WindowEvent::Reload;
        self.events_for_servo.borrow_mut().push(event);
    }

    pub fn go_back(&self) {
        let event = WindowEvent::Navigation(WindowNavigateMsg::Back);
        self.events_for_servo.borrow_mut().push(event);
    }

    pub fn go_forward(&self) {
        let event = WindowEvent::Navigation(WindowNavigateMsg::Forward);
        self.events_for_servo.borrow_mut().push(event);
    }

    // pub fn perform_mouse_move(&self, x: i32, y: i32) {
    //     let event = WindowEvent::MouseWindowMoveEventClass(TypedPoint2D::new(x as f32, y as f32));
    //     self.events_for_servo.borrow_mut().push(event);
    // }

    pub fn perform_scroll(&self, x: i32, y: i32, dx: f32, dy: f32, phase: TouchPhase) {
        let scroll_location = webrender_traits::ScrollLocation::Delta(TypedPoint2D::new(dx, dy));
        let phase = match phase {
            TouchPhase::Started => TouchEventType::Down,
            TouchPhase::Moved => TouchEventType::Move,
            TouchPhase::Ended => TouchEventType::Up,
        };
        let event = WindowEvent::Scroll(scroll_location, TypedPoint2D::new(x, y), phase);
        self.events_for_servo.borrow_mut().push(event);
    }

    pub fn update_geometry(&self, geometry: DrawableGeometry) {
        self.callbacks.update_geometry(geometry);
        let event = WindowEvent::Resize(self.callbacks.framebuffer_size());
        self.events_for_servo.borrow_mut().push(event);
    }

    // pub fn perform_click(&self,
    //              x: i32,
    //              y: i32,
    //              org_x: i32,
    //              org_y: i32,
    //              element_state: WindowElementState,
    //              mouse_button: WindowMouseButton,
    //              mouse_down_button: Option<WindowMouseButton>) {
    //     use self::servo::script_traits::MouseButton;
    //     let max_pixel_dist = 10f64;
    //     let event = match element_state {
    //         WindowElementState::Pressed => {
    //             MouseWindowEvent::MouseDown(MouseButton::Left,
    //                                         TypedPoint2D::new(x as f32, y as f32))
    //         }
    //         WindowElementState::Released => {
    //             let mouse_up_event = MouseWindowEvent::MouseUp(MouseButton::Left,
    //                                                            TypedPoint2D::new(x as f32,
    //                                                                              y as f32));
    //             match mouse_down_button {
    //                 None => mouse_up_event,
    //                 Some(but) if mouse_button == but => {
    //                     // Same button
    //                     let pixel_dist = Point2D::new(org_x, org_y) - Point2D::new(x, y);
    //                     let pixel_dist =
    //                         ((pixel_dist.x * pixel_dist.x + pixel_dist.y * pixel_dist.y) as f64)
    //                             .sqrt();
    //                     if pixel_dist < max_pixel_dist {
    //                         self.events_for_servo
    //                             .borrow_mut()
    //                             .push(WindowEvent::MouseWindowEventClass(mouse_up_event));
    //                         MouseWindowEvent::Click(MouseButton::Left,
    //                                                 TypedPoint2D::new(x as f32, y as f32))
    //                     } else {
    //                         mouse_up_event
    //                     }
    //                 }
    //                 Some(_) => mouse_up_event,
    //             }
    //         }
    //     };
    //     self.events_for_servo.borrow_mut().push(WindowEvent::MouseWindowEventClass(event));
    // }

    pub fn sync(&self) {
        // FIXME: ports/glutin/window.rs uses mem::replace. Should we too?
        // See: https://doc.rust-lang.org/core/mem/fn.replace.html
        let mut events = self.events_for_servo.borrow_mut();
        let clone = events.drain(..).collect();
        self.servo_browser.borrow_mut().handle_events(clone);
    }
}

struct ServoCallbacks {
    event_queue: RefCell<Vec<ServoEvent>>,
    geometry: Cell<DrawableGeometry>,
    riser: EventLoopRiser,
    allowed_domain: Option<String>,
}

impl ServoCallbacks {
    fn get_events(&self) -> Vec<ServoEvent> {
        // FIXME: ports/glutin/window.rs uses mem::replace. Should we too?
        // See: https://doc.rust-lang.org/core/mem/fn.replace.html
        let mut events = self.event_queue.borrow_mut();
        let copy = events.drain(..).collect();
        copy
    }

    fn update_geometry(&self, geometry: DrawableGeometry) {
        self.geometry.set(geometry);
    }
}

impl WindowMethods for ServoCallbacks {
    fn prepare_for_composite(&self, _width: usize, _height: usize) -> bool {
        true
    }

    fn supports_clipboard(&self) -> bool {
        // FIXME
        false
    }

    fn allow_navigation(&self, url: ServoUrl) -> bool {
        let allow = self.allowed_domain
            .as_ref()
            .map(|domain| url.domain().unwrap() == domain)
            .unwrap_or(false);
        if !allow {
            self.event_queue.borrow_mut().push(ServoEvent::UnhandledURL(url));
        }
        allow
    }

    fn create_compositor_channel(&self) -> CompositorChannel {
        let (sender, receiver) = mpsc::channel();
        (box ShellCompositorProxy {
             sender: sender,
             riser: self.riser.clone(),
         } as Box<CompositorProxy + Send>,
         box receiver as Box<CompositorReceiver>)
    }

    fn hidpi_factor(&self) -> ScaleFactor<f32, DeviceIndependentPixel, DevicePixel> {
        let scale_factor = self.geometry.get().hidpi_factor;
        ScaleFactor::new(scale_factor)
    }

    fn framebuffer_size(&self) -> TypedSize2D<u32, DevicePixel> {
        let scale_factor = self.geometry.get().hidpi_factor as u32;
        let (width, height) = self.geometry.get().view_size;
        TypedSize2D::new(scale_factor * width, scale_factor * height)
    }

    fn window_rect(&self) -> TypedRect<u32, DevicePixel> {
        let scale_factor = self.geometry.get().hidpi_factor as u32;
        let mut size = self.framebuffer_size();

        let (top, right, bottom, left) = self.geometry.get().margins;
        let top = top * scale_factor;
        let right = right * scale_factor;
        let bottom = bottom * scale_factor;
        let left = left * scale_factor;

        size.height = size.height - top - bottom;
        size.width = size.width - left - right;

        TypedRect::new(TypedPoint2D::new(left, top), size)
    }

    fn size(&self) -> TypedSize2D<f32, DeviceIndependentPixel> {
        let (width, height) = self.geometry.get().view_size;
        TypedSize2D::new(width as f32, height as f32)
    }

    fn client_window(&self) -> (Size2D<u32>, Point2D<i32>) {
        let (width, height) = self.geometry.get().view_size;
        let (x, y) = self.geometry.get().position;
        (Size2D::new(width, height), Point2D::new(x as i32, y as i32))
    }

    // Events

    fn set_inner_size(&self, size: Size2D<u32>) {
        self.event_queue
            .borrow_mut()
            .push(ServoEvent::SetWindowInnerSize(size.width as u32, size.height as u32));
    }

    fn set_position(&self, point: Point2D<i32>) {
        self.event_queue.borrow_mut().push(ServoEvent::SetWindowPosition(point.x, point.y));
    }

    fn set_fullscreen_state(&self, state: bool) {
        self.event_queue.borrow_mut().push(ServoEvent::SetFullScreenState(state))
    }

    fn present(&self) {
        // FIXME: what is that for? Aren't we supposed to flush buffer everytime
        // CompositorProxy::send is called?
        self.event_queue.borrow_mut().push(ServoEvent::Present);
    }

    fn set_page_title(&self, title: Option<String>) {
        self.event_queue.borrow_mut().push(ServoEvent::TitleChanged(title));
    }

    fn status(&self, status: Option<String>) {
        self.event_queue.borrow_mut().push(ServoEvent::StatusChanged(status));
    }

    fn load_start(&self, can_go_back: bool, can_go_forward: bool) {
        self.event_queue.borrow_mut().push(ServoEvent::LoadStart(can_go_back, can_go_forward));
    }

    fn load_end(&self, can_go_back: bool, can_go_forward: bool, root: bool) {
        self.event_queue.borrow_mut().push(ServoEvent::LoadEnd(can_go_back, can_go_forward, root));
    }

    fn load_error(&self, _: NetError, url: String) {
        // FIXME: never called by servo
        self.event_queue.borrow_mut().push(ServoEvent::LoadError(url));
    }

    fn head_parsed(&self, url: ServoUrl) {
        self.event_queue.borrow_mut().push(ServoEvent::HeadParsed(url));
    }

    fn set_cursor(&self, cursor: ServoCursor) {
        println!("set_cursor: {:?}", cursor);
        self.event_queue.borrow_mut().push(ServoEvent::CursorChanged(cursor));
    }

    fn set_favicon(&self, url: ServoUrl) {
        self.event_queue.borrow_mut().push(ServoEvent::FaviconChanged(url));
    }

    fn handle_key(&self, ch: Option<char>, key: Key, mods: constellation_msg::KeyModifiers) {
        self.event_queue.borrow_mut().push(ServoEvent::Key(ch, key, mods));
    }
}

struct ShellCompositorProxy {
    sender: mpsc::Sender<compositor_thread::Msg>,
    riser: EventLoopRiser,
}

impl CompositorProxy for ShellCompositorProxy {
    fn send(&self, msg: compositor_thread::Msg) {
        if let Err(err) = self.sender.send(msg) {
            println!("Failed to send response ({}).", err);
        }
        self.riser.rise()
    }

    fn clone_compositor_proxy(&self) -> Box<CompositorProxy + Send> {
        box ShellCompositorProxy {
            sender: self.sender.clone(),
            riser: self.riser.clone(),
        } as Box<CompositorProxy + Send>
    }
}
<|endoftext|>


<commit_before>//
// base64n.rs - new base64 module
//
// The Base64 Alphabet
//
//  Value Encoding  Value Encoding  Value Encoding  Value Encoding
//      0 A            17 R            34 i            51 z
//      1 B            18 S            35 j            52 0
//      2 C            19 T            36 k            53 1
//      3 D            20 U            37 l            54 2
//      4 E            21 V            38 m            55 3
//      5 F            22 W            39 n            56 4
//      6 G            23 X            40 o            57 5
//      7 H            24 Y            41 p            58 6
//      8 I            25 Z            42 q            59 7
//      9 J            26 a            43 r            60 8
//     10 K            27 b            44 s            61 9
//     11 L            28 c            45 t            62 +
//     12 M            29 d            46 u            63 /
//     13 N            30 e            47 v
//     14 O            31 f            48 w         (pad) =
//     15 P            32 g            49 x
//     16 Q            33 h            50 y
//
// The "URL and Filename safe" Base 64 Alphabet
//
// Value Encoding  Value Encoding  Value Encoding  Value Encoding
//     0 A            17 R            34 i            51 z
//     1 B            18 S            35 j            52 0
//     2 C            19 T            36 k            53 1
//     3 D            20 U            37 l            54 2
//     4 E            21 V            38 m            55 3
//     5 F            22 W            39 n            56 4
//     6 G            23 X            40 o            57 5
//     7 H            24 Y            41 p            58 6
//     8 I            25 Z            42 q            59 7
//     9 J            26 a            43 r            60 8
//    10 K            27 b            44 s            61 9
//    11 L            28 c            45 t            62 - (minus)
//    12 M            29 d            46 u            63 _ (underline)
//    13 N            30 e            47 v
//    14 O            31 f            48 w
//    15 P            32 g            49 x
//    16 Q            33 h            50 y         (pad) =
//

use std;

import vec::len;

const PAD: u8 = 61u8;

iface enc {
    fn encode(dst: [mutable u8], src: [u8]);
    fn encode_u(dst: [mutable u8], src: [u8]);
    fn encode_bytes(src: [u8]) -> [u8];
    fn encode_bytes_u(src: [u8]) -> [u8];
    fn encode_str(src: str) -> str;
    fn encode_str_u(src: str) -> str;
    // FIXME `decode` and `decode_u` should return desired length of `dst`
    fn decode(dst: [mutable u8], src: [u8]);
    fn decode_u(dst: [mutable u8], src: [u8]);
    fn decode_bytes(src: [u8]) -> [u8];
    fn decode_bytes_u(src: [u8]) -> [u8];
}

fn mk() -> enc {
    type _enc = {table: [u8], table_u: [u8],
                 decode_map: [u8], decode_map_u: [u8]};

    impl of enc for _enc {
        fn encode(dst: [mutable u8], src: [u8]) {
            b64encode(self.table, dst, src);
        }
        fn encode_u(dst: [mutable u8], src: [u8]) {
            b64encode(self.table_u, dst, src);
        }
        fn encode_bytes(src: [u8]) -> [u8] {
            let dst_length = encoded_len(len(src));
            let dst = vec::to_mut(vec::from_elem(dst_length, 0u8));
            self.encode(dst, src);
            vec::from_mut(dst)
        }
        fn encode_bytes_u(src: [u8]) -> [u8] {
            let dst_length = encoded_len(len(src));
            let dst = vec::to_mut(vec::from_elem(dst_length, 0u8));
            self.encode_u(dst, src);
            vec::from_mut(dst)
        }
        fn encode_str(src: str) -> str {
            let src = str::bytes(src);
            str::from_bytes(self.encode_bytes(src))
        }
        fn encode_str_u(src: str) -> str {
            let src = str::bytes(src);
            str::from_bytes(self.encode_bytes_u(src))
        }
        fn decode(dst: [mutable u8], src: [u8]) {
            b64decode(self.decode_map, dst, src);
        }
        fn decode_u(dst: [mutable u8], src: [u8]) {
            b64decode(self.decode_map_u, dst, src);
        }
        fn decode_bytes(src: [u8]) -> [u8] { [] }
        fn decode_bytes_u(src: [u8]) -> [u8] { [] }
    }

    let table = vec::to_mut(vec::from_elem(64u, 0u8)), i = 0u8;
    u8::range(65u8, 91u8)  { |j| table[i] = j; i += 1u8; }
    u8::range(97u8, 123u8) { |j| table[i] = j; i += 1u8; }
    u8::range(48u8, 58u8)  { |j| table[i] = j; i += 1u8; }
    table[i] = 43u8; table[i + 1u8] = 47u8;

    let table_u = table;
    table_u[i] = 45u8; table_u[i + 1u8] = 95u8;

    let decode_map = vec::to_mut(vec::from_elem(256u, 0xff_u8));
    let decode_map_u = vec::to_mut(vec::from_elem(256u, 0xff_u8));

    i = 0u8;
    while i < 64u8 {
        decode_map[table[i]] = i as u8;
        i += 1u8;
    }

    i = 0u8;
    while i < 64u8 {
        decode_map_u[table_u[i]] = i as u8;
        i += 1u8;
    }

    {table: vec::from_mut(table),
     table_u: vec::from_mut(table_u),
     decode_map: vec::from_mut(decode_map),
     decode_map_u: vec::from_mut(decode_map_u)} as enc
}

fn encoded_len(src_length: uint) -> uint {
    (src_length + 2u) / 3u * 4u
}

fn decoded_len(src_length: uint) -> uint {
    src_length / 4u * 3u
}

fn b64encode(table: [u8], dst: [mutable u8], src: [u8]) {
    if len(src) == 0u {
        ret;
    }

    let src_length = len(src);
    let dst_length = len(dst);
    let dst_curr = 0u;
    let src_curr = 0u;

    if dst_length % 4u != 0u {
        fail "dst's length should be divisible by 4";
    }

    while src_length > 0u {
        dst[dst_curr + 0u] = 0u8;
        dst[dst_curr + 1u] = 0u8;
        dst[dst_curr + 2u] = 0u8;
        dst[dst_curr + 3u] = 0u8;

        if src_length == 1u {
            dst[dst_curr + 0u] |= (src[src_curr + 0u]) >> 2u8;
            dst[dst_curr + 1u] |= (src[src_curr + 0u] << 4u8) & 0x3f_u8;
        } else if src_length == 2u {
            dst[dst_curr + 0u] |= (src[src_curr + 0u]) >> 2u8;
            dst[dst_curr + 1u] |= (src[src_curr + 0u] << 4u8) & 0x3f_u8;
            dst[dst_curr + 1u] |= (src[src_curr + 1u] >> 4u8);
            dst[dst_curr + 2u] |= (src[src_curr + 1u] << 2u8) & 0x3f_u8;
        } else {
            dst[dst_curr + 0u] |= (src[src_curr + 0u]) >> 2u8;
            dst[dst_curr + 1u] |= (src[src_curr + 0u] << 4u8) & 0x3f_u8;
            dst[dst_curr + 1u] |= (src[src_curr + 1u] >> 4u8);
            dst[dst_curr + 2u] |= (src[src_curr + 1u] << 2u8) & 0x3f_u8;
            dst[dst_curr + 2u] |= (src[src_curr + 2u] >> 6u8);
            dst[dst_curr + 3u] |= (src[src_curr + 2u]) & 0x3f_u8;
        }

        dst[dst_curr + 0u] = table[dst[dst_curr + 0u]];
        dst[dst_curr + 1u] = table[dst[dst_curr + 1u]];
        dst[dst_curr + 2u] = table[dst[dst_curr + 2u]];
        dst[dst_curr + 3u] = table[dst[dst_curr + 3u]];

        if src_length < 3u {
            dst[dst_curr + 3u] = PAD;
            if src_length < 2u {
                dst[dst_curr + 2u] = PAD;
            }
            break;
        }

        src_length -= 3u;
        src_curr += 3u;
        dst_curr += 4u;
    }
}

fn b64decode(table: [u8], dst: [mutable u8], src: [u8]) {
    // FIXME write
}

#[cfg(test)]
mod tests {
    #[test]
    fn test_encode_bytes() {
        let src = [102u8, 111u8, 111u8, 98u8, 97u8, 114u8];
        let enc = mk();
        let res = enc.encode_bytes(src);
        assert res == [90u8, 109u8, 57u8, 118u8, 89u8, 109u8, 70u8, 121u8];
    }
    #[test]
    fn test_encode_str() {
        let src = "foobar";
        let enc = mk();
        let res = enc.encode_str(src);
        assert res == "Zm9vYmFy";
    }
    #[test]
    fn test_encode_bytes_u() {
        let src = [102u8, 111u8, 111u8, 98u8, 97u8, 63u8];
        let enc = mk();
        let res = enc.encode_bytes_u(src);
        assert res == [90u8, 109u8, 57u8, 118u8, 89u8, 109u8, 69u8, 95u8];
    }
    #[test]
    fn test_encode_str_u() {
        let src = "fooba?";
        let enc = mk();
        let res = enc.encode_str_u(src);
        assert res == "Zm9vYmE_";
    }
}
<commit_msg>wip: base64n.rs<commit_after>//
// base64n.rs - new base64 module
//
// The Base64 Alphabet
//
//  Value Encoding  Value Encoding  Value Encoding  Value Encoding
//      0 A            17 R            34 i            51 z
//      1 B            18 S            35 j            52 0
//      2 C            19 T            36 k            53 1
//      3 D            20 U            37 l            54 2
//      4 E            21 V            38 m            55 3
//      5 F            22 W            39 n            56 4
//      6 G            23 X            40 o            57 5
//      7 H            24 Y            41 p            58 6
//      8 I            25 Z            42 q            59 7
//      9 J            26 a            43 r            60 8
//     10 K            27 b            44 s            61 9
//     11 L            28 c            45 t            62 +
//     12 M            29 d            46 u            63 /
//     13 N            30 e            47 v
//     14 O            31 f            48 w         (pad) =
//     15 P            32 g            49 x
//     16 Q            33 h            50 y
//
// The "URL and Filename safe" Base 64 Alphabet
//
// Value Encoding  Value Encoding  Value Encoding  Value Encoding
//     0 A            17 R            34 i            51 z
//     1 B            18 S            35 j            52 0
//     2 C            19 T            36 k            53 1
//     3 D            20 U            37 l            54 2
//     4 E            21 V            38 m            55 3
//     5 F            22 W            39 n            56 4
//     6 G            23 X            40 o            57 5
//     7 H            24 Y            41 p            58 6
//     8 I            25 Z            42 q            59 7
//     9 J            26 a            43 r            60 8
//    10 K            27 b            44 s            61 9
//    11 L            28 c            45 t            62 - (minus)
//    12 M            29 d            46 u            63 _ (underline)
//    13 N            30 e            47 v
//    14 O            31 f            48 w
//    15 P            32 g            49 x
//    16 Q            33 h            50 y         (pad) =
//

use std;

import vec::len;

const PAD: u8 = 61u8;

iface enc {
    fn encode(dst: [mutable u8], src: [u8]);
    fn encode_u(dst: [mutable u8], src: [u8]);
    fn encode_bytes(src: [u8]) -> [u8];
    fn encode_bytes_u(src: [u8]) -> [u8];
    fn encode_str(src: str) -> str;
    fn encode_str_u(src: str) -> str;
    // FIXME `decode` and `decode_u` should return desired length of `dst`
    fn decode(dst: [mutable u8], src: [u8]);
    fn decode_u(dst: [mutable u8], src: [u8]);
    fn decode_bytes(src: [u8]) -> [u8];
    fn decode_bytes_u(src: [u8]) -> [u8];
}

fn mk() -> enc {
    type _enc = {table: [u8], table_u: [u8],
                 decode_map: [u8], decode_map_u: [u8]};

    impl of enc for _enc {
        fn encode(dst: [mutable u8], src: [u8]) {
            b64encode(self.table, dst, src);
        }
        fn encode_u(dst: [mutable u8], src: [u8]) {
            b64encode(self.table_u, dst, src);
        }
        fn encode_bytes(src: [u8]) -> [u8] {
            let dst_length = encoded_len(len(src));
            let dst = vec::to_mut(vec::from_elem(dst_length, 0u8));
            self.encode(dst, src);
            vec::from_mut(dst)
        }
        fn encode_bytes_u(src: [u8]) -> [u8] {
            let dst_length = encoded_len(len(src));
            let dst = vec::to_mut(vec::from_elem(dst_length, 0u8));
            self.encode_u(dst, src);
            vec::from_mut(dst)
        }
        fn encode_str(src: str) -> str {
            let src = str::bytes(src);
            str::from_bytes(self.encode_bytes(src))
        }
        fn encode_str_u(src: str) -> str {
            let src = str::bytes(src);
            str::from_bytes(self.encode_bytes_u(src))
        }
        fn decode(dst: [mutable u8], src: [u8]) {
            b64decode(self.decode_map, dst, src);
        }
        fn decode_u(dst: [mutable u8], src: [u8]) {
            b64decode(self.decode_map_u, dst, src);
        }
        fn decode_bytes(src: [u8]) -> [u8] { [] }
        fn decode_bytes_u(src: [u8]) -> [u8] { [] }
    }

    let table = vec::to_mut(vec::from_elem(64u, 0u8)), i = 0u8;
    u8::range(65u8, 91u8)  { |j| table[i] = j; i += 1u8; }
    u8::range(97u8, 123u8) { |j| table[i] = j; i += 1u8; }
    u8::range(48u8, 58u8)  { |j| table[i] = j; i += 1u8; }
    table[i] = 43u8; table[i + 1u8] = 47u8;

    let table_u = table;
    table_u[i] = 45u8; table_u[i + 1u8] = 95u8;

    let decode_map = vec::to_mut(vec::from_elem(256u, 0xff_u8));
    let decode_map_u = vec::to_mut(vec::from_elem(256u, 0xff_u8));

    i = 0u8;
    while i < 64u8 {
        decode_map[table[i]] = i as u8;
        i += 1u8;
    }

    i = 0u8;
    while i < 64u8 {
        decode_map_u[table_u[i]] = i as u8;
        i += 1u8;
    }

    {table: vec::from_mut(table),
     table_u: vec::from_mut(table_u),
     decode_map: vec::from_mut(decode_map),
     decode_map_u: vec::from_mut(decode_map_u)} as enc
}

fn encoded_len(src_length: uint) -> uint {
    (src_length + 2u) / 3u * 4u
}

fn decoded_len(src_length: uint) -> uint {
    src_length / 4u * 3u
}

fn b64encode(table: [u8], dst: [mutable u8], src: [u8]) {
    if len(src) == 0u {
        ret;
    }

    let src_length = len(src);
    let dst_length = len(dst);
    let dst_curr = 0u;
    let src_curr = 0u;

    if dst_length % 4u != 0u {
        fail "dst's length should be divisible by 4";
    }

    while src_length > 0u {
        dst[dst_curr + 0u] = 0u8;
        dst[dst_curr + 1u] = 0u8;
        dst[dst_curr + 2u] = 0u8;
        dst[dst_curr + 3u] = 0u8;

        if src_length == 1u {
            dst[dst_curr + 0u] |= (src[src_curr + 0u]) >> 2u8;
            dst[dst_curr + 1u] |= (src[src_curr + 0u] << 4u8) & 0x3f_u8;
        } else if src_length == 2u {
            dst[dst_curr + 0u] |= (src[src_curr + 0u]) >> 2u8;
            dst[dst_curr + 1u] |= (src[src_curr + 0u] << 4u8) & 0x3f_u8;
            dst[dst_curr + 1u] |= (src[src_curr + 1u] >> 4u8);
            dst[dst_curr + 2u] |= (src[src_curr + 1u] << 2u8) & 0x3f_u8;
        } else {
            dst[dst_curr + 0u] |= (src[src_curr + 0u]) >> 2u8;
            dst[dst_curr + 1u] |= (src[src_curr + 0u] << 4u8) & 0x3f_u8;
            dst[dst_curr + 1u] |= (src[src_curr + 1u] >> 4u8);
            dst[dst_curr + 2u] |= (src[src_curr + 1u] << 2u8) & 0x3f_u8;
            dst[dst_curr + 2u] |= (src[src_curr + 2u] >> 6u8);
            dst[dst_curr + 3u] |= (src[src_curr + 2u]) & 0x3f_u8;
        }

        dst[dst_curr + 0u] = table[dst[dst_curr + 0u]];
        dst[dst_curr + 1u] = table[dst[dst_curr + 1u]];
        dst[dst_curr + 2u] = table[dst[dst_curr + 2u]];
        dst[dst_curr + 3u] = table[dst[dst_curr + 3u]];

        if src_length < 3u {
            dst[dst_curr + 3u] = PAD;
            if src_length < 2u {
                dst[dst_curr + 2u] = PAD;
            }
            break;
        }

        src_length -= 3u;
        src_curr += 3u;
        dst_curr += 4u;
    }
}

fn b64decode(decode_map: [u8], dst: [mutable u8], src: [u8]) {
    // FIXME write
}

#[cfg(test)]
mod tests {
    #[test]
    fn test_encode_bytes() {
        let src = [102u8, 111u8, 111u8, 98u8, 97u8, 114u8];
        let enc = mk();
        let res = enc.encode_bytes(src);
        assert res == [90u8, 109u8, 57u8, 118u8, 89u8, 109u8, 70u8, 121u8];
    }
    #[test]
    fn test_encode_str() {
        let src = "foobar";
        let enc = mk();
        let res = enc.encode_str(src);
        assert res == "Zm9vYmFy";
    }
    #[test]
    fn test_encode_bytes_u() {
        let src = [102u8, 111u8, 111u8, 98u8, 97u8, 63u8];
        let enc = mk();
        let res = enc.encode_bytes_u(src);
        assert res == [90u8, 109u8, 57u8, 118u8, 89u8, 109u8, 69u8, 95u8];
    }
    #[test]
    fn test_encode_str_u() {
        let src = "fooba?";
        let enc = mk();
        let res = enc.encode_str_u(src);
        assert res == "Zm9vYmE_";
    }
}
<|endoftext|>