<commit_before>use std::rand::random;
use std::bool;
use graphics::Graphics;
/* CPU, Graphics and Memory core */

mod graphics;

//enum mode = {SCHIP, CHIP}; /* differentiate between CHIP and SCHIP modes */

static U12_MAX : u16 = 4096;
static FLAG : uint = 15;

struct CPU {
    registers : [u8, ..16], /* 16 8 bit general purpose registers */
    mem : [u8, .. 0xFFF], /* 4096bytes of memory */
    I : u16, /* 16 bit index register */
    pc: u16, /* Program counter */
    sp: uint, /* Stack Pointer */
    stack : [u16, ..16], /* 16 stack frames */
    sound_timer : u8,
    delay_timer : u8,
    hp_48_flags: [u16, ..8], /*SCHIP */
    opcode : u16,
    graphics :Graphics

}

impl CPU {

    fn new(mut mem: Vec<u8>) -> CPU {
        let mut cpu = CPU { registers: [0u8, ..16],
              mem: [0u8, ..0xFFF],
              I: 0,
              pc: 0x200,
              sp: 1,
              stack : [0u16, ..16],
              sound_timer: 0,
              delay_timer: 0,
              hp_48_flags: [0u16, ..8],
              opcode : 0,
              graphics : Graphics::new()
       };

       /* Move values out of supplied vector and into
        * CPU mem */
       mem.reverse(); /* reverse vector first as poping off the end (O(1))
                         is much cheaper than shifting from the front (O(n)) */
       for val in cpu.mem.mut_iter() {
            *val = match mem.pop() {
                    Some(item) => item,
                    None => 0x0
            };
       }

       return cpu;
    }

    fn to_addr(dig1 :u8, dig2 :u8, dig3 :u8) -> u16 {
        ((dig1 << 8) | (dig2 << 4) | dig3) as u16
    }

    fn to_val(dig1 :u8, dig2 :u8) -> u8 {
        (dig1 << 4) | dig2
    }

    fn u16_to_hex_vec(hex :u16) -> (u8, u8, u8, u8) {
         (((hex & 0xF000) >> 12) as u8,
          ((hex & 0x0F00) >> 8)  as u8,
          ((hex & 0x00F0) >> 4)  as u8,
           (hex & 0x000F)        as u8)
    }

    fn get_opcode(&self) -> u16 {
        (self.registers[self.pc as uint] << 4) as u16 |
        (self.registers[(self.pc + 1) as uint]) as u16
    }

    fn perform_cycle(&mut self) {

        let opcode = self.get_opcode();
        let opcode_v =  CPU::u16_to_hex_vec(opcode);
        self.inc_pc();

        match opcode_v {
            (0x0, 0x0 ,0xE, 0x0) => self.clear_screen(),
            (0x0, 0x0, 0xE, 0xE) => self.ret(),
            (0x0, N1, N2, N3) => {},
            (0x1, N1, N2, N3) => self.jump(CPU::to_addr(N1, N2, N3)),
            (0x2, N1, N2, N3) => self.call(CPU::to_addr(N1, N2, N3)),
            (0x3, X, N1, N2) => self.skip_equals_reg_val(X, CPU::to_val(N1, N2)),
            (0x4, X, N1, N2) => self.skip_not_equals_reg_val(X, CPU::to_val(N1, N2)),
            (0x5, X, Y, 0x0) => self.skip_equals_regs(X, Y),
            (0x6, X, N1, N2) => self.mov_reg_val(X, CPU::to_val(N1, N2)),
            (0x7, X, N1, N2) => self.add_reg_val(X, CPU::to_val(N1, N2)),
            (0x8, X, Y, 0x0) => self.mov_regs(X, Y),
            (0x8, X, Y, 0x1) => self.or_regs(X, Y),
            (0x8, X, Y, 0x2) => self.and_regs(X, Y),
            (0x8, X, Y, 0x3) => self.xor_regs(X, Y),
            (0x8, X, Y, 0x4) => self.add_regs(X, Y),
            (0x8, X, Y, 0x5) => self.sub_regs(X, Y),
            (0x8, X, _, 0x6) => self.shift_right(X),
            (0x8, X, Y, 0x7) => self.sub_regs(Y, X),
            (0x8, X, _, 0xE) => self.shift_left(X),
            (0x9, X, Y, 0x0) => self.skip_not_equals_regs(X, Y),
            (0xA, N1, N2, N3) => self.set_i(CPU::to_addr(N1, N2, N3)),
            (0xB, N1, N2, N3) => self.jump_val_reg0(CPU::to_addr(N1, N2, N3)),
            (0xC, X, N1, N2) => self.rand(X, CPU::to_val(N1, N2)),
            (0xD, X, Y, N) => {},
            (0xE, X, 0x9, 0xE) => {},
            (0xE, X, 0xA, 0x1) => {},
            (0xF, X, 0x0, 0x7) => {},
            (0xF, X, 0x0, 0xA) => {},
            (0xF, X, 0x1, 0x5) => self.set_reg_delay(X),
            (0xF, X, 0x1, 0x8) => self.set_reg_sound(X),
            (0xF, X, 0x1, 0xE) => self.add_reg_index(X),
            (0xF, X, 0x2, 0x9) =>  {},
            (0xF, X, 0x3, 0x3) => self.binary_decimal(X),
            (0xF, X, 0x5, 0x5) => self.store_regs(X),
            (0xF, X, 0x6, 0x5) => self.load_regs(X),
            _ => fail!("Unknown opcode {:x}",opcode)
        }
    }

    fn reset(&mut self) {
        self.pc = 0x200;
        self.sp = 0;
    }

    fn pop(&mut self) -> u16 {
        let item = self.stack[self.sp];
        self.sp -= 1;
        return item;

    }

    fn push(&mut self, val:u16) {
        self.sp += 1;
        self.stack[self.sp] = val;
    }

    fn inc_pc(&mut self) {
        self.pc += 2;
    }

    /* Jump to address at 0NNN */
    fn call(&mut self, addr:u16) {
        self.pc = addr & 0xFFF;
    }

    /* Clear the screen */
    fn clear_screen(&mut self) {

    }

    /* Return from a subroutine */
    fn ret(&mut self) {
        self.pc = self.pop();
    }

    fn jump(&mut self, addr: u16) {
        self.pc = addr & 0xFFF;
    }

    /* Skip next instruction if register X is equal to NN */
    fn skip_equals_reg_val(&mut self, reg:u8, val:u8) {
        if self.registers[reg as uint] == val {
            self.inc_pc();
        }

    }

    fn skip_not_equals_reg_val(&mut self, reg:u8, val:u8) {
        if self.registers[reg as uint] != val {
            self.inc_pc();
        }
    }

    fn skip_equals_regs(&mut self, reg1:u8, reg2:u8) {

        if self.registers[reg1 as uint] == self.registers[reg2 as uint] {
               self.inc_pc();
        }
    }

    fn skip_not_equals_regs(&mut self, reg1:u8, reg2:u8) {
        if self.registers[reg1 as uint] != self.registers[reg2 as uint] {
               self.inc_pc();
           }
    }

    fn mov_reg_val(&mut self, reg:u8, val:u8) {
        self.registers[reg as uint] = val;
    }

    fn add_reg_val(&mut self, reg:u8, val:u8) {
        self.registers[reg as uint] += val;
    }

    fn mov_regs(&mut self, reg1:u8, reg2:u8) {
        self.registers[reg1 as uint] = self.registers[reg2 as uint];
    }

    fn or_regs(&mut self, reg1:u8, reg2:u8) {
       self.registers[reg1 as uint] |= self.registers[reg2 as uint];
    }

    fn and_regs(&mut self, reg1:u8, reg2:u8) {
        self.registers[reg1 as uint] |= self.registers[reg2 as uint];
    }

    fn xor_regs(&mut self, reg1:u8, reg2:u8) {
        self.registers[reg1 as uint] ^= self.registers[reg2 as uint];
    }

    /* Add two regs, if overflow set flag register otherwise
     * unset flag register */
    fn add_regs(&mut self, reg1:u8, reg2:u8) {
        self.registers[FLAG] =  bool::to_bit::<u8>(0xFF - reg1 > reg2);
        self.registers[reg1 as uint] += self.registers[reg2 as uint];
    }

    /* Sub reg2 from reg1, unset flag register if underflow,
     * set otherwise */
    fn sub_regs(&mut self, reg1:u8, reg2:u8) {
        self.registers[FLAG] = bool::to_bit::<u8>(reg2 < reg1);
        self.registers[reg1 as uint] -= self.registers[reg2 as uint];
    }

    /* Sub reg1 from reg2, unset flag register if underflow,
     * set otherwise */
    fn sub_regs_inv(&mut self, reg1:u8, reg2:u8) {
        self.registers[FLAG] = bool::to_bit::<u8>(reg1 < reg2);
        self.registers[reg2 as uint] -= self.registers[reg1 as uint];
    }

    /* Shift register left by 1, set flag register to most significant bit
     * before shifting */
    fn shift_left(&mut self, reg:u8) {
        self.registers[FLAG] = (self.registers[reg as uint] & 0x80) >> 7;
        self.registers[reg as uint] <<= 1;
    }

    /* Shift register right by 1, set flag register to least significant bit
     * before shifting */
    fn shift_right(&mut self, reg:u8) {
        self.registers[FLAG] = self.registers[reg as uint] & 0x1;
        self.registers[reg as uint] >>= 1;
    }

    /* Set index register to address in opcode */
    fn set_i(&mut self, addr:u16) {
        self.I = addr;
    }

    fn jump_val_reg0(&mut self, addr:u16) {
        self.pc = self.registers[0] as u16 + addr;
    }

    fn rand(&mut self, reg:u8, val:u8) {
        self.registers[reg as uint] = val & random::<u8>();
    }

    fn set_reg_delay(&mut self, reg:u8) {
        self.registers[reg as uint] = self.delay_timer;
    }

    fn set_reg_sound(&mut self, reg:u8) {
        self.registers[reg as uint] = self.sound_timer;
    }

    fn add_reg_index(&mut self, reg:u8) {
        self.registers[FLAG] =
            bool::to_bit::<u8>(0xFFF - (self.registers[reg as uint] as u16) < self.I);
        self.I += self.registers[reg as uint] as u16
    }

    fn store_regs(&mut self, reg:u8) {
        let mut addr = self.I;
        for reg in self.registers.iter() {
            self.mem[addr as uint] = *reg;
            addr += 1;
        }
    }

    fn load_regs(&mut self, reg:u8) {
        let mut addr = self.I;
        for reg in self.registers.mut_iter() {
            *reg = self.mem[addr as uint];
            addr += 1;
        }
    }

    /*stores the Binary-coded decimal representation of VX, with the
     * most significant of three digits at the address in I, the middle digit
     * at I + 1, and the lsg at I + 2.*/
    fn binary_decimal(&mut self, reg:u8) {
        let val = self.registers[reg as uint];
        self.mem[self.I as uint] = val/100;
        self.mem[(self.I + 1) as uint] = (val % 100)/10;
        self.mem[(self.I + 2) as uint] = (val % 100)%10;

    }

}

fn main() {

    print!("yay");
}

<commit_msg>Added new store/load operations using iterators<commit_after>use std::rand::random;
use std::bool;
use graphics::Graphics;
/* CPU, Graphics and Memory core */

mod graphics;

//enum mode = {SCHIP, CHIP}; /* differentiate between CHIP and SCHIP modes */

static MAX_RAM : u16 = 4096;
static FLAG : uint = 15;

struct CPU {
    registers : [u8, ..16], /* 16 8 bit general purpose registers */
    mem : [u8, .. MAX_RAM], /* 4096bytes of memory */
    I : u16, /* 16 bit index register */
    pc: u16, /* Program counter */
    sp: uint, /* Stack Pointer */
    stack : [u16, ..16], /* 16 stack frames */
    sound_timer : u8,
    delay_timer : u8,
    hp_48_flags: [u16, ..8], /*SCHIP */
    opcode : u16,
    graphics :Graphics

}

impl CPU {

    fn new(mut mem: Vec<u8>) -> CPU {
        let mut cpu = CPU { registers: [0u8, ..16],
              mem: [0u8, ..MAX_RAM],
              I: 0,
              pc: 0x200,
              sp: 1,
              stack : [0u16, ..16],
              sound_timer: 0,
              delay_timer: 0,
              hp_48_flags: [0u16, ..8],
              opcode : 0,
              graphics : Graphics::new()
       };

       /* Move values out of supplied vector and into
        * CPU mem */
       mem.reverse(); /* reverse vector first as poping off the end (O(1))
                         is much cheaper than shifting from the front (O(n)) */
       for val in cpu.mem.mut_iter() {
            *val = match mem.pop() {
                    Some(item) => item,
                    None => 0x0
            };
       }

       return cpu;
    }

    fn to_addr(dig1 :u8, dig2 :u8, dig3 :u8) -> u16 {
        ((dig1 << 8) | (dig2 << 4) | dig3) as u16
    }

    fn to_val(dig1 :u8, dig2 :u8) -> u8 {
        (dig1 << 4) | dig2
    }

    fn u16_to_hex_vec(hex :u16) -> (u8, u8, u8, u8) {
         (((hex & 0xF000) >> 12) as u8,
          ((hex & 0x0F00) >> 8)  as u8,
          ((hex & 0x00F0) >> 4)  as u8,
           (hex & 0x000F)        as u8)
    }

    fn get_opcode(&self) -> u16 {
        (self.registers[self.pc as uint] << 4) as u16 |
        (self.registers[(self.pc + 1) as uint]) as u16
    }

    fn perform_cycle(&mut self) {

        let opcode = self.get_opcode();
        let opcode_v =  CPU::u16_to_hex_vec(opcode);
        self.inc_pc();

        match opcode_v {
            (0x0, 0x0 ,0xE, 0x0) => self.clear_screen(),
            (0x0, 0x0, 0xE, 0xE) => self.ret(),
            (0x0, N1, N2, N3) => {},
            (0x1, N1, N2, N3) => self.jump(CPU::to_addr(N1, N2, N3)),
            (0x2, N1, N2, N3) => self.call(CPU::to_addr(N1, N2, N3)),
            (0x3, X, N1, N2) => self.skip_equals_reg_val(X, CPU::to_val(N1, N2)),
            (0x4, X, N1, N2) => self.skip_not_equals_reg_val(X, CPU::to_val(N1, N2)),
            (0x5, X, Y, 0x0) => self.skip_equals_regs(X, Y),
            (0x6, X, N1, N2) => self.mov_reg_val(X, CPU::to_val(N1, N2)),
            (0x7, X, N1, N2) => self.add_reg_val(X, CPU::to_val(N1, N2)),
            (0x8, X, Y, 0x0) => self.mov_regs(X, Y),
            (0x8, X, Y, 0x1) => self.or_regs(X, Y),
            (0x8, X, Y, 0x2) => self.and_regs(X, Y),
            (0x8, X, Y, 0x3) => self.xor_regs(X, Y),
            (0x8, X, Y, 0x4) => self.add_regs(X, Y),
            (0x8, X, Y, 0x5) => self.sub_regs(X, Y),
            (0x8, X, _, 0x6) => self.shift_right(X),
            (0x8, X, Y, 0x7) => self.sub_regs(Y, X),
            (0x8, X, _, 0xE) => self.shift_left(X),
            (0x9, X, Y, 0x0) => self.skip_not_equals_regs(X, Y),
            (0xA, N1, N2, N3) => self.set_i(CPU::to_addr(N1, N2, N3)),
            (0xB, N1, N2, N3) => self.jump_val_reg0(CPU::to_addr(N1, N2, N3)),
            (0xC, X, N1, N2) => self.rand(X, CPU::to_val(N1, N2)),
            (0xD, X, Y, N) => {},
            (0xE, X, 0x9, 0xE) => {},
            (0xE, X, 0xA, 0x1) => {},
            (0xF, X, 0x0, 0x7) => {},
            (0xF, X, 0x0, 0xA) => {},
            (0xF, X, 0x1, 0x5) => self.set_reg_delay(X),
            (0xF, X, 0x1, 0x8) => self.set_reg_sound(X),
            (0xF, X, 0x1, 0xE) => self.add_reg_index(X),
            (0xF, X, 0x2, 0x9) =>  {},
            (0xF, X, 0x3, 0x3) => self.binary_decimal(X),
            (0xF, X, 0x5, 0x5) => self.store_regs(X),
            (0xF, X, 0x6, 0x5) => self.load_regs(X),
            _ => fail!("Unknown opcode {:x}",opcode)
        }
    }

    fn reset(&mut self) {
        self.pc = 0x200;
        self.sp = 0;
    }

    fn pop(&mut self) -> u16 {
        let item = self.stack[self.sp];
        self.sp -= 1;
        return item;

    }

    fn push(&mut self, val:u16) {
        self.sp += 1;
        self.stack[self.sp] = val;
    }

    fn inc_pc(&mut self) {
        self.pc += 2;
        self.pc %= MAX_RAM;
    }

    /* Jump to address at 0NNN */
    fn call(&mut self, addr:u16) {
        self.pc = addr & 0xFFF;
    }

    /* Clear the screen */
    fn clear_screen(&mut self) {

    }

    /* Return from a subroutine */
    fn ret(&mut self) {
        self.pc = self.pop();
    }

    fn jump(&mut self, addr: u16) {
        self.pc = addr;
    }

    /* Skip next instruction if register X is equal to NN */
    fn skip_equals_reg_val(&mut self, reg:u8, val:u8) {
        if self.registers[reg as uint] == val {
            self.inc_pc();
        }

    }

    fn skip_not_equals_reg_val(&mut self, reg:u8, val:u8) {
        if self.registers[reg as uint] != val {
            self.inc_pc();
        }
    }

    fn skip_equals_regs(&mut self, reg1:u8, reg2:u8) {

        if self.registers[reg1 as uint] == self.registers[reg2 as uint] {
               self.inc_pc();
        }
    }

    fn skip_not_equals_regs(&mut self, reg1:u8, reg2:u8) {
        if self.registers[reg1 as uint] != self.registers[reg2 as uint] {
               self.inc_pc();
           }
    }

    fn mov_reg_val(&mut self, reg:u8, val:u8) {
        self.registers[reg as uint] = val;
    }

    fn add_reg_val(&mut self, reg:u8, val:u8) {
        self.registers[reg as uint] += val;
    }

    fn mov_regs(&mut self, reg1:u8, reg2:u8) {
        self.registers[reg1 as uint] = self.registers[reg2 as uint];
    }

    fn or_regs(&mut self, reg1:u8, reg2:u8) {
       self.registers[reg1 as uint] |= self.registers[reg2 as uint];
    }

    fn and_regs(&mut self, reg1:u8, reg2:u8) {
        self.registers[reg1 as uint] |= self.registers[reg2 as uint];
    }

    fn xor_regs(&mut self, reg1:u8, reg2:u8) {
        self.registers[reg1 as uint] ^= self.registers[reg2 as uint];
    }

    /* Add two regs, if overflow set flag register otherwise
     * unset flag register */
    fn add_regs(&mut self, reg1:u8, reg2:u8) {
        self.registers[FLAG] =  bool::to_bit::<u8>(0xFF - reg1 > reg2);
        self.registers[reg1 as uint] += self.registers[reg2 as uint];
    }

    /* Sub reg2 from reg1, unset flag register if underflow,
     * set otherwise */
    fn sub_regs(&mut self, reg1:u8, reg2:u8) {
        self.registers[FLAG] = bool::to_bit::<u8>(reg2 < reg1);
        self.registers[reg1 as uint] -= self.registers[reg2 as uint];
    }

    /* Sub reg1 from reg2, unset flag register if underflow,
     * set otherwise */
    fn sub_regs_inv(&mut self, reg1:u8, reg2:u8) {
        self.registers[FLAG] = bool::to_bit::<u8>(reg1 < reg2);
        self.registers[reg2 as uint] -= self.registers[reg1 as uint];
    }

    /* Shift register left by 1, set flag register to most significant bit
     * before shifting */
    fn shift_left(&mut self, reg:u8) {
        self.registers[FLAG] = (self.registers[reg as uint] & 0x80) >> 7;
        self.registers[reg as uint] <<= 1;
    }

    /* Shift register right by 1, set flag register to least significant bit
     * before shifting */
    fn shift_right(&mut self, reg:u8) {
        self.registers[FLAG] = self.registers[reg as uint] & 0x1;
        self.registers[reg as uint] >>= 1;
    }

    /* Set index register to address in opcode */
    fn set_i(&mut self, addr:u16) {
        self.I = addr;
    }

    fn jump_val_reg0(&mut self, addr:u16) {
        self.pc = self.registers[0] as u16 + addr;
    }

    fn rand(&mut self, reg:u8, val:u8) {
        self.registers[reg as uint] = val & random::<u8>();
    }

    fn set_reg_delay(&mut self, reg:u8) {
        self.registers[reg as uint] = self.delay_timer;
    }

    fn set_reg_sound(&mut self, reg:u8) {
        self.registers[reg as uint] = self.sound_timer;
    }

    fn add_reg_index(&mut self, reg:u8) {
        self.registers[FLAG] =
            bool::to_bit::<u8>(0xFFF - (self.registers[reg as uint] as u16) < self.I);
        self.I += self.registers[reg as uint] as u16
    }

    fn store_regs(&mut self, max_reg:u8) {
        let mut addr = self.I;
        for reg in self.registers.slice_to(max_reg as uint + 1).iter() {
            self.mem[addr as uint] = *reg;
            addr += 1;
            addr %= MAX_RAM;
        }
    }

    fn load_regs(&mut self, max_reg:u8) {
        let mut addr = self.I;
        for reg in self.registers.mut_slice_to(max_reg as uint +1).mut_iter() {
            *reg = self.mem[addr as uint];
            addr += 1;
            addr %= MAX_RAM;
        }
    }

    fn store_regs2(&mut self, max_reg:u8) {
        let regs = self.registers.slice_to(max_reg as uint + 1).iter();
        let store = self.mem.mut_slice_from(self.I as uint).mut_iter();
        for (mem, reg) in store.zip(regs) {
            *mem = *reg;
        }

    }

    fn load_regs2(&mut self, max_reg:u8) {
        let regs = self.registers.mut_slice_to(max_reg as uint + 1).mut_iter();
        let store = self.mem.slice_from(self.I as uint).iter();
        for (mem, reg) in store.zip(regs) {
            *reg = *mem;
        }
   }

    /*stores the Binary-coded decimal representation of VX, with the
     * most significant of three digits at the address in I, the middle digit
     * at I + 1, and the lsg at I + 2.*/
    fn binary_decimal(&mut self, reg:u8) {
        let val = self.registers[reg as uint];
        self.mem[self.I as uint] = val/100;
        self.mem[(self.I + 1) as uint] = (val % 100)/10;
        self.mem[(self.I + 2) as uint] = (val % 100)%10;

    }

}

fn main() {

    print!("yay");
}

<|endoftext|>


<commit_before>use crate::image::Image;
use crate::Render;

extern crate crossterm;

use crossterm::{cursor, terminal, ClearType };

const TITLE: &str = "ASCII-ART HANGMAN FOR KIDS";

#[derive(Debug)]
pub struct UserInterface {
    pub image: Image,
    pub message: String,
}

impl Render for UserInterface {
    fn render(&self) {
        // Clear all lines in terminal;
        let terminal = terminal();
        terminal.clear(ClearType::All).expect("Can not clear terminal.");
        cursor().goto(0, 0).expect("Can not set curson position.");
        terminal.write(&TITLE).expect("Can not write on terminal");
        terminal.write("\n").expect("Can not write on terminal");
        &self.image.render();
        terminal.write("\n\n").expect("Can not write on terminal");
        terminal.clear(ClearType::CurrentLine).expect("Can not clear current line.");
        terminal.write(&self.message).expect("Can not write on terminal");
     }
}

impl UserInterface {
    pub fn new(config: &str, offset: (usize, usize)) -> Self {
        Self {
            image: Image::new(&config, offset),
            message: String::new(),
        }
    }
}
<commit_msg>message can be longer than one line<commit_after>use crate::image::Image;
use crate::Render;

extern crate crossterm;

use crossterm::{cursor, terminal, ClearType };

const TITLE: &str = "ASCII-ART HANGMAN FOR KIDS";

#[derive(Debug)]
pub struct UserInterface {
    pub image: Image,
    pub message: String,
}

impl Render for UserInterface {
    fn render(&self) {
        // Clear all lines in terminal;
        let terminal = terminal();
        terminal.clear(ClearType::All).expect("Can not clear terminal.");
        cursor().goto(0, 0).expect("Can not set curson position.");
        terminal.write(&TITLE).expect("Can not write on terminal");
        terminal.write("\n").expect("Can not write on terminal");
        &self.image.render();
        terminal.write("\n\n").expect("Can not write on terminal");
        terminal.clear(ClearType::FromCursorDown).expect("Can not clear current line.");
        terminal.write(&self.message).expect("Can not write on terminal");
     }
}

impl UserInterface {
    pub fn new(config: &str, offset: (usize, usize)) -> Self {
        Self {
            image: Image::new(&config, offset),
            message: String::new(),
        }
    }
}
<|endoftext|>


<commit_before>use std::collections::HashMap;

type LeftTable<'a> = Vec<(i64, &'a str)>;
type RightTable<'a> = Vec<(&'a str, &'a str)>;
type ResultTable<'a> = Vec<(i64, &'a str, &'a str)>;

fn hash_join<'a>(s: LeftTable<'a>, r: RightTable<'a>) -> ResultTable<'a> {
    let mut hash_map: HashMap<&str, Vec<&str>> = HashMap::new();

    // hash phase
    for (name, nemesis) in r {
        if hash_map.contains_key(name) {
            hash_map.get_mut(name).unwrap().push(nemesis);
        } else {
            hash_map.insert(name, vec![nemesis]);
        }
    }

    let mut result = vec![];
    // join phase
    for (age, name) in s {
        if let Some(v) = hash_map.get(name) {
            for nemesis in v {
                result.push((age, name, *nemesis));
            }
        }
    }
    result
}

pub fn main() {
    let table1 = vec![(27, "Jonah"), (18, "Alan"), (28, "Glory"), (18, "Popeye"), (28, "Alan")];
    let table2 = vec![("Jonah", "Whales"),
                      ("Jonah", "Spiders"),
                      ("Alan", "Ghosts"),
                      ("Alan", "Zombies"),
                      ("Glory", "Buffy")];
    let result = hash_join(table1, table2);
    for (age, name, nemesis) in result {
        println!("{}, {}, {}", age, name, nemesis);
    }
}

#[test]
pub fn test() {
    let t1 = vec![(0, "hello"), (1, "world")];
    let t2 = vec![("hello", "rust"), ("hello", "cargo")];
    let r = hash_join(t1, t2);
    assert!(r == vec![(0, "hello", "rust"), (0, "hello", "cargo")]);
}
<commit_msg>update and genericize Hash-Join<commit_after>use std::collections::HashMap;
use std::hash::Hash;

// If you know one of the tables is smaller, it is best to make it the second parameter.
fn hash_join<A, B, K>(first: &[(K, A)], second: &[(K, B)]) -> Vec<(A, K, B)>
where
    K: Hash + Eq + Copy,
    A: Copy,
    B: Copy,
{
    let mut hash_map = HashMap::new();

    // hash phase
    for &(key, val_a) in second {
        // collect all values by their keys, appending new ones to each existing entry
        hash_map.entry(key).or_insert_with(Vec::new).push(val_a);
    }

    let mut result = Vec::new();
    // join phase
    for &(key, val_b) in first {
        if let Some(vals) = hash_map.get(&key) {
            let tuples = vals.iter().map(|&val_a| (val_b, key, val_a));
            result.extend(tuples);
        }
    }

    result
}

fn main() {
    let table1 = [("Jonah", 27), ("Alan", 18), ("Glory", 28), ("Popeye", 18), ("Alan", 28)];
    let table2 = [
        ("Jonah", "Whales"), ("Jonah", "Spiders"), ("Alan", "Ghosts"),
        ("Alan", "Zombies"), ("Glory", "Buffy")
    ];
    let result = hash_join(&table1, &table2);
    println!("Age | Character Name | Nemesis");
    println!("----|----------------|--------");
    for (age, name, nemesis) in result {
        println!("{:<3} | {:^14} | {}", age, name, nemesis);
    }
}

#[test]
fn test() {
    let t1 = [("hello", 0), ("world", 1)];
    let t2 = [("hello", "rust"), ("hello", "cargo")];
    let r = hash_join(&t1, &t2);
    let expected = [(0, "hello", "rust"), (0, "hello", "cargo")];
    assert_eq!(r, expected);
}
<|endoftext|>


<commit_before>//  * This file is part of the uutils coreutils package.
//  *
//  * (c) Alex Lyon <arcterus@mail.com>
//  *
//  * For the full copyright and license information, please view the LICENSE
//  * file that was distributed with this source code.

// spell-checker:ignore (ToDO) sbytes slen dlen memmem

#[macro_use]
extern crate uucore;

use clap::{crate_version, App, Arg};
use memchr::memmem;
use std::io::{stdin, stdout, BufReader, Read, Write};
use std::{fs::File, path::Path};
use uucore::display::Quotable;
use uucore::InvalidEncodingHandling;

static NAME: &str = "tac";
static USAGE: &str = "[OPTION]... [FILE]...";
static SUMMARY: &str = "Write each file to standard output, last line first.";

mod options {
    pub static BEFORE: &str = "before";
    pub static REGEX: &str = "regex";
    pub static SEPARATOR: &str = "separator";
    pub static FILE: &str = "file";
}

pub fn uumain(args: impl uucore::Args) -> i32 {
    let args = args
        .collect_str(InvalidEncodingHandling::ConvertLossy)
        .accept_any();

    let matches = uu_app().get_matches_from(args);

    let before = matches.is_present(options::BEFORE);
    let regex = matches.is_present(options::REGEX);
    let raw_separator = matches.value_of(options::SEPARATOR).unwrap_or("\n");
    let separator = if raw_separator.is_empty() {
        "\0"
    } else {
        raw_separator
    };

    let files: Vec<String> = match matches.values_of(options::FILE) {
        Some(v) => v.map(|v| v.to_owned()).collect(),
        None => vec!["-".to_owned()],
    };

    tac(files, before, regex, separator)
}

pub fn uu_app() -> App<'static, 'static> {
    App::new(uucore::util_name())
        .name(NAME)
        .version(crate_version!())
        .usage(USAGE)
        .about(SUMMARY)
        .arg(
            Arg::with_name(options::BEFORE)
                .short("b")
                .long(options::BEFORE)
                .help("attach the separator before instead of after")
                .takes_value(false),
        )
        .arg(
            Arg::with_name(options::REGEX)
                .short("r")
                .long(options::REGEX)
                .help("interpret the sequence as a regular expression")
                .takes_value(false),
        )
        .arg(
            Arg::with_name(options::SEPARATOR)
                .short("s")
                .long(options::SEPARATOR)
                .help("use STRING as the separator instead of newline")
                .takes_value(true),
        )
        .arg(Arg::with_name(options::FILE).hidden(true).multiple(true))
}

/// Print lines of a buffer in reverse, with line separator given as a regex.
///
/// `data` contains the bytes of the file.
///
/// `pattern` is the regular expression given as a
/// [`regex::bytes::Regex`] (not a [`regex::Regex`], since the input is
/// given as a slice of bytes). If `before` is `true`, then each match
/// of this pattern in `data` is interpreted as the start of a line. If
/// `before` is `false`, then each match of this pattern is interpreted
/// as the end of a line.
///
/// This function writes each line in `data` to [`std::io::Stdout`] in
/// reverse.
///
/// # Errors
///
/// If there is a problem writing to `stdout`, then this function
/// returns [`std::io::Error`].
fn buffer_tac_regex(
    data: &[u8],
    pattern: regex::bytes::Regex,
    before: bool,
) -> std::io::Result<()> {
    let mut out = stdout();

    // The index of the line separator for the current line.
    //
    // As we scan through the `data` from right to left, we update this
    // variable each time we find a new line separator. We restrict our
    // regular expression search to only those bytes up to the line
    // separator.
    let mut this_line_end = data.len();

    // The index of the start of the next line in the `data`.
    //
    // As we scan through the `data` from right to left, we update this
    // variable each time we find a new line.
    //
    // If `before` is `true`, then each line starts immediately before
    // the line separator. Otherwise, each line starts immediately after
    // the line separator.
    let mut following_line_start = data.len();

    // Iterate over each byte in the buffer in reverse. When we find a
    // line separator, write the line to stdout.
    //
    // The `before` flag controls whether the line separator appears at
    // the end of the line (as in "abc\ndef\n") or at the beginning of
    // the line (as in "/abc/def").
    for i in (0..data.len()).rev() {
        // Determine if there is a match for `pattern` starting at index
        // `i` in `data`. Only search up to the line ending that was
        // found previously.
        if let Some(match_) = pattern.find_at(&data[..this_line_end], i) {
            // Record this index as the ending of the current line.
            this_line_end = i;

            // The length of the match (that is, the line separator), in bytes.
            let slen = match_.end() - match_.start();

            if before {
                out.write_all(&data[i..following_line_start])?;
                following_line_start = i;
            } else {
                out.write_all(&data[i + slen..following_line_start])?;
                following_line_start = i + slen;
            }
        }
    }

    // After the loop terminates, write whatever bytes are remaining at
    // the beginning of the buffer.
    out.write_all(&data[0..following_line_start])?;
    Ok(())
}

/// Write lines from `data` to stdout in reverse.
///
/// This function writes to [`stdout`] each line appearing in `data`,
/// starting with the last line and ending with the first line. The
/// `separator` parameter defines what characters to use as a line
/// separator.
///
/// If `before` is `false`, then this function assumes that the
/// `separator` appears at the end of each line, as in `"abc\ndef\n"`.
/// If `before` is `true`, then this function assumes that the
/// `separator` appears at the beginning of each line, as in
/// `"/abc/def"`.
fn buffer_tac(data: &[u8], before: bool, separator: &str) -> std::io::Result<()> {
    let mut out = stdout();

    // The number of bytes in the line separator.
    let slen = separator.as_bytes().len();

    // The index of the start of the next line in the `data`.
    //
    // As we scan through the `data` from right to left, we update this
    // variable each time we find a new line.
    //
    // If `before` is `true`, then each line starts immediately before
    // the line separator. Otherwise, each line starts immediately after
    // the line separator.
    let mut following_line_start = data.len();

    // Iterate over each byte in the buffer in reverse. When we find a
    // line separator, write the line to stdout.
    //
    // The `before` flag controls whether the line separator appears at
    // the end of the line (as in "abc\ndef\n") or at the beginning of
    // the line (as in "/abc/def").
    for i in memmem::rfind_iter(data, separator) {
        if before {
            out.write_all(&data[i..following_line_start])?;
            following_line_start = i;
        } else {
            out.write_all(&data[i + slen..following_line_start])?;
            following_line_start = i + slen;
        }
    }

    // After the loop terminates, write whatever bytes are remaining at
    // the beginning of the buffer.
    out.write_all(&data[0..following_line_start])?;
    Ok(())
}

fn tac(filenames: Vec<String>, before: bool, regex: bool, separator: &str) -> i32 {
    let mut exit_code = 0;

    for filename in &filenames {
        let mut file = BufReader::new(if filename == "-" {
            Box::new(stdin()) as Box<dyn Read>
        } else {
            let path = Path::new(filename);
            if path.is_dir() || path.metadata().is_err() {
                if path.is_dir() {
                    show_error!("{}: read error: Invalid argument", filename.maybe_quote());
                } else {
                    show_error!(
                        "failed to open {} for reading: No such file or directory",
                        filename.quote()
                    );
                }
                exit_code = 1;
                continue;
            }
            match File::open(path) {
                Ok(f) => Box::new(f) as Box<dyn Read>,
                Err(e) => {
                    show_error!("failed to open {} for reading: {}", filename.quote(), e);
                    exit_code = 1;
                    continue;
                }
            }
        });

        let mut data = Vec::new();
        if let Err(e) = file.read_to_end(&mut data) {
            show_error!("failed to read {}: {}", filename.quote(), e);
            exit_code = 1;
            continue;
        };
        if regex {
            let pattern = crash_if_err!(1, regex::bytes::Regex::new(separator));
            buffer_tac_regex(&data, pattern, before)
        } else {
            buffer_tac(&data, before, separator)
        }
        .unwrap_or_else(|e| crash!(1, "failed to write to stdout: {}", e));
    }
    exit_code
}
<commit_msg>tac: do not re-compile regular expression for each file<commit_after>//  * This file is part of the uutils coreutils package.
//  *
//  * (c) Alex Lyon <arcterus@mail.com>
//  *
//  * For the full copyright and license information, please view the LICENSE
//  * file that was distributed with this source code.

// spell-checker:ignore (ToDO) sbytes slen dlen memmem

#[macro_use]
extern crate uucore;

use clap::{crate_version, App, Arg};
use memchr::memmem;
use std::io::{stdin, stdout, BufReader, Read, Write};
use std::{fs::File, path::Path};
use uucore::display::Quotable;
use uucore::InvalidEncodingHandling;

static NAME: &str = "tac";
static USAGE: &str = "[OPTION]... [FILE]...";
static SUMMARY: &str = "Write each file to standard output, last line first.";

mod options {
    pub static BEFORE: &str = "before";
    pub static REGEX: &str = "regex";
    pub static SEPARATOR: &str = "separator";
    pub static FILE: &str = "file";
}

pub fn uumain(args: impl uucore::Args) -> i32 {
    let args = args
        .collect_str(InvalidEncodingHandling::ConvertLossy)
        .accept_any();

    let matches = uu_app().get_matches_from(args);

    let before = matches.is_present(options::BEFORE);
    let regex = matches.is_present(options::REGEX);
    let raw_separator = matches.value_of(options::SEPARATOR).unwrap_or("\n");
    let separator = if raw_separator.is_empty() {
        "\0"
    } else {
        raw_separator
    };

    let files: Vec<&str> = match matches.values_of(options::FILE) {
        Some(v) => v.collect(),
        None => vec!["-"],
    };

    tac(files, before, regex, separator)
}

pub fn uu_app() -> App<'static, 'static> {
    App::new(uucore::util_name())
        .name(NAME)
        .version(crate_version!())
        .usage(USAGE)
        .about(SUMMARY)
        .arg(
            Arg::with_name(options::BEFORE)
                .short("b")
                .long(options::BEFORE)
                .help("attach the separator before instead of after")
                .takes_value(false),
        )
        .arg(
            Arg::with_name(options::REGEX)
                .short("r")
                .long(options::REGEX)
                .help("interpret the sequence as a regular expression")
                .takes_value(false),
        )
        .arg(
            Arg::with_name(options::SEPARATOR)
                .short("s")
                .long(options::SEPARATOR)
                .help("use STRING as the separator instead of newline")
                .takes_value(true),
        )
        .arg(Arg::with_name(options::FILE).hidden(true).multiple(true))
}

/// Print lines of a buffer in reverse, with line separator given as a regex.
///
/// `data` contains the bytes of the file.
///
/// `pattern` is the regular expression given as a
/// [`regex::bytes::Regex`] (not a [`regex::Regex`], since the input is
/// given as a slice of bytes). If `before` is `true`, then each match
/// of this pattern in `data` is interpreted as the start of a line. If
/// `before` is `false`, then each match of this pattern is interpreted
/// as the end of a line.
///
/// This function writes each line in `data` to [`std::io::Stdout`] in
/// reverse.
///
/// # Errors
///
/// If there is a problem writing to `stdout`, then this function
/// returns [`std::io::Error`].
fn buffer_tac_regex(
    data: &[u8],
    pattern: &regex::bytes::Regex,
    before: bool,
) -> std::io::Result<()> {
    let mut out = stdout();

    // The index of the line separator for the current line.
    //
    // As we scan through the `data` from right to left, we update this
    // variable each time we find a new line separator. We restrict our
    // regular expression search to only those bytes up to the line
    // separator.
    let mut this_line_end = data.len();

    // The index of the start of the next line in the `data`.
    //
    // As we scan through the `data` from right to left, we update this
    // variable each time we find a new line.
    //
    // If `before` is `true`, then each line starts immediately before
    // the line separator. Otherwise, each line starts immediately after
    // the line separator.
    let mut following_line_start = data.len();

    // Iterate over each byte in the buffer in reverse. When we find a
    // line separator, write the line to stdout.
    //
    // The `before` flag controls whether the line separator appears at
    // the end of the line (as in "abc\ndef\n") or at the beginning of
    // the line (as in "/abc/def").
    for i in (0..data.len()).rev() {
        // Determine if there is a match for `pattern` starting at index
        // `i` in `data`. Only search up to the line ending that was
        // found previously.
        if let Some(match_) = pattern.find_at(&data[..this_line_end], i) {
            // Record this index as the ending of the current line.
            this_line_end = i;

            // The length of the match (that is, the line separator), in bytes.
            let slen = match_.end() - match_.start();

            if before {
                out.write_all(&data[i..following_line_start])?;
                following_line_start = i;
            } else {
                out.write_all(&data[i + slen..following_line_start])?;
                following_line_start = i + slen;
            }
        }
    }

    // After the loop terminates, write whatever bytes are remaining at
    // the beginning of the buffer.
    out.write_all(&data[0..following_line_start])?;
    Ok(())
}

/// Write lines from `data` to stdout in reverse.
///
/// This function writes to [`stdout`] each line appearing in `data`,
/// starting with the last line and ending with the first line. The
/// `separator` parameter defines what characters to use as a line
/// separator.
///
/// If `before` is `false`, then this function assumes that the
/// `separator` appears at the end of each line, as in `"abc\ndef\n"`.
/// If `before` is `true`, then this function assumes that the
/// `separator` appears at the beginning of each line, as in
/// `"/abc/def"`.
fn buffer_tac(data: &[u8], before: bool, separator: &str) -> std::io::Result<()> {
    let mut out = stdout();

    // The number of bytes in the line separator.
    let slen = separator.as_bytes().len();

    // The index of the start of the next line in the `data`.
    //
    // As we scan through the `data` from right to left, we update this
    // variable each time we find a new line.
    //
    // If `before` is `true`, then each line starts immediately before
    // the line separator. Otherwise, each line starts immediately after
    // the line separator.
    let mut following_line_start = data.len();

    // Iterate over each byte in the buffer in reverse. When we find a
    // line separator, write the line to stdout.
    //
    // The `before` flag controls whether the line separator appears at
    // the end of the line (as in "abc\ndef\n") or at the beginning of
    // the line (as in "/abc/def").
    for i in memmem::rfind_iter(data, separator) {
        if before {
            out.write_all(&data[i..following_line_start])?;
            following_line_start = i;
        } else {
            out.write_all(&data[i + slen..following_line_start])?;
            following_line_start = i + slen;
        }
    }

    // After the loop terminates, write whatever bytes are remaining at
    // the beginning of the buffer.
    out.write_all(&data[0..following_line_start])?;
    Ok(())
}

fn tac(filenames: Vec<&str>, before: bool, regex: bool, separator: &str) -> i32 {
    let mut exit_code = 0;

    let pattern = if regex {
        Some(crash_if_err!(1, regex::bytes::Regex::new(separator)))
    } else {
        None
    };

    for &filename in &filenames {
        let mut file = BufReader::new(if filename == "-" {
            Box::new(stdin()) as Box<dyn Read>
        } else {
            let path = Path::new(filename);
            if path.is_dir() || path.metadata().is_err() {
                if path.is_dir() {
                    show_error!("{}: read error: Invalid argument", filename.maybe_quote());
                } else {
                    show_error!(
                        "failed to open {} for reading: No such file or directory",
                        filename.quote()
                    );
                }
                exit_code = 1;
                continue;
            }
            match File::open(path) {
                Ok(f) => Box::new(f) as Box<dyn Read>,
                Err(e) => {
                    show_error!("failed to open {} for reading: {}", filename.quote(), e);
                    exit_code = 1;
                    continue;
                }
            }
        });

        let mut data = Vec::new();
        if let Err(e) = file.read_to_end(&mut data) {
            show_error!("failed to read {}: {}", filename.quote(), e);
            exit_code = 1;
            continue;
        };
        if let Some(pattern) = &pattern {
            buffer_tac_regex(&data, pattern, before)
        } else {
            buffer_tac(&data, before, separator)
        }
        .unwrap_or_else(|e| crash!(1, "failed to write to stdout: {}", e));
    }
    exit_code
}
<|endoftext|>


<commit_before>fn main() {
    let mut s = String::new();

    let data = "random stuff";

    let s_to_string = data.to_string();

    let s_from = String::from("Initial content");

    s.push_str("pushed");
}
<commit_msg>Example of appending one character to string with push<commit_after>fn main() {
    let mut s = String::new();

    let data = "random stuff";

    let s_to_string = data.to_string();

    let s_from = String::from("Initial content");

    s.push_str("pushed");

    s.push('l');
}
<|endoftext|>


<commit_before>extern crate clap;

#[derive(Debug, Clone, PartialEq, Eq)]
struct Bits {
    bits: Vec<bool>,
}

impl std::str::FromStr for Bits {
    type Err = String;
    fn from_str(s: &str) -> Result<Self, Self::Err> {
        let mut bits = Vec::with_capacity(s.len());
        for ch in s.chars() {
            match ch {
                '0' => bits.push(false),
                '1' => bits.push(true),
                _ => return Err(format!("Invalid digit: {}", ch)),
            }
        }
        Ok(Bits { bits: bits })
    }
}

impl std::fmt::Display for Bits {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
        for bit in &self.bits {
            write!(f, "{}", if *bit { '1' } else { '0' })?
        }
        Ok(())
    }
}

fn run_dragon_curve(mut state: Bits, target_size: usize) -> Bits {
    while state.bits.len() < target_size {
        let initial_len = state.bits.len();
        state.bits.reserve(initial_len + 1);
        state.bits.push(false);
        for idx in (0..initial_len).rev() {
            let next = !state.bits[idx];
            state.bits.push(next);
        }
    }
    state.bits.truncate(target_size);
    state
}

fn checksum(bits: &Bits) -> Bits {
    let mut checksum = bits.clone();
    while checksum.bits.len() % 2 == 0 {
        let mut idx = 0;
        while idx < checksum.bits.len() {
            if checksum.bits[idx] == checksum.bits[idx + 1] {
                checksum.bits[idx / 2] = true;
            } else {
                checksum.bits[idx / 2] = false;
            }
            idx += 2
        }
        let new_size = checksum.bits.len() / 2;
        checksum.bits.truncate(new_size);
    }
    checksum
}

fn parse_args() -> Result<Bits, String> {
    let matches = clap::App::new("Day 16")
        .author("Devon Hollowood")
        .arg(clap::Arg::with_name("initial")
            .index(1)
            .short("i")
            .long("initial")
            .help("initial state for dragon curve")
            .required(true)
            .takes_value(true))
        .get_matches();
    matches.value_of("initial").unwrap().parse()
}

fn main() {
    let initial_state = parse_args().expect("Invalid initial state!");
    let checksum = checksum(&run_dragon_curve(initial_state, 272));
    println!("checksum: {}", checksum);
}

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

    #[test]
    fn simple_curves() {
        assert_eq!(run_dragon_curve("1".parse().unwrap(), 3),
                   "100".parse().unwrap());
        assert_eq!(run_dragon_curve("0".parse().unwrap(), 3),
                   "001".parse().unwrap());
        assert_eq!(run_dragon_curve("11111".parse().unwrap(), 11),
                   "11111000000".parse().unwrap());
        assert_eq!(run_dragon_curve("111100001010".parse().unwrap(), 25),
                   "1111000010100101011110000".parse().unwrap());
    }

    #[test]
    fn example() {
        let initial_state = "10000".parse().unwrap();
        let final_state = run_dragon_curve(initial_state, 20);
        assert_eq!(final_state, "10000011110010000111".parse().unwrap());
        assert_eq!(checksum(&final_state), "01100".parse().unwrap());
    }
}
<commit_msg>Day 16 part 2<commit_after>extern crate clap;

#[derive(Debug, Clone, PartialEq, Eq)]
struct Bits {
    bits: Vec<bool>,
}

impl std::str::FromStr for Bits {
    type Err = String;
    fn from_str(s: &str) -> Result<Self, Self::Err> {
        let mut bits = Vec::with_capacity(s.len());
        for ch in s.chars() {
            match ch {
                '0' => bits.push(false),
                '1' => bits.push(true),
                _ => return Err(format!("Invalid digit: {}", ch)),
            }
        }
        Ok(Bits { bits: bits })
    }
}

impl std::fmt::Display for Bits {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
        for bit in &self.bits {
            write!(f, "{}", if *bit { '1' } else { '0' })?
        }
        Ok(())
    }
}

fn run_dragon_curve(mut state: Bits, target_size: usize) -> Bits {
    while state.bits.len() < target_size {
        let initial_len = state.bits.len();
        state.bits.reserve(initial_len + 1);
        state.bits.push(false);
        for idx in (0..initial_len).rev() {
            let next = !state.bits[idx];
            state.bits.push(next);
        }
    }
    state.bits.truncate(target_size);
    state
}

fn checksum(bits: &Bits) -> Bits {
    let mut checksum = bits.clone();
    while checksum.bits.len() % 2 == 0 {
        let mut idx = 0;
        while idx < checksum.bits.len() {
            if checksum.bits[idx] == checksum.bits[idx + 1] {
                checksum.bits[idx / 2] = true;
            } else {
                checksum.bits[idx / 2] = false;
            }
            idx += 2
        }
        let new_size = checksum.bits.len() / 2;
        checksum.bits.truncate(new_size);
    }
    checksum
}

fn parse_args() -> Result<Bits, String> {
    let matches = clap::App::new("Day 16")
        .author("Devon Hollowood")
        .arg(clap::Arg::with_name("initial")
            .index(1)
            .short("i")
            .long("initial")
            .help("initial state for dragon curve")
            .required(true)
            .takes_value(true))
        .get_matches();
    matches.value_of("initial").unwrap().parse()
}

fn main() {
    let initial_state = parse_args().expect("Invalid initial state!");
    let checksum1 = checksum(&run_dragon_curve(initial_state.clone(), 272));
    println!("part 1 checksum: {}", checksum1);
    let checksum2 = checksum(&run_dragon_curve(initial_state, 35651584));
    println!("part 2 checksum: {}", checksum2);
}

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

    #[test]
    fn simple_curves() {
        assert_eq!(run_dragon_curve("1".parse().unwrap(), 3),
                   "100".parse().unwrap());
        assert_eq!(run_dragon_curve("0".parse().unwrap(), 3),
                   "001".parse().unwrap());
        assert_eq!(run_dragon_curve("11111".parse().unwrap(), 11),
                   "11111000000".parse().unwrap());
        assert_eq!(run_dragon_curve("111100001010".parse().unwrap(), 25),
                   "1111000010100101011110000".parse().unwrap());
    }

    #[test]
    fn example() {
        let initial_state = "10000".parse().unwrap();
        let final_state = run_dragon_curve(initial_state, 20);
        assert_eq!(final_state, "10000011110010000111".parse().unwrap());
        assert_eq!(checksum(&final_state), "01100".parse().unwrap());
    }
}
<|endoftext|>


<commit_before>extern crate structopt;
#[macro_use]
extern crate structopt_derive;

use structopt::StructOpt;
use std::collections::HashMap;
use std::fs::File;
use std::io::Read;
use std::path::PathBuf;

type Node = usize;

#[derive(Debug, Clone, PartialEq, Eq, Hash)]
enum Direction {
    Up,
    Down,
}

#[derive(Debug, Clone, PartialEq, Eq, Hash)]
struct Scanner {
    location: usize,
    range: usize,
    direction: Direction,
}

impl Scanner {
    fn advance(&mut self) {
        use Direction::*;
        if self.range == 0 {
            return;
        }
        if self.location == 0 {
            self.direction = Down;
        } else if self.location == self.range - 1 {
            self.direction = Up;
        }
        match self.direction {
            Up => self.location -= 1,
            Down => self.location += 1,
        }
    }
}

#[derive(Debug, Clone)]
struct Firewall {
    scanners: HashMap<Node, Scanner>,
    depth: usize,
}

impl Firewall {
    fn advance(&mut self) {
        for scanner in self.scanners.values_mut() {
            scanner.advance();
        }
    }
}

impl std::str::FromStr for Firewall {
    type Err = String;
    fn from_str(s: &str) -> Result<Self, Self::Err> {
        let mut max_depth = 0;
        let mut scanners = HashMap::new();
        for line in s.lines() {
            let mut split = line.split(':');
            match (split.next(), split.next()) {
                (Some(depth_str), Some(range_str)) => {
                    let depth = depth_str
                        .trim()
                        .parse()
                        .map_err(|e| format!("{:?}", e))?;
                    let range = range_str
                        .trim()
                        .parse()
                        .map_err(|e| format!("{:?}", e))?;
                    if depth > max_depth {
                        max_depth = depth;
                    }
                    scanners.insert(
                        depth,
                        Scanner {
                            location: 0,
                            range: range,
                            direction: Direction::Down,
                        },
                    );
                }
                _ => Err(format!("Could not parse: {}", line))?,
            }
        }
        Ok(Firewall {
            scanners: scanners,
            depth: max_depth,
        })
    }
}

fn part1(firewall: &mut Firewall) -> usize {
    let mut severity = 0;
    for depth in 0..firewall.depth + 1 {
        {
            if let Some(scanner) = firewall.scanners.get(&depth) {
                if scanner.location == 0 {
                    severity += depth * scanner.range;
                }
            }
        }
        firewall.advance();
    }
    severity
}

fn main() {
    let opt = Opt::from_args();
    let mut contents = String::new();
    if opt.input.to_str() == Some("-") {
        std::io::stdin()
            .read_to_string(&mut contents)
            .expect("could not read stdin");
    } else {
        let mut file = File::open(&opt.input)
            .expect(&format!("file {} not found", opt.input.display()));
        file.read_to_string(&mut contents)
            .expect(&format!("could not read file {}", opt.input.display()));
    }
    let firewall: Firewall = contents.parse().expect("Error parsing input");
    println!("Part 1: {}", part1(&mut firewall.clone()));
}

#[derive(StructOpt, Debug)]
#[structopt(name = "day13", about = "Advent of code 2017 day 13")]
struct Opt {
    #[structopt(help = "Input file", parse(from_os_str))] input: PathBuf,
}

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

    #[test]
    fn part1_test() {
        let mut firewall = "0: 3\n1: 2\n4: 4\n6: 4".parse().unwrap();
        assert_eq!(part1(&mut firewall), 24);
    }
}
<commit_msg>2017 day 13 part 02<commit_after>extern crate structopt;
#[macro_use]
extern crate structopt_derive;

use structopt::StructOpt;
use std::collections::{HashMap, HashSet};
use std::fs::File;
use std::io::Read;
use std::path::PathBuf;

type Node = usize;

#[derive(Debug, Clone, PartialEq, Eq, Hash)]
enum Direction {
    Up,
    Down,
}

#[derive(Debug, Clone, PartialEq, Eq, Hash)]
struct Scanner {
    location: usize,
    range: usize,
    direction: Direction,
}

impl Scanner {
    fn advance(&mut self) {
        use Direction::*;
        if self.range == 0 {
            return;
        }
        if self.location == 0 {
            self.direction = Down;
        } else if self.location == self.range - 1 {
            self.direction = Up;
        }
        match self.direction {
            Up => self.location -= 1,
            Down => self.location += 1,
        }
    }
}

#[derive(Debug, Clone)]
struct Firewall {
    scanners: HashMap<Node, Scanner>,
    depth: usize,
}

impl Firewall {
    fn advance(&mut self) {
        for scanner in self.scanners.values_mut() {
            scanner.advance();
        }
    }
}

impl std::str::FromStr for Firewall {
    type Err = String;
    fn from_str(s: &str) -> Result<Self, Self::Err> {
        let mut max_depth = 0;
        let mut scanners = HashMap::new();
        for line in s.lines() {
            let mut split = line.split(':');
            match (split.next(), split.next()) {
                (Some(depth_str), Some(range_str)) => {
                    let depth = depth_str
                        .trim()
                        .parse()
                        .map_err(|e| format!("{:?}", e))?;
                    let range = range_str
                        .trim()
                        .parse()
                        .map_err(|e| format!("{:?}", e))?;
                    if depth > max_depth {
                        max_depth = depth;
                    }
                    scanners.insert(
                        depth,
                        Scanner {
                            location: 0,
                            range: range,
                            direction: Direction::Down,
                        },
                    );
                }
                _ => Err(format!("Could not parse: {}", line))?,
            }
        }
        Ok(Firewall {
            scanners: scanners,
            depth: max_depth,
        })
    }
}

fn part1(firewall: &mut Firewall) -> usize {
    let mut severity = 0;
    for depth in 0..firewall.depth + 1 {
        {
            if let Some(scanner) = firewall.scanners.get(&depth) {
                if scanner.location == 0 {
                    severity += depth * scanner.range;
                }
            }
        }
        firewall.advance();
    }
    severity
}

fn part2(firewall: &mut Firewall) -> usize {
    let mut starts = HashSet::new();
    let mut time = 0;
    loop {
        starts.insert(time);
        for start in starts.iter().cloned().collect::<Vec<usize>>() {
            let mut loc = time - start;
            {
                if let Some(scanner) = firewall.scanners.get(&loc) {
                    if scanner.location == 0 {
                        starts.remove(&start);
                        continue;
                    }
                }
                if loc == firewall.depth {
                    return start;
                }
            }
        }
        firewall.advance();
        time += 1;
    }
}

fn main() {
    let opt = Opt::from_args();
    let mut contents = String::new();
    if opt.input.to_str() == Some("-") {
        std::io::stdin()
            .read_to_string(&mut contents)
            .expect("could not read stdin");
    } else {
        let mut file = File::open(&opt.input)
            .expect(&format!("file {} not found", opt.input.display()));
        file.read_to_string(&mut contents)
            .expect(&format!("could not read file {}", opt.input.display()));
    }
    let firewall: Firewall = contents.parse().expect("Error parsing input");
    println!("Part 1: {}", part1(&mut firewall.clone()));
    println!("Part 2: {}", part2(&mut firewall.clone()));
}

#[derive(StructOpt, Debug)]
#[structopt(name = "day13", about = "Advent of code 2017 day 13")]
struct Opt {
    #[structopt(help = "Input file", parse(from_os_str))] input: PathBuf,
}

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

    #[test]
    fn part1_test() {
        let mut firewall = "0: 3\n1: 2\n4: 4\n6: 4".parse().unwrap();
        assert_eq!(part1(&mut firewall), 24);
    }

    #[test]
    fn part2_test() {
        let mut firewall = "0: 3\n1: 2\n4: 4\n6: 4".parse().unwrap();
        assert_eq!(part2(&mut firewall), 10);
    }
}
<|endoftext|>


<commit_before>extern crate tokio_core;
extern crate futures;
extern crate tk_http;
extern crate tk_listen;
extern crate wonky_tanks_server;

use std::cell::RefCell;
use std::collections::hash_map::HashMap;
use std::fmt::Write;
use std::rc::Rc;

use tokio_core::reactor::Core;
use tokio_core::net::TcpListener;

use futures::{Stream, Future};
use futures::future::{FutureResult, ok};
use futures::sync::mpsc::{unbounded, UnboundedSender};

use tk_http::{Status};
use tk_http::server::buffered::{Request, BufferedDispatcher};
use tk_http::server::{Encoder, EncoderDone, Config as ServerConfig, Proto, Error as ServerError};
use tk_http::websocket::{Packet, Config as WebsocketConfig, Dispatcher, Frame, Loop, Error as WebsocketError};

use tk_listen::ListenExt;

use wonky_tanks_server::*;

const LOBBY: &'static str = "/";

type SharedData = Rc<RefCell<HashMap<CountedString, ChannelState>>>;

#[derive(Clone, Default)]
pub struct ChannelState {
    capacity: Option<u8>,
    identities: usize,
    participants: Vec<Rc<UnboundedSender<Packet>>>,
}

impl std::fmt::Debug for ChannelState {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        f
            .debug_struct("ChannelState")
            .field("capacity", &self.capacity)
            .field("identities", &self.identities)
            .field(
                "participants",
                &format_args!(
                    "{:x}",
                    IterContentsDebug(||
                        self
                            .participants
                            .iter()
                            .map(|i| (&i) as &UnboundedSender<_> as *const _ as usize)
                    )
                )
            )
            .finish()
    }
}

fn service<S>(directory: &Directory, token: &mut Option<String>, req: Request, mut e: Encoder<S>) -> FutureResult<EncoderDone<S>, ServerError> {
    if let Some(ws) = req.websocket_handshake() {
        *token = Some(req.path().to_string());
        e.status(Status::SwitchingProtocol);
        e.add_header("Connection", "upgrade").unwrap();
        e.add_header("Upgrade", "websocket").unwrap();
        e.format_header("Sec-Websocket-Accept", &ws.accept).unwrap();
        e.done_headers().unwrap();
        return ok(e.done())
    }

    if let Some((ctype, data)) = directory.get(req.path()) {
        e.status(Status::Ok);
        e.add_length(data.len() as u64).unwrap();
        e.add_header("Content-Type", ctype).unwrap();
        if e.done_headers().unwrap() {
            e.write_body(&data);
        }
        return ok(e.done())
    }

    let not_found = "No data found".as_bytes();
    e.status(Status::NotFound);
    e.add_length(not_found.len() as u64).unwrap();
    e.add_header("Content-Type", "text/plain").unwrap();
    if e.done_headers().unwrap() {
        e.write_body(not_found);
    }
    ok(e.done())
}

struct WebsocketConnection {
    shared_data: SharedData,
    sender: Rc<UnboundedSender<Packet>>,
    token: CountedString,
}

impl WebsocketConnection {
    pub fn new(shared_data: SharedData, token: String, sender: Rc<UnboundedSender<Packet>>) -> Self {
        let token: CountedString  = token.into();
        let mut data: String = Default::default();
        match match {
            let shared_data = &mut *shared_data.borrow_mut();
            let shared_data = shared_data.entry(token.clone());
            #[cfg(debug_assertions)]
            println!("Previous data: {:?}", &shared_data);
            let &mut ChannelState { ref mut participants, capacity, ref mut identities } = shared_data.or_insert_with(Default::default);
            write!(data, "identity: {}", identities).unwrap();
            drop(sender.unbounded_send(Packet::Text(data.clone())));
            *identities += 1;
            participants.push(sender.clone());
            data.clear();
            write!(data, "count: {}", participants.len()).unwrap();
            let (last, participants_sans_last) = participants.split_last().unwrap();
            for participant in participants_sans_last {
                drop(participant.unbounded_send(Packet::Text(data.clone())));
            }
            drop(last.unbounded_send(Packet::Text(data.clone())));
            (capacity, participants.len())
        } {
            (Some(capacity), 1) if 1 < capacity => Some(true),
            (None, 1) => Some(true),
            (Some(capacity), count) if count == capacity as usize && count != 1 => Some(false),
            _ => None,
        } {
            _ if &*token.0 == LOBBY => {
                for (channel, state)in shared_data.try_borrow().unwrap().iter() {
                    if &*channel.0 == LOBBY { continue }
                    if let Some(capacity) = state.capacity {
                        if state.participants.len() >= capacity as usize {
                            continue
                        }
                    }
                    data.clear();
                    write!(data, "{}: {}", "+", channel).unwrap();

                }
            },
            Some(toggle) => {
                if let Some(lobby) = shared_data.try_borrow().unwrap().get(LOBBY) {
                    data.clear();
                    write!(data, "{}: {}", if toggle { "+" } else { "-" }, token).unwrap();
                    let (last, participants) = lobby.participants.split_last().unwrap();
                    for participant in participants {
                        drop(participant.unbounded_send(Packet::Text(data.clone())));
                    }
                    drop(last.unbounded_send(Packet::Text(data)));
                }
            },
            _ => {},
        }
        #[cfg(debug_assertions)]
        println!("Created connection: {:x}/{}", (&sender) as &UnboundedSender<_> as *const _ as usize, token);
        WebsocketConnection { shared_data, sender, token }
    }

    fn is_lobby(&self) -> bool {
        LOBBY == &*self.token.0
    }
}

impl Drop for WebsocketConnection {
    fn drop(&mut self) {
        let &mut WebsocketConnection {
            ref mut shared_data,
            ref sender,
            ref token
        } = self;
        use std::collections::hash_map::Entry::Occupied;
        if Some(1) != {
            let mut map = shared_data.borrow_mut();
            let mut entry =
                if let Occupied(entry) = map.entry(token.clone()) {
                    entry
                } else {
                    panic!("Bad state; expected {} to be occupied: {:?}", token, shared_data)
                };
            if let (capacity, 0) = {
                let &mut ChannelState { ref mut participants, capacity, identities: _ } = entry.get_mut();
                participants.retain(|target| !Rc::ptr_eq(target, sender));
                if !participants.is_empty() {
                    let data = format!("count: {}", participants.len());
                    let (last, participants) = participants.split_last().unwrap();
                    for participant in participants {
                        drop(participant.unbounded_send(Packet::Text(data.clone())));
                    }
                    drop(last.unbounded_send(Packet::Text(data)));
                }
                (capacity, participants.len())
            } {
                entry.remove_entry();
                capacity
            } else {
                Some(1)
            }
        } {
            if let Some(lobby) = shared_data.try_borrow().unwrap().get(LOBBY) {
                let data = format!("-: {}", token);
                let (last, participants) = lobby.participants.split_last().unwrap();
                for participant in participants {
                    drop(participant.unbounded_send(Packet::Text(data.clone())));
                }
                drop(last.unbounded_send(Packet::Text(data)));
            }
        }
        #[cfg(debug_assertions)]
        println!("Dropped connection: {:x}/{}", (&self.sender) as &UnboundedSender<_> as *const _ as usize, token);
    }
}

impl Dispatcher for WebsocketConnection {
    type Future = FutureResult<(), WebsocketError>;

    fn frame(&mut self, frame: &Frame) -> FutureResult<(), WebsocketError> {
        if self.is_lobby() { return ok(()) }
        let &mut WebsocketConnection { ref shared_data, ref sender, ref token } = self;

        match frame {
            &Frame::Text(t) => {
                // FIXME - parse state changes
            },
            &Frame::Binary(b) => {
                let &ChannelState { ref participants, .. } = &(&*shared_data.borrow())[token];
                if participants.len() == 1 { return ok(()) }
                let mut participants = participants.iter().filter(|&participant| !Rc::ptr_eq(participant, sender));
                let last = participants.next_back().unwrap();
                let to_send = Packet::Binary(Vec::from(b));
                for participant in participants {
                    drop(participant.unbounded_send(to_send.clone()));
                }
                drop(last.unbounded_send(to_send));
            },
            _ => {},
        }

        ok(())
    }
}

fn main() {
    let directory = &Directory::new(std::env::current_dir().unwrap().as_path());
    let shared_data: SharedData = Default::default();
    let mut lp = Core::new().unwrap();
    let h1 = lp.handle();

    let addr = "0.0.0.0:5000".parse().unwrap();
    let listener = TcpListener::bind(&addr, &lp.handle()).unwrap();
    let cfg = ServerConfig::new().done();
    let wcfg = WebsocketConfig::new().done();

    let pipeline = listener.incoming();
    let pipeline =
        pipeline.map(
        move |(socket, addr)| {
            let wcfg = wcfg.clone();
            let h2 = h1.clone();

            let shared_data = shared_data.clone();
            let data = Rc::new(RefCell::new(None));
            let http_data = data.clone();
            let websocket_data = data;

            Proto::new(
                socket,
                &cfg,
                BufferedDispatcher::new_with_websockets(
                    addr,
                    &h1,
                    move |p1, p2| {
                        let mut http_data = http_data.borrow_mut();
                        let http_data: &mut Option<String> = &mut http_data;
                        if let Some(token) = http_data.take() {
                            panic!("http initializer called twice: {}", token);
                        }
                        let ret = service(
                            directory,
                            http_data,
                            p1,
                            p2
                        );
                        ret
                    },
                    move |outp, inp| {
                        let (sender, receiver) = unbounded();
                        let sender = Rc::new(sender);
                        Loop::server(
                            outp,
                            inp,
                            receiver.map_err(move |e| format!("{:?} : stream closed", e)),
                            WebsocketConnection::new(
                                shared_data.clone(),
                                websocket_data
                                    .borrow_mut()
                                    .take()
                                    .expect("http initializer not called yet"),
                                sender
                            ),
                            &wcfg,
                            &h2
                        )
                            .map_err(|e| println!("Websocket err: {:?}", e))
                    }
                ),
                &h1
            )
                .map_err(|e| {println!("Connection error: {:?}", e);})
                .then(|_| Ok(())) // don't fail, please
        }
    );
    let pipeline = pipeline.listen(1000);

    lp.run(pipeline).unwrap();
}<commit_msg>Have the server send channels on lobby connect<commit_after>extern crate tokio_core;
extern crate futures;
extern crate tk_http;
extern crate tk_listen;
extern crate wonky_tanks_server;

use std::cell::RefCell;
use std::collections::hash_map::HashMap;
use std::fmt::Write;
use std::rc::Rc;

use tokio_core::reactor::Core;
use tokio_core::net::TcpListener;

use futures::{Stream, Future};
use futures::future::{FutureResult, ok};
use futures::sync::mpsc::{unbounded, UnboundedSender};

use tk_http::{Status};
use tk_http::server::buffered::{Request, BufferedDispatcher};
use tk_http::server::{Encoder, EncoderDone, Config as ServerConfig, Proto, Error as ServerError};
use tk_http::websocket::{Packet, Config as WebsocketConfig, Dispatcher, Frame, Loop, Error as WebsocketError};

use tk_listen::ListenExt;

use wonky_tanks_server::*;

const LOBBY: &'static str = "/";

type SharedData = Rc<RefCell<HashMap<CountedString, ChannelState>>>;

#[derive(Clone, Default)]
pub struct ChannelState {
    capacity: Option<u8>,
    identities: usize,
    participants: Vec<Rc<UnboundedSender<Packet>>>,
}

impl std::fmt::Debug for ChannelState {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        f
            .debug_struct("ChannelState")
            .field("capacity", &self.capacity)
            .field("identities", &self.identities)
            .field(
                "participants",
                &format_args!(
                    "{:x}",
                    IterContentsDebug(||
                        self
                            .participants
                            .iter()
                            .map(|i| (&i) as &UnboundedSender<_> as *const _ as usize)
                    )
                )
            )
            .finish()
    }
}

fn service<S>(directory: &Directory, token: &mut Option<String>, req: Request, mut e: Encoder<S>) -> FutureResult<EncoderDone<S>, ServerError> {
    if let Some(ws) = req.websocket_handshake() {
        *token = Some(req.path().to_string());
        e.status(Status::SwitchingProtocol);
        e.add_header("Connection", "upgrade").unwrap();
        e.add_header("Upgrade", "websocket").unwrap();
        e.format_header("Sec-Websocket-Accept", &ws.accept).unwrap();
        e.done_headers().unwrap();
        return ok(e.done())
    }

    if let Some((ctype, data)) = directory.get(req.path()) {
        e.status(Status::Ok);
        e.add_length(data.len() as u64).unwrap();
        e.add_header("Content-Type", ctype).unwrap();
        if e.done_headers().unwrap() {
            e.write_body(&data);
        }
        return ok(e.done())
    }

    let not_found = "No data found".as_bytes();
    e.status(Status::NotFound);
    e.add_length(not_found.len() as u64).unwrap();
    e.add_header("Content-Type", "text/plain").unwrap();
    if e.done_headers().unwrap() {
        e.write_body(not_found);
    }
    ok(e.done())
}

struct WebsocketConnection {
    shared_data: SharedData,
    sender: Rc<UnboundedSender<Packet>>,
    token: CountedString,
}

impl WebsocketConnection {
    pub fn new(shared_data: SharedData, token: String, sender: Rc<UnboundedSender<Packet>>) -> Self {
        let token: CountedString  = token.into();
        match match {
            let shared_data = &mut *shared_data.borrow_mut();
            let shared_data = shared_data.entry(token.clone());
            #[cfg(debug_assertions)]
            println!("Previous data: {:?}", &shared_data);
            let &mut ChannelState { ref mut participants, capacity, ref mut identities } = shared_data.or_insert_with(Default::default);

            let mut data: String = Default::default();
            write!(data, "identity: {}", identities).unwrap();
            drop(sender.unbounded_send(Packet::Text(data.clone())));

            *identities += 1;
            participants.push(sender.clone());

            data.clear();
            write!(data, "count: {}", participants.len()).unwrap();

            let (last, participants_sans_last) = participants.split_last().unwrap();
            for participant in participants_sans_last {
                drop(participant.unbounded_send(Packet::Text(data.clone())));
            }
            drop(last.unbounded_send(Packet::Text(data)));
            (capacity, participants.len())
        } {
            (Some(capacity), 1) if 1 < capacity => Some(true),
            (None, 1) => Some(true),
            (Some(capacity), count) if count == capacity as usize && count != 1 => Some(false),
            _ => None,
        } {
            _ if &*token.0 == LOBBY => {
                for (channel, state)in shared_data.try_borrow().unwrap().iter() {
                    if &*channel.0 == LOBBY { continue }
                    if let Some(capacity) = state.capacity {
                        if state.participants.len() >= capacity as usize {
                            continue
                        }
                    }
                    let mut data: String = Default::default();
                    write!(data, "{}: {}", "+", channel).unwrap();
                    drop(sender.unbounded_send(Packet::Text(data)));
                }
            },
            Some(toggle) => {
                if let Some(lobby) = shared_data.try_borrow().unwrap().get(LOBBY) {
                    let mut data: String = Default::default();
                    write!(data, "{}: {}", if toggle { "+" } else { "-" }, token).unwrap();

                    let (last, participants) = lobby.participants.split_last().unwrap();
                    for participant in participants {
                        drop(participant.unbounded_send(Packet::Text(data.clone())));
                    }
                    drop(last.unbounded_send(Packet::Text(data)));
                }
            },
            _ => {},
        }
        #[cfg(debug_assertions)]
        println!("Created connection: {:x}/{}", (&sender) as &UnboundedSender<_> as *const _ as usize, token);
        WebsocketConnection { shared_data, sender, token }
    }

    fn is_lobby(&self) -> bool {
        LOBBY == &*self.token.0
    }
}

impl Drop for WebsocketConnection {
    fn drop(&mut self) {
        let &mut WebsocketConnection {
            ref mut shared_data,
            ref sender,
            ref token
        } = self;
        use std::collections::hash_map::Entry::Occupied;
        if Some(1) != {
            let mut map = shared_data.borrow_mut();
            let mut entry =
                if let Occupied(entry) = map.entry(token.clone()) {
                    entry
                } else {
                    panic!("Bad state; expected {} to be occupied: {:?}", token, shared_data)
                };
            if let (capacity, 0) = {
                let &mut ChannelState { ref mut participants, capacity, identities: _ } = entry.get_mut();
                participants.retain(|target| !Rc::ptr_eq(target, sender));
                if !participants.is_empty() {
                    let data = format!("count: {}", participants.len());
                    let (last, participants) = participants.split_last().unwrap();
                    for participant in participants {
                        drop(participant.unbounded_send(Packet::Text(data.clone())));
                    }
                    drop(last.unbounded_send(Packet::Text(data)));
                }
                (capacity, participants.len())
            } {
                entry.remove_entry();
                capacity
            } else {
                Some(1)
            }
        } {
            if let Some(lobby) = shared_data.try_borrow().unwrap().get(LOBBY) {
                let data = format!("-: {}", token);
                let (last, participants) = lobby.participants.split_last().unwrap();
                for participant in participants {
                    drop(participant.unbounded_send(Packet::Text(data.clone())));
                }
                drop(last.unbounded_send(Packet::Text(data)));
            }
        }
        #[cfg(debug_assertions)]
        println!("Dropped connection: {:x}/{}", (&self.sender) as &UnboundedSender<_> as *const _ as usize, token);
    }
}

impl Dispatcher for WebsocketConnection {
    type Future = FutureResult<(), WebsocketError>;

    fn frame(&mut self, frame: &Frame) -> FutureResult<(), WebsocketError> {
        if self.is_lobby() { return ok(()) }
        let &mut WebsocketConnection { ref shared_data, ref sender, ref token } = self;

        match frame {
            &Frame::Text(t) => {
                // FIXME - parse state changes
            },
            &Frame::Binary(b) => {
                let &ChannelState { ref participants, .. } = &(&*shared_data.borrow())[token];
                if participants.len() == 1 { return ok(()) }
                let mut participants = participants.iter().filter(|&participant| !Rc::ptr_eq(participant, sender));
                let last = participants.next_back().unwrap();
                let to_send = Packet::Binary(Vec::from(b));
                for participant in participants {
                    drop(participant.unbounded_send(to_send.clone()));
                }
                drop(last.unbounded_send(to_send));
            },
            _ => {},
        }

        ok(())
    }
}

fn main() {
    let directory = &Directory::new(std::env::current_dir().unwrap().as_path());
    let shared_data: SharedData = Default::default();
    let mut lp = Core::new().unwrap();
    let h1 = lp.handle();

    let addr = "0.0.0.0:5000".parse().unwrap();
    let listener = TcpListener::bind(&addr, &lp.handle()).unwrap();
    let cfg = ServerConfig::new().done();
    let wcfg = WebsocketConfig::new().done();

    let pipeline = listener.incoming();
    let pipeline =
        pipeline.map(
        move |(socket, addr)| {
            let wcfg = wcfg.clone();
            let h2 = h1.clone();

            let shared_data = shared_data.clone();
            let data = Rc::new(RefCell::new(None));
            let http_data = data.clone();
            let websocket_data = data;

            Proto::new(
                socket,
                &cfg,
                BufferedDispatcher::new_with_websockets(
                    addr,
                    &h1,
                    move |p1, p2| {
                        let mut http_data = http_data.borrow_mut();
                        let http_data: &mut Option<String> = &mut http_data;
                        if let Some(token) = http_data.take() {
                            panic!("http initializer called twice: {}", token);
                        }
                        let ret = service(
                            directory,
                            http_data,
                            p1,
                            p2
                        );
                        ret
                    },
                    move |outp, inp| {
                        let (sender, receiver) = unbounded();
                        let sender = Rc::new(sender);
                        Loop::server(
                            outp,
                            inp,
                            receiver.map_err(move |e| format!("{:?} : stream closed", e)),
                            WebsocketConnection::new(
                                shared_data.clone(),
                                websocket_data
                                    .borrow_mut()
                                    .take()
                                    .expect("http initializer not called yet"),
                                sender
                            ),
                            &wcfg,
                            &h2
                        )
                            .map_err(|e| println!("Websocket err: {:?}", e))
                    }
                ),
                &h1
            )
                .map_err(|e| {println!("Connection error: {:?}", e);})
                .then(|_| Ok(())) // don't fail, please
        }
    );
    let pipeline = pipeline.listen(1000);

    lp.run(pipeline).unwrap();
}<|endoftext|>