<commit_before>extern crate byteorder;
use std::io::Cursor;
use std::str::FromStr;
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};

macro_rules! define_instructions {
    ($($variant:ident,
       $value:expr,
       $name:expr,
       $num_operands:expr,
       $num_stack_args:expr,
       $stack_effect:expr)*) => (

        #[derive(Clone, Copy, Debug, Eq, PartialEq)]
        #[repr(u8)]
        enum Inst { $($variant = $value),* }

        struct InvalidInstError;

        impl Inst {
            fn from_u8(inst: u8) -> Result<Inst, InvalidInstError> {
                match inst {
                    $($value => Ok(Inst::$variant),)*
                    _ => Err(InvalidInstError),
                }
            }

            fn num_operands(self)   -> u8 { match self { $(Inst::$variant => $num_operands),* } }
            fn num_stack_args(self) -> u8 { match self { $(Inst::$variant => $num_stack_args),* } }
            fn stack_effect(self)   -> i8 { match self { $(Inst::$variant => $stack_effect),* } }
        }

        impl FromStr for Inst {
            type Err = InvalidInstError;

            fn from_str(inst: &str) -> Result<Inst, InvalidInstError> {
                match inst {
                    $($name => Ok(Inst::$variant),)*
                    _ => Err(InvalidInstError),
                }
            }

        }
    )
}

// Bytecode instruction opcodes. The values of these opcodes should never change, to remain
// compatible with existing bytecode programs.
define_instructions! {
//  variant, value, name,    operands, stack_args, stack_effect
    Nop,     0,     "nop",   0,        0,           0
    Print,   1,     "print", 0,        1,          -1
    Halt,    2,     "halt",  0,        0,           0
    Push,    3,     "push",  1,        0,           1
    Dup,     4,     "dup",   0,        1,           1
    Pop,     5,     "pop",   0,        1,          -1
    Swap,    6,     "swap",  0,        2,           0
    Add,     7,     "add",   0,        2,          -1
    Sub,     8,     "sub",   0,        2,          -1
    Mul,     9,     "mul",   0,        2,          -1
    Div,     10,    "div",   0,        2,          -1
    Mod,     11,    "mod",   0,        2,          -1
    Eq,      12,    "eq",    0,        2,          -1
    Lt,      13,    "lt",    0,        2,          -1
    Lte,     14,    "lte",   0,        2,          -1
    Gt,      15,    "gt",    0,        2,          -1
    Gte,     16,    "gte",   0,        2,          -1
    Jz,      17,    "jz",    1,        1,          -1
    Jnz,     18,    "jnz",   1,        1,          -1
    Jump,    19,    "jump",  1,        0,           0
    Call,    20,    "call",  1,        0,           0
    Ret,     21,    "ret",   0,        0,           0
    CPush,   22,    "cpush", 0,        1,          -1
    CPop,    23,    "cpop",  0,        0,           1
    CDup,    24,    "cdup",  0,        0,           1
}

#[derive(Clone, Copy, Debug, Eq, PartialEq)]
enum VmError {
    InvalidOpcode,
    UnexpectedProgramEnd, // Hit end of program while reading operand.
    StackOverflow,
    StackUnderflow,
    ControlStackOverflow,
    ControlStackUnderflow,
}

#[derive(Debug, Eq, PartialEq)]
struct Vm {
    stack: Box<[i32]>,
    stack_idx: usize,
    control_stack: Box<[i32]>,
    control_stack_idx: usize,
}

impl From<InvalidInstError> for VmError {
    fn from(_: InvalidInstError) -> Self {
        VmError::InvalidOpcode
    }
}

impl Vm {
    fn execute(&mut self, program: &[u8]) -> Result<(), VmError> {
        #[inline]
        fn jump(opcodes: &mut Cursor<&[u8]>, condition: bool) -> Result<(), VmError> {
            let delta = try!(opcodes.read_i32::<LittleEndian>().or(Err(UnexpectedProgramEnd)));
            if condition {
                let operand_size = std::mem::size_of::<i32>() as i64;
                let addr = (opcodes.position() as i64 + delta as i64 - operand_size) as u64;
                opcodes.set_position(addr);
            }
            Ok(())
        }

        use VmError::*;

        let mut opcodes = Cursor::new(program);

        while let Ok(opcode) = opcodes.read_u8() {
            let inst = try!(Inst::from_u8(opcode));

            if self.stack_idx < inst.num_stack_args() as usize { return Err(StackUnderflow); }
            if self.stack_idx as isize >= self.stack.len() as isize - inst.stack_effect() as isize {
                return Err(StackOverflow);
            }

            match inst {
                Inst::Nop | Inst::Pop => {}

                Inst::Print => {
                    let val = unsafe { *self.stack.get_unchecked(self.stack_idx - 1) };
                    println!("{}", val);
                }

                Inst::Halt => {
                    break;
                }

                Inst::Push => {
                    let val = try!(opcodes.read_i32::<LittleEndian>()
                                   .or(Err(UnexpectedProgramEnd)));
                    let stack_top = try!(self.stack.get_mut(self.stack_idx).ok_or(StackOverflow));
                    *stack_top = val;
                }

                Inst::Dup => {
                    unsafe {
                        *self.stack.get_unchecked_mut(self.stack_idx) =
                            *self.stack.get_unchecked(self.stack_idx - 1);
                    }
                }

                Inst::Swap => {
                    unsafe {
                        let tmp = *self.stack.get_unchecked(self.stack_idx - 1);
                        *self.stack.get_unchecked_mut(self.stack_idx - 1) =
                            *self.stack.get_unchecked(self.stack_idx - 2);
                        *self.stack.get_unchecked_mut(self.stack_idx - 2) = tmp;
                    }
                }

                Inst::Add => {
                    unsafe {
                        *self.stack.get_unchecked_mut(self.stack_idx - 2) +=
                            *self.stack.get_unchecked(self.stack_idx - 1);
                    }
                }

                Inst::Sub => {
                    unsafe {
                        *self.stack.get_unchecked_mut(self.stack_idx - 2) -=
                            *self.stack.get_unchecked(self.stack_idx - 1);
                    }
                }

                Inst::Mul => {
                    unsafe {
                        *self.stack.get_unchecked_mut(self.stack_idx - 2) *=
                            *self.stack.get_unchecked(self.stack_idx - 1);
                    }
                }

                Inst::Div => {
                    unsafe {
                        *self.stack.get_unchecked_mut(self.stack_idx - 2) /=
                            *self.stack.get_unchecked(self.stack_idx - 1);
                    }
                }

                Inst::Mod => {
                    unsafe {
                        *self.stack.get_unchecked_mut(self.stack_idx - 2) %=
                            *self.stack.get_unchecked(self.stack_idx - 1);
                    }
                }

                Inst::Eq => {
                    unsafe {
                        let val1 = *self.stack.get_unchecked_mut(self.stack_idx - 1);
                        let ptr2 = self.stack.get_unchecked_mut(self.stack_idx - 2);
                        *ptr2 = (*ptr2 == val1) as i32;
                    }
                }

                Inst::Lt => {
                    unsafe {
                        let val1 = *self.stack.get_unchecked_mut(self.stack_idx - 1);
                        let ptr2 = self.stack.get_unchecked_mut(self.stack_idx - 2);
                        *ptr2 = (*ptr2 < val1) as i32;
                    }
                }

                Inst::Lte => {
                    unsafe {
                        let val1 = *self.stack.get_unchecked_mut(self.stack_idx - 1);
                        let ptr2 = self.stack.get_unchecked_mut(self.stack_idx - 2);
                        *ptr2 = (*ptr2 <= val1) as i32;
                    }
                }

                Inst::Gt => {
                    unsafe {
                        let val1 = *self.stack.get_unchecked_mut(self.stack_idx - 1);
                        let ptr2 = self.stack.get_unchecked_mut(self.stack_idx - 2);
                        *ptr2 = (*ptr2 > val1) as i32;
                    }
                }

                Inst::Gte => {
                    unsafe {
                        let val1 = *self.stack.get_unchecked_mut(self.stack_idx - 1);
                        let ptr2 = self.stack.get_unchecked_mut(self.stack_idx - 2);
                        *ptr2 = (*ptr2 >= val1) as i32;
                    }
                }

                Inst::Jz => {
                    let condition = unsafe { *self.stack.get_unchecked(self.stack_idx - 1) };
                    try!(jump(&mut opcodes, condition == 0));
                }

                Inst::Jnz => {
                    let condition = unsafe { *self.stack.get_unchecked(self.stack_idx - 1) };
                    try!(jump(&mut opcodes, condition != 0));
                }

                Inst::Jump => {
                    try!(jump(&mut opcodes, true));
                }

                Inst::Call => {
                    let control_stack_top = try!(self.control_stack.get_mut(self.control_stack_idx)
                                                 .ok_or(ControlStackOverflow));
                    *control_stack_top = opcodes.position() as i32 + 4;
                    try!(jump(&mut opcodes, true));
                    self.control_stack_idx += 1;
                }

                Inst::Ret => {
                    let addr = *try!(self.control_stack.get_mut(self.control_stack_idx - 1)
                                     .ok_or(ControlStackOverflow));
                    opcodes.set_position(addr as u64);
                    self.control_stack_idx -= 1;
                }

                Inst::CPush => {
                    if self.control_stack_idx >= self.control_stack.len() {
                        return Err(ControlStackOverflow);
                    }
                    unsafe {
                        *self.control_stack.get_unchecked_mut(self.control_stack_idx) =
                            *self.stack.get_unchecked(self.stack_idx - 1);
                    }
                    self.control_stack_idx += 1;
                }

                Inst::CPop => {
                    if self.control_stack_idx < 1 { return Err(ControlStackUnderflow); }
                    unsafe {
                        *self.stack.get_unchecked_mut(self.stack_idx) =
                            *self.control_stack.get_unchecked(self.control_stack_idx - 1);
                    }
                    self.control_stack_idx -= 1;
                }

                Inst::CDup => {
                    if self.control_stack_idx < 1 { return Err(ControlStackUnderflow); }
                    unsafe {
                        *self.stack.get_unchecked_mut(self.stack_idx) =
                            *self.control_stack.get_unchecked(self.control_stack_idx - 1);
                    }
                }
            }

            self.stack_idx = (self.stack_idx as isize + inst.stack_effect() as isize) as usize;
        }

        Ok(())
    }
}

fn assemble(source: &str) -> Vec<u8> {
    use std::collections::HashMap;

    let mut program: Vec<u8> = Vec::new();
    let mut label_definitions: HashMap<&str, usize> = HashMap::new();
    let mut label_uses: Vec<(&str, usize)> = Vec::new();

    for line in source.split(|c| c == '\n' || c == ';') {
        let mut tokens = line.split(char::is_whitespace).filter(|s| !s.is_empty());
        let mut first_token = tokens.next();

        // Parse an optional label at the start of the line.
        if let Some(label) = first_token {
            if label.chars().next_back() == Some(':') {
                let label_name = &label[..label.len() - 1];
                if label_definitions.insert(label_name, program.len()).is_some() {
                    panic!("Attempted to redefine label '{}'", label_name);
                }
                first_token = tokens.next()
            }
        }

        // Parse the rest of the line if it's not blank.
        if let Some(opcode) = first_token {
            // Parse the instruction name.
            let inst = Inst::from_str(opcode).unwrap_or_else(|_| {
                panic!("Unrecognized instruction '{}'", opcode)
            });
            program.push(inst as u8);

            // Parse the operands.
            for _ in 0..inst.num_operands() {
                let operand = tokens.next().unwrap_or_else(|| {
                    panic!("Missing one or more operands after '{}'", opcode)
                });

                if operand.starts_with('@') {
                    let label_name = &operand[1..];
                    label_uses.push((label_name, program.len()));

                    // Push four zero bytes to be overwritten by the label location later.
                    for _ in 0..4 { program.push(0); }
                } else if let Ok(number) = operand.parse::<i32>() {
                    let operand_index = program.len();
                    for _ in 0..4 { program.push(0); }
                    (&mut program[operand_index..]).write_i32::<LittleEndian>(number).unwrap();
                } else {
                    panic!("Expected label or valid 32-bit signed integer after '{}', not '{}'",
                           opcode, operand);
                }
            }
        }
    }

    // Resolve label references and fill in their values in the bytecode.
    for (label_name, use_index) in label_uses {
        let target_index = *label_definitions.get(label_name).unwrap_or_else(|| {
            panic!("Reference to undefined label '{}'", label_name);
        });
        let delta = target_index as i32 - use_index as i32;
        (&mut program[use_index..]).write_i32::<LittleEndian>(delta).unwrap();
    }

    program
}

fn main() {
    // let source = r"
    //     push 1
// loop:   call @plus2
    //     dup; print
    //     jump @loop

// plus2:  push 2
    //     add
    //     ret
    // ";

    let source = r"
        push 10
        call @fact
        print
        halt

fact:   push 1
        swap
loop:   dup; jz @done
        dup; cpush
        mul
        cpop; push 1; sub
        jump @loop
done:   pop
        ret
    ";

    let program = assemble(source);

    let mut vm = Vm {
        stack: Box::new([0; 256]),
        stack_idx: 0,
        control_stack: Box::new([0; 256]),
        control_stack_idx: 0,
    };
    vm.execute(&program).unwrap();
}
<commit_msg>Replace from_str with parse for consistency.<commit_after>extern crate byteorder;
use std::io::Cursor;
use std::str::FromStr;
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};

macro_rules! define_instructions {
    ($($variant:ident,
       $value:expr,
       $name:expr,
       $num_operands:expr,
       $num_stack_args:expr,
       $stack_effect:expr)*) => (

        #[derive(Clone, Copy, Debug, Eq, PartialEq)]
        #[repr(u8)]
        enum Inst { $($variant = $value),* }

        struct InvalidInstError;

        impl Inst {
            fn from_u8(inst: u8) -> Result<Inst, InvalidInstError> {
                match inst {
                    $($value => Ok(Inst::$variant),)*
                    _ => Err(InvalidInstError),
                }
            }

            fn num_operands(self)   -> u8 { match self { $(Inst::$variant => $num_operands),* } }
            fn num_stack_args(self) -> u8 { match self { $(Inst::$variant => $num_stack_args),* } }
            fn stack_effect(self)   -> i8 { match self { $(Inst::$variant => $stack_effect),* } }
        }

        impl FromStr for Inst {
            type Err = InvalidInstError;

            fn from_str(inst: &str) -> Result<Inst, InvalidInstError> {
                match inst {
                    $($name => Ok(Inst::$variant),)*
                    _ => Err(InvalidInstError),
                }
            }

        }
    )
}

// Bytecode instruction opcodes. The values of these opcodes should never change, to remain
// compatible with existing bytecode programs.
define_instructions! {
//  variant, value, name,    operands, stack_args, stack_effect
    Nop,     0,     "nop",   0,        0,           0
    Print,   1,     "print", 0,        1,          -1
    Halt,    2,     "halt",  0,        0,           0
    Push,    3,     "push",  1,        0,           1
    Dup,     4,     "dup",   0,        1,           1
    Pop,     5,     "pop",   0,        1,          -1
    Swap,    6,     "swap",  0,        2,           0
    Add,     7,     "add",   0,        2,          -1
    Sub,     8,     "sub",   0,        2,          -1
    Mul,     9,     "mul",   0,        2,          -1
    Div,     10,    "div",   0,        2,          -1
    Mod,     11,    "mod",   0,        2,          -1
    Eq,      12,    "eq",    0,        2,          -1
    Lt,      13,    "lt",    0,        2,          -1
    Lte,     14,    "lte",   0,        2,          -1
    Gt,      15,    "gt",    0,        2,          -1
    Gte,     16,    "gte",   0,        2,          -1
    Jz,      17,    "jz",    1,        1,          -1
    Jnz,     18,    "jnz",   1,        1,          -1
    Jump,    19,    "jump",  1,        0,           0
    Call,    20,    "call",  1,        0,           0
    Ret,     21,    "ret",   0,        0,           0
    CPush,   22,    "cpush", 0,        1,          -1
    CPop,    23,    "cpop",  0,        0,           1
    CDup,    24,    "cdup",  0,        0,           1
}

#[derive(Clone, Copy, Debug, Eq, PartialEq)]
enum VmError {
    InvalidOpcode,
    UnexpectedProgramEnd, // Hit end of program while reading operand.
    StackOverflow,
    StackUnderflow,
    ControlStackOverflow,
    ControlStackUnderflow,
}

#[derive(Debug, Eq, PartialEq)]
struct Vm {
    stack: Box<[i32]>,
    stack_idx: usize,
    control_stack: Box<[i32]>,
    control_stack_idx: usize,
}

impl From<InvalidInstError> for VmError {
    fn from(_: InvalidInstError) -> Self {
        VmError::InvalidOpcode
    }
}

impl Vm {
    fn execute(&mut self, program: &[u8]) -> Result<(), VmError> {
        #[inline]
        fn jump(opcodes: &mut Cursor<&[u8]>, condition: bool) -> Result<(), VmError> {
            let delta = try!(opcodes.read_i32::<LittleEndian>().or(Err(UnexpectedProgramEnd)));
            if condition {
                let operand_size = std::mem::size_of::<i32>() as i64;
                let addr = (opcodes.position() as i64 + delta as i64 - operand_size) as u64;
                opcodes.set_position(addr);
            }
            Ok(())
        }

        use VmError::*;

        let mut opcodes = Cursor::new(program);

        while let Ok(opcode) = opcodes.read_u8() {
            let inst = try!(Inst::from_u8(opcode));

            if self.stack_idx < inst.num_stack_args() as usize { return Err(StackUnderflow); }
            if self.stack_idx as isize >= self.stack.len() as isize - inst.stack_effect() as isize {
                return Err(StackOverflow);
            }

            match inst {
                Inst::Nop | Inst::Pop => {}

                Inst::Print => {
                    let val = unsafe { *self.stack.get_unchecked(self.stack_idx - 1) };
                    println!("{}", val);
                }

                Inst::Halt => {
                    break;
                }

                Inst::Push => {
                    let val = try!(opcodes.read_i32::<LittleEndian>()
                                   .or(Err(UnexpectedProgramEnd)));
                    let stack_top = try!(self.stack.get_mut(self.stack_idx).ok_or(StackOverflow));
                    *stack_top = val;
                }

                Inst::Dup => {
                    unsafe {
                        *self.stack.get_unchecked_mut(self.stack_idx) =
                            *self.stack.get_unchecked(self.stack_idx - 1);
                    }
                }

                Inst::Swap => {
                    unsafe {
                        let tmp = *self.stack.get_unchecked(self.stack_idx - 1);
                        *self.stack.get_unchecked_mut(self.stack_idx - 1) =
                            *self.stack.get_unchecked(self.stack_idx - 2);
                        *self.stack.get_unchecked_mut(self.stack_idx - 2) = tmp;
                    }
                }

                Inst::Add => {
                    unsafe {
                        *self.stack.get_unchecked_mut(self.stack_idx - 2) +=
                            *self.stack.get_unchecked(self.stack_idx - 1);
                    }
                }

                Inst::Sub => {
                    unsafe {
                        *self.stack.get_unchecked_mut(self.stack_idx - 2) -=
                            *self.stack.get_unchecked(self.stack_idx - 1);
                    }
                }

                Inst::Mul => {
                    unsafe {
                        *self.stack.get_unchecked_mut(self.stack_idx - 2) *=
                            *self.stack.get_unchecked(self.stack_idx - 1);
                    }
                }

                Inst::Div => {
                    unsafe {
                        *self.stack.get_unchecked_mut(self.stack_idx - 2) /=
                            *self.stack.get_unchecked(self.stack_idx - 1);
                    }
                }

                Inst::Mod => {
                    unsafe {
                        *self.stack.get_unchecked_mut(self.stack_idx - 2) %=
                            *self.stack.get_unchecked(self.stack_idx - 1);
                    }
                }

                Inst::Eq => {
                    unsafe {
                        let val1 = *self.stack.get_unchecked_mut(self.stack_idx - 1);
                        let ptr2 = self.stack.get_unchecked_mut(self.stack_idx - 2);
                        *ptr2 = (*ptr2 == val1) as i32;
                    }
                }

                Inst::Lt => {
                    unsafe {
                        let val1 = *self.stack.get_unchecked_mut(self.stack_idx - 1);
                        let ptr2 = self.stack.get_unchecked_mut(self.stack_idx - 2);
                        *ptr2 = (*ptr2 < val1) as i32;
                    }
                }

                Inst::Lte => {
                    unsafe {
                        let val1 = *self.stack.get_unchecked_mut(self.stack_idx - 1);
                        let ptr2 = self.stack.get_unchecked_mut(self.stack_idx - 2);
                        *ptr2 = (*ptr2 <= val1) as i32;
                    }
                }

                Inst::Gt => {
                    unsafe {
                        let val1 = *self.stack.get_unchecked_mut(self.stack_idx - 1);
                        let ptr2 = self.stack.get_unchecked_mut(self.stack_idx - 2);
                        *ptr2 = (*ptr2 > val1) as i32;
                    }
                }

                Inst::Gte => {
                    unsafe {
                        let val1 = *self.stack.get_unchecked_mut(self.stack_idx - 1);
                        let ptr2 = self.stack.get_unchecked_mut(self.stack_idx - 2);
                        *ptr2 = (*ptr2 >= val1) as i32;
                    }
                }

                Inst::Jz => {
                    let condition = unsafe { *self.stack.get_unchecked(self.stack_idx - 1) };
                    try!(jump(&mut opcodes, condition == 0));
                }

                Inst::Jnz => {
                    let condition = unsafe { *self.stack.get_unchecked(self.stack_idx - 1) };
                    try!(jump(&mut opcodes, condition != 0));
                }

                Inst::Jump => {
                    try!(jump(&mut opcodes, true));
                }

                Inst::Call => {
                    let control_stack_top = try!(self.control_stack.get_mut(self.control_stack_idx)
                                                 .ok_or(ControlStackOverflow));
                    *control_stack_top = opcodes.position() as i32 + 4;
                    try!(jump(&mut opcodes, true));
                    self.control_stack_idx += 1;
                }

                Inst::Ret => {
                    let addr = *try!(self.control_stack.get_mut(self.control_stack_idx - 1)
                                     .ok_or(ControlStackOverflow));
                    opcodes.set_position(addr as u64);
                    self.control_stack_idx -= 1;
                }

                Inst::CPush => {
                    if self.control_stack_idx >= self.control_stack.len() {
                        return Err(ControlStackOverflow);
                    }
                    unsafe {
                        *self.control_stack.get_unchecked_mut(self.control_stack_idx) =
                            *self.stack.get_unchecked(self.stack_idx - 1);
                    }
                    self.control_stack_idx += 1;
                }

                Inst::CPop => {
                    if self.control_stack_idx < 1 { return Err(ControlStackUnderflow); }
                    unsafe {
                        *self.stack.get_unchecked_mut(self.stack_idx) =
                            *self.control_stack.get_unchecked(self.control_stack_idx - 1);
                    }
                    self.control_stack_idx -= 1;
                }

                Inst::CDup => {
                    if self.control_stack_idx < 1 { return Err(ControlStackUnderflow); }
                    unsafe {
                        *self.stack.get_unchecked_mut(self.stack_idx) =
                            *self.control_stack.get_unchecked(self.control_stack_idx - 1);
                    }
                }
            }

            self.stack_idx = (self.stack_idx as isize + inst.stack_effect() as isize) as usize;
        }

        Ok(())
    }
}

fn assemble(source: &str) -> Vec<u8> {
    use std::collections::HashMap;

    let mut program: Vec<u8> = Vec::new();
    let mut label_definitions: HashMap<&str, usize> = HashMap::new();
    let mut label_uses: Vec<(&str, usize)> = Vec::new();

    for line in source.split(|c| c == '\n' || c == ';') {
        let mut tokens = line.split(char::is_whitespace).filter(|s| !s.is_empty());
        let mut first_token = tokens.next();

        // Parse an optional label at the start of the line.
        if let Some(label) = first_token {
            if label.chars().next_back() == Some(':') {
                let label_name = &label[..label.len() - 1];
                if label_definitions.insert(label_name, program.len()).is_some() {
                    panic!("Attempted to redefine label '{}'", label_name);
                }
                first_token = tokens.next()
            }
        }

        // Parse the rest of the line if it's not blank.
        if let Some(opcode) = first_token {
            // Parse the instruction name.
            let inst = opcode.parse::<Inst>().unwrap_or_else(|_| {
                panic!("Unrecognized instruction '{}'", opcode)
            });
            program.push(inst as u8);

            // Parse the operands.
            for _ in 0..inst.num_operands() {
                let operand = tokens.next().unwrap_or_else(|| {
                    panic!("Missing one or more operands after '{}'", opcode)
                });

                if operand.starts_with('@') {
                    let label_name = &operand[1..];
                    label_uses.push((label_name, program.len()));

                    // Push four zero bytes to be overwritten by the label location later.
                    for _ in 0..4 { program.push(0); }
                } else if let Ok(number) = operand.parse::<i32>() {
                    let operand_index = program.len();
                    for _ in 0..4 { program.push(0); }
                    (&mut program[operand_index..]).write_i32::<LittleEndian>(number).unwrap();
                } else {
                    panic!("Expected label or valid 32-bit signed integer after '{}', not '{}'",
                           opcode, operand);
                }
            }
        }
    }

    // Resolve label references and fill in their values in the bytecode.
    for (label_name, use_index) in label_uses {
        let target_index = *label_definitions.get(label_name).unwrap_or_else(|| {
            panic!("Reference to undefined label '{}'", label_name);
        });
        let delta = target_index as i32 - use_index as i32;
        (&mut program[use_index..]).write_i32::<LittleEndian>(delta).unwrap();
    }

    program
}

fn main() {
    // let source = r"
    //     push 1
// loop:   call @plus2
    //     dup; print
    //     jump @loop

// plus2:  push 2
    //     add
    //     ret
    // ";

    let source = r"
        push 10
        call @fact
        print
        halt

fact:   push 1
        swap
loop:   dup; jz @done
        dup; cpush
        mul
        cpop; push 1; sub
        jump @loop
done:   pop
        ret
    ";

    let program = assemble(source);

    let mut vm = Vm {
        stack: Box::new([0; 256]),
        stack_idx: 0,
        control_stack: Box::new([0; 256]),
        control_stack_idx: 0,
    };
    vm.execute(&program).unwrap();
}
<|endoftext|>


<commit_before>extern crate futures;
extern crate tokio_core;
extern crate tokio_proto;
extern crate tokio_service;

use std::io;
use std::str;
use tokio_core::io::{Codec, EasyBuf};
use tokio_core::io::{Io, Framed};
use tokio_proto::pipeline::ServerProto;
use tokio_proto::TcpServer;
use tokio_service::Service;
use futures::{future, Future, BoxFuture};

#[derive(Default)]
pub struct CliCodec {
    /// Offset within the incoming EasyBuf at which newline search last ended
    search_offset: usize,
}

impl Codec for CliCodec {
    /// Input type is a tuple with the command name and an optional argument string
    type In = (String, Option<String>);

    /// Response type is just a string
    type Out = String;

    /// Find a newline, decode to a tuple of command name and argument string
    fn decode(&mut self, buf: &mut EasyBuf) -> io::Result<Option<Self::In>> {
        // Find newline
        let m = buf
            .as_slice()
            .iter()
            .skip(self.search_offset)
            .position(|&b| b == b'\n');

        match m {
            // Process the line if a newline was found
            Some(idx) => {
                println!("Found newline at position {}, started at {}", idx, self.search_offset); // XXX

                let line = buf.drain_to(self.search_offset + idx);
                buf.drain_to(1);

                self.search_offset = 0;

                match str::from_utf8(line.as_slice()) {
                    // Return the string on successful UTF-8 decode
                    // (TODO: split on the first space, ignore blank lines)
                    Ok(s) => Ok(Some(
                            ("Received".to_string(), Some(s.trim().to_string()))
                            )),

                    // Return an error if invalid UTF-8 is received
                    Err(e) => Err(io::Error::new(io::ErrorKind::Other, format!("invalid UTF-8: {:?}", e))),
                }
            },

            // Save our position if a newline wasn't found
            _ => {
                println!("Haven't received newline yet!  We started at offset {}, now at {}", self.search_offset, buf.len()); // XXX

                self.search_offset = buf.len();

                Ok(None)
            }
        }
    }

    /// Append the String to the outgoing buffer
    fn encode(&mut self, msg: String, buf: &mut Vec<u8>) -> io::Result<()> {
        buf.extend(msg.as_bytes());
        buf.push(b'\n');
        Ok(())
    }
}

pub struct CliProto;

impl<T: Io + 'static> ServerProto<T> for CliProto {
    type Request = (String, Option<String>);
    type Response = String;

    type Transport = Framed<T, CliCodec>;
    type BindTransport = Result<Self::Transport, io::Error>;

    fn bind_transport(&self, io: T) -> Self::BindTransport {
        Ok(io.framed(CliCodec::default()))
    }
}

pub struct CliServer;

impl Service for CliServer {
    type Request = (String, Option<String>);
    type Response = String;

    type Error = io::Error;

    type Future = BoxFuture<Self::Response, Self::Error>;

    fn call(&self, req: Self::Request) -> Self::Future {
        future::ok(format!("Received: {:?}", req)).boxed()
    }
}

fn main() {
    let addr = "0.0.0.0:14311".parse().unwrap();
    let server = TcpServer::new(CliProto, addr);

    println!("Serving on {}", addr);
    server.serve(|| Ok(CliServer));
}
<commit_msg>Defined initial trait for command implementations.<commit_after>extern crate futures;
extern crate tokio_core;
extern crate tokio_proto;
extern crate tokio_service;

use std::io;
use std::str;
use tokio_core::io::{Codec, EasyBuf};
use tokio_core::io::{Io, Framed};
use tokio_proto::pipeline::ServerProto;
use tokio_proto::TcpServer;
use tokio_service::Service;
use futures::{future, Future, BoxFuture};

#[derive(Default)]
pub struct CliCodec {
    /// Offset within the incoming EasyBuf at which newline search last ended
    search_offset: usize,
}

impl Codec for CliCodec {
    /// Input type is a tuple with the command name and an optional argument string
    type In = (String, Option<String>);

    /// Response type is just a string
    type Out = String;

    /// Find a newline, decode to a tuple of command name and argument string
    fn decode(&mut self, buf: &mut EasyBuf) -> io::Result<Option<Self::In>> {
        // Find newline
        let m = buf
            .as_slice()
            .iter()
            .skip(self.search_offset)
            .position(|&b| b == b'\n');

        match m {
            // Process the line if a newline was found
            Some(idx) => {
                println!("Found newline at position {}, started at {}", idx, self.search_offset); // XXX

                let line = buf.drain_to(self.search_offset + idx);
                buf.drain_to(1);

                self.search_offset = 0;

                match str::from_utf8(line.as_slice()) {
                    // Return the string on successful UTF-8 decode
                    // (TODO: split on the first space, ignore blank lines)
                    Ok(s) => Ok(Some(
                            ("Received".to_string(), Some(s.trim().to_string()))
                            )),

                    // Return an error if invalid UTF-8 is received
                    Err(e) => Err(io::Error::new(io::ErrorKind::Other, format!("invalid UTF-8: {:?}", e))),
                }
            },

            // Save our position if a newline wasn't found
            _ => {
                println!("Haven't received newline yet!  We started at offset {}, now at {}", self.search_offset, buf.len()); // XXX

                self.search_offset = buf.len();

                Ok(None)
            }
        }
    }

    /// Append the String to the outgoing buffer
    fn encode(&mut self, msg: String, buf: &mut Vec<u8>) -> io::Result<()> {
        buf.extend(msg.as_bytes());
        buf.push(b'\n');
        Ok(())
    }
}

pub struct CliProto;

impl<T: Io + 'static> ServerProto<T> for CliProto {
    type Request = (String, Option<String>);
    type Response = String;

    type Transport = Framed<T, CliCodec>;
    type BindTransport = Result<Self::Transport, io::Error>;

    fn bind_transport(&self, io: T) -> Self::BindTransport {
        Ok(io.framed(CliCodec::default()))
    }
}

trait CliCommand {
    fn name(&self) -> &str;
    fn description(&self) -> &str;
    fn call(&self, args: &str) -> &str;
}

pub struct CliServer;

impl Service for CliServer {
    type Request = (String, Option<String>);
    type Response = String;

    type Error = io::Error;

    type Future = BoxFuture<Self::Response, Self::Error>;

    fn call(&self, req: Self::Request) -> Self::Future {
        future::ok(format!("Received: {:?}", req)).boxed()
    }
}

fn main() {
    let addr = "0.0.0.0:14311".parse().unwrap();
    let server = TcpServer::new(CliProto, addr);

    println!("Serving on {}", addr);
    server.serve(|| Ok(CliServer));
}
<|endoftext|>


<commit_before>/* Command Line Argument Parser */
#[macro_use]
extern crate clap;
use clap::AppSettings::SubcommandRequiredElseHelp;

#[macro_use]
extern crate serde_derive;

/* For parsing time */
#[macro_use]
extern crate nom;
use nom::IResult::Done;

/* For time handling */
extern crate chrono;
use chrono::Duration;

/* For from::utf8 */
use std::str;

/* For parsing time in HH:MM format. TODO: extend to other formats or find better solution */
named!(duration(&[u8]) -> Duration,
    do_parse!(
        hour: map_res!(map_res!(nom::digit, str::from_utf8), |s: &str| s.parse::<i64>()) >>
        tag!(":") >>
        min: map_res!(map_res!(nom::digit, str::from_utf8), |s: &str| s.parse::<i64>()) >>
        (Duration::minutes(hour * 60 + min))
    )
);

mod timesheet;

fn main() {
    /* Handle command line arguments with clap */
    let arguments = clap_app!(trk =>
        (setting: SubcommandRequiredElseHelp)
        (version: "0.1")
        (author: "Rafael B. <mediumendian@gmail.com>")
        (about: "Create timesheets from git history and meta info")
            /* (@arg CONFIG: -c --config +takes_value "[UNUSED] Sets a custom config file") */
            /* (@arg debug: -d ... "[UNUSED] Sets the level of debugging information") */

            (@subcommand init =>
                (about: "Initialise trk in this directory and give name (should match git user name)")
                (version: "0.1")
                (author:  "Rafael B. <mediumendian@gmail.com>")
                (@arg name: "Optional: user name. Default is git user name if set, empty otherwise.")
            )
            (@subcommand begin =>
                (about: "Begin session")
                (version: "0.1")
                (author:  "Rafael B. <mediumendian@gmail.com>")
            )
            (@subcommand end =>
                (about: "End session")
                (version: "0.1")
                (author:  "Rafael B. <mediumendian@gmail.com>")
            )
            (@subcommand pause =>
                (about: "Pause current session")
                (version: "0.1")
                (author: "mediumendian@gmail.com")
            )
            // TODO: make this unnecessary by making it possible to add any command with a time
            // offset
            (@subcommand retropause =>
                (about: "Pause current session after the fact (set length of break)")
                (version: "0.1")
                (author: "mediumendian@gmail.com")
                (@arg length: +required "How long the pause was")
                (@arg note_text: "Note about pause")
            )
            (@subcommand resume =>
                (about: "Resume currently paused session")
                (version: "0.1")
                (author: "mediumendian@gmail.com")
            )
            (@subcommand note =>
                (about: "Add a note about current work or pause")
                (version: "0.1")
                (author: "mediumendian@gmail.com")
                (@arg note_text: +required "Note text")
            )
            (@subcommand commit =>
                (about: "Add a commit to the event list")
                (version: "0.1")
                (author: "mediumendian@gmail.com")
                (@arg hash: +required "Commit hash id")
            )
            (@subcommand branch =>
                (about: "Add a branch change to the event list")
                (version: "0.1")
                (author: "mediumendian@gmail.com")
                (@arg name: +required "The branch's name")
            )
            (@subcommand status =>
                (about: "Prints the current WIP for session or sheet")
                (version: "0.1")
                (author: "mediumendian@gmail.com")
                (@arg which: +required "session or sheet")
                )
            (@subcommand report =>
                (about: "Generate html report for current session or entire sheet and save it to {timesheet|session}.html")
                (version: "0.1")
                (author: "mediumendian@gmail.com")
                (@arg which: +required "session or sheet")
            )
            (@subcommand clear =>
                (about: "Temporary: clears all sessions and updates all timestamps")
                (version: "0.1")
                (author: "mediumendian@gmail.com")
            )
       )
            .get_matches();

    /* Gets a value for config if supplied by user, or defaults to "default.conf" */
    /* let config = matches.value_of("config").unwrap_or("default.conf");
    println!("[UNUSED] Value for config: {}", config); */

    let sheet_opt: Option<timesheet::Timesheet> = timesheet::Timesheet::load_from_file();

    /* Special case for init because t_sheet can and should be None before initialisation */
    if let Some(command) = arguments.subcommand_matches("init") {
        match sheet_opt {
            Some(..) => println!("Already initialised!"),
            None => {
                match timesheet::Timesheet::init(command.value_of("name")) {
                    Some(..) => println!("Init successful."),
                    None => println!("Could not initialize."),
                }
            }
        }
        /* Nothing left to do. TODO: Continue instead? */
        return;
    }

    /* Unwrap the timesheet and continue only if timesheet file exists */
    let mut sheet = match sheet_opt {
        Some(file) => file,
        None => {
            println!("No timesheet file! You might have to init first.");
            return;
        }
    };

    match arguments.subcommand() {
        ("begin", Some(..)) => {
            sheet.new_session();
        }
        ("end", Some(..)) => {
            sheet.end_session();
        }
        ("pause", Some(..)) => {
            sheet.pause(None);
        }
        /*("retropause", Some(arg)) => {
            println!("{:?}", parse_to_seconds("30:00"));
            let length_in_seconds = parse_to_seconds(arg.value_of("length").unwrap());
            let note_text = arg.value_of("note_text");
            match note_text {
                Some(note_text) => sheet.retropause(length_in_seconds, Some(note_text.to_string())),
                None => sheet.retropause(length_in_seconds, None),
            }
        }*/
        ("resume", Some(..)) => {
            sheet.resume();
        }
        ("note", Some(arg)) => {
            let note_text = arg.value_of("note_text").unwrap();
            sheet.push_note(note_text.to_string());
        }
        ("commit", Some(arg)) => {
            let commit_hash = arg.value_of("hash").unwrap();
            sheet.push_commit(commit_hash.to_string());
        }
        ("branch", Some(arg)) => {
            let branch_name = arg.value_of("name").unwrap();
            sheet.push_branch(branch_name.to_string());
        }
        ("status", Some(arg)) => {
            match arg.value_of("which") {
                Some("session") => println!("{:?}", sheet.last_session_status()),
                Some("sheet") => println!("{:?}", sheet.timesheet_status()),
                Some(text) => {
                    println!("What do you mean by {}? Should be either 'sheet' or 'session'.",
                             text)
                }
                None => {}
            }
        }
        ("report", Some(arg)) => {
            match arg.value_of("which") {
                Some("session") => {
                    sheet.last_session_report();
                }
                Some("sheet") => {
                    sheet.report();
                }
                Some(text) => {
                    println!("What do you mean by {}? Should be either 'sheet' or 'session'.",
                             text)
                }
                None => {}
            }
        }
        ("clear", Some(..)) => {
            println!("Clearing timesheet!");
            timesheet::Timesheet::clear();
        }
        _ => unreachable!(),
    }
}

fn parse_to_seconds(timestr: &str) -> Option<u64> {
    match duration(timestr.as_bytes()) {
        Done(_, out) => Some(out.num_seconds() as u64),
        _ => None,
    }
}
<commit_msg>improve clearing of timesheet<commit_after>/* Command Line Argument Parser */
#[macro_use]
extern crate clap;
use clap::AppSettings::SubcommandRequiredElseHelp;

#[macro_use]
extern crate serde_derive;

/* For parsing time */
#[macro_use]
extern crate nom;
use nom::IResult::Done;

/* For time handling */
extern crate chrono;
use chrono::Duration;

/* For from::utf8 */
use std::str;

/* For parsing time in HH:MM format. TODO: extend to other formats or find better solution */
named!(duration(&[u8]) -> Duration,
    do_parse!(
        hour: map_res!(map_res!(nom::digit, str::from_utf8), |s: &str| s.parse::<i64>()) >>
        tag!(":") >>
        min: map_res!(map_res!(nom::digit, str::from_utf8), |s: &str| s.parse::<i64>()) >>
        (Duration::minutes(hour * 60 + min))
    )
);

mod timesheet;

fn main() {
    /* Handle command line arguments with clap */
    let arguments = clap_app!(trk =>
        (setting: SubcommandRequiredElseHelp)
        (version: "0.1")
        (author: "Rafael B. <mediumendian@gmail.com>")
        (about: "Create timesheets from git history and meta info")
            /* (@arg CONFIG: -c --config +takes_value "[UNUSED] Sets a custom config file") */
            /* (@arg debug: -d ... "[UNUSED] Sets the level of debugging information") */

            (@subcommand init =>
                (about: "Initialise trk in this directory and give name (should match git user name)")
                (version: "0.1")
                (author:  "Rafael B. <mediumendian@gmail.com>")
                (@arg name: "Optional: user name. Default is git user name if set, empty otherwise.")
            )
            (@subcommand begin =>
                (about: "Begin session")
                (version: "0.1")
                (author:  "Rafael B. <mediumendian@gmail.com>")
            )
            (@subcommand end =>
                (about: "End session")
                (version: "0.1")
                (author:  "Rafael B. <mediumendian@gmail.com>")
            )
            (@subcommand pause =>
                (about: "Pause current session")
                (version: "0.1")
                (author: "mediumendian@gmail.com")
            )
            // TODO: make this unnecessary by making it possible to add any command with a time
            // offset
            (@subcommand retropause =>
                (about: "Pause current session after the fact (set length of break)")
                (version: "0.1")
                (author: "mediumendian@gmail.com")
                (@arg length: +required "How long the pause was")
                (@arg note_text: "Note about pause")
            )
            (@subcommand resume =>
                (about: "Resume currently paused session")
                (version: "0.1")
                (author: "mediumendian@gmail.com")
            )
            (@subcommand note =>
                (about: "Add a note about current work or pause")
                (version: "0.1")
                (author: "mediumendian@gmail.com")
                (@arg note_text: +required "Note text")
            )
            (@subcommand commit =>
                (about: "Add a commit to the event list")
                (version: "0.1")
                (author: "mediumendian@gmail.com")
                (@arg hash: +required "Commit hash id")
            )
            (@subcommand branch =>
                (about: "Add a branch change to the event list")
                (version: "0.1")
                (author: "mediumendian@gmail.com")
                (@arg name: +required "The branch's name")
            )
            (@subcommand status =>
                (about: "Prints the current WIP for session or sheet")
                (version: "0.1")
                (author: "mediumendian@gmail.com")
                (@arg which: +required "session or sheet")
                )
            (@subcommand report =>
                (about: "Generate html report for current session or entire sheet and save it to {timesheet|session}.html")
                (version: "0.1")
                (author: "mediumendian@gmail.com")
                (@arg which: +required "session or sheet")
            )
            (@subcommand clear =>
                (about: "Temporary: clears all sessions and updates all timestamps")
                (version: "0.1")
                (author: "mediumendian@gmail.com")
            )
       )
            .get_matches();

    /* Gets a value for config if supplied by user, or defaults to "default.conf" */
    /* let config = matches.value_of("config").unwrap_or("default.conf");
    println!("[UNUSED] Value for config: {}", config); */

    let sheet_opt: Option<timesheet::Timesheet> = timesheet::Timesheet::load_from_file();

    /* Special case for init because t_sheet can and should be None before initialisation */
    if let Some(command) = arguments.subcommand_matches("init") {
        match sheet_opt {
            Some(..) => println!("Already initialised!"),
            None => {
                match timesheet::Timesheet::init(command.value_of("name")) {
                    Some(..) => println!("Init successful."),
                    None => println!("Could not initialize."),
                }
            }
        }
        /* Nothing left to do. TODO: Continue instead? */
        return;
    }

    /* Special case for clear because t_sheet can be None when clearing (corrupt file) */
    if let Some(command) = arguments.subcommand_matches("clear") {
        match sheet_opt {
            Some(..) => {
                println!("Clearing timesheet!");
                timesheet::Timesheet::clear();
            }
            None => {
                match timesheet::Timesheet::init(command.value_of("name")) {
                    Some(..) => println!("Reinitialised timesheet"),
                    None => println!("Could not initialize."),
                }
            }
        }
        /* Nothing left to do. TODO: Continue instead? */
        return;
    }

    /* Unwrap the timesheet and continue only if timesheet file exists */
    let mut sheet = match sheet_opt {
        Some(file) => file,
        None => {
            println!("No timesheet file! You might have to init first.");
            return;
        }
    };

    match arguments.subcommand() {
        ("begin", Some(..)) => {
            sheet.new_session();
        }
        ("end", Some(..)) => {
            sheet.end_session();
        }
        ("pause", Some(..)) => {
            sheet.pause(None);
        }
        /*("retropause", Some(arg)) => {
            println!("{:?}", parse_to_seconds("30:00"));
            let length_in_seconds = parse_to_seconds(arg.value_of("length").unwrap());
            let note_text = arg.value_of("note_text");
            match note_text {
                Some(note_text) => sheet.retropause(length_in_seconds, Some(note_text.to_string())),
                None => sheet.retropause(length_in_seconds, None),
            }
        }*/
        ("resume", Some(..)) => {
            sheet.resume();
        }
        ("note", Some(arg)) => {
            let note_text = arg.value_of("note_text").unwrap();
            sheet.push_note(note_text.to_string());
        }
        ("commit", Some(arg)) => {
            let commit_hash = arg.value_of("hash").unwrap();
            sheet.push_commit(commit_hash.to_string());
        }
        ("branch", Some(arg)) => {
            let branch_name = arg.value_of("name").unwrap();
            sheet.push_branch(branch_name.to_string());
        }
        ("status", Some(arg)) => {
            match arg.value_of("which") {
                Some("session") => println!("{:?}", sheet.last_session_status()),
                Some("sheet") => println!("{:?}", sheet.timesheet_status()),
                Some(text) => {
                    println!("What do you mean by {}? Should be either 'sheet' or 'session'.",
                             text)
                }
                None => {}
            }
        }
        ("report", Some(arg)) => {
            match arg.value_of("which") {
                Some("session") => {
                    sheet.last_session_report();
                }
                Some("sheet") => {
                    sheet.report();
                }
                Some(text) => {
                    println!("What do you mean by {}? Should be either 'sheet' or 'session'.",
                             text)
                }
                None => {}
            }
        }

        _ => unreachable!(),
    }
}

fn parse_to_seconds(timestr: &str) -> Option<u64> {
    match duration(timestr.as_bytes()) {
        Done(_, out) => Some(out.num_seconds() as u64),
        _ => None,
    }
}
<|endoftext|>


<commit_before>#[macro_use]
extern crate log;
extern crate rustc_serialize;
extern crate argparse;
extern crate env_logger;
extern crate hyper;
extern crate mozprofile;
extern crate mozrunner;
extern crate regex;
#[macro_use]
extern crate webdriver;

use std::borrow::ToOwned;
use std::process::exit;
use std::net::{SocketAddr, SocketAddrV4, Ipv4Addr};
use std::str::FromStr;
use std::path::Path;

use argparse::{ArgumentParser, StoreTrue, Store};
use webdriver::server::start;

use marionette::{MarionetteHandler, BrowserLauncher, MarionetteSettings};

macro_rules! try_opt {
    ($expr:expr, $err_type:expr, $err_msg:expr) => ({
        match $expr {
            Some(x) => x,
            None => return Err(WebDriverError::new($err_type, $err_msg))
        }
    })
}

mod marionette;

struct Options {
    binary: String,
    webdriver_host: String,
    webdriver_port: u16,
    marionette_port: u16,
    connect_existing: bool
}

fn parse_args() -> Options {
    let mut opts = Options {
        binary: "".to_owned(),
        webdriver_host: "127.0.0.1".to_owned(),
        webdriver_port: 4444u16,
        marionette_port: 2828u16,
        connect_existing: false
    };

    {
        let mut parser = ArgumentParser::new();
        parser.set_description("WebDriver to marionette proxy.");
        parser.refer(&mut opts.binary)
            .add_option(&["-b", "--binary"], Store,
                        "Path to the Firefox binary");
        parser.refer(&mut opts.webdriver_host)
            .add_option(&["--webdriver-host"], Store,
                        "Host to run webdriver server on");
        parser.refer(&mut opts.webdriver_port)
            .add_option(&["--webdriver-port"], Store,
                        "Port to run webdriver on");
        parser.refer(&mut opts.marionette_port)
            .add_option(&["--marionette-port"], Store,
                        "Port to run marionette on");
        parser.refer(&mut opts.connect_existing)
            .add_option(&["--connect-existing"], StoreTrue,
                        "Connect to an existing firefox process");
        parser.parse_args_or_exit()
    }
    opts
}

fn main() {
    env_logger::init().unwrap();
    let opts = parse_args();

    let host = &opts.webdriver_host[..];
    let port = opts.webdriver_port;
    let addr = Ipv4Addr::from_str(host).map(
        |x| SocketAddr::V4(SocketAddrV4::new(x, port))).unwrap_or_else(
        |_| {
            println!("Invalid host address");
            exit(1);
        }
        );

    let launcher = if opts.connect_existing {
        BrowserLauncher::None
    } else {
        BrowserLauncher::BinaryLauncher(Path::new(&opts.binary).to_path_buf())
    };

    let settings = MarionetteSettings::new(opts.marionette_port,
                                           launcher);

    //TODO: what if binary isn't a valid path?
    start(addr, MarionetteHandler::new(settings));
}
<commit_msg>Better handling of missing command line arguments<commit_after>#[macro_use]
extern crate log;
extern crate rustc_serialize;
extern crate argparse;
extern crate env_logger;
extern crate hyper;
extern crate mozprofile;
extern crate mozrunner;
extern crate regex;
#[macro_use]
extern crate webdriver;

use std::borrow::ToOwned;
use std::process::exit;
use std::net::{SocketAddr, SocketAddrV4, Ipv4Addr};
use std::str::FromStr;
use std::path::Path;

use argparse::{ArgumentParser, StoreTrue, Store};
use webdriver::server::start;

use marionette::{MarionetteHandler, BrowserLauncher, MarionetteSettings};

macro_rules! try_opt {
    ($expr:expr, $err_type:expr, $err_msg:expr) => ({
        match $expr {
            Some(x) => x,
            None => return Err(WebDriverError::new($err_type, $err_msg))
        }
    })
}

mod marionette;

struct Options {
    binary: String,
    webdriver_host: String,
    webdriver_port: u16,
    marionette_port: u16,
    connect_existing: bool
}

fn parse_args() -> Options {
    let mut opts = Options {
        binary: "".to_owned(),
        webdriver_host: "127.0.0.1".to_owned(),
        webdriver_port: 4444u16,
        marionette_port: 2828u16,
        connect_existing: false
    };

    {
        let mut parser = ArgumentParser::new();
        parser.set_description("WebDriver to marionette proxy.");
        parser.refer(&mut opts.binary)
            .add_option(&["-b", "--binary"], Store,
                        "Path to the Firefox binary");
        parser.refer(&mut opts.webdriver_host)
            .add_option(&["--webdriver-host"], Store,
                        "Host to run webdriver server on");
        parser.refer(&mut opts.webdriver_port)
            .add_option(&["--webdriver-port"], Store,
                        "Port to run webdriver on");
        parser.refer(&mut opts.marionette_port)
            .add_option(&["--marionette-port"], Store,
                        "Port to run marionette on");
        parser.refer(&mut opts.connect_existing)
            .add_option(&["--connect-existing"], StoreTrue,
                        "Connect to an existing firefox process");
        parser.parse_args_or_exit();
    }

    if opts.binary == "" && !opts.connect_existing {
        println!("Must supply a binary path or --connect-existing\n");
        exit(1)
    }

    opts
}

fn main() {
    env_logger::init().unwrap();
    let opts = parse_args();

    let host = &opts.webdriver_host[..];
    let port = opts.webdriver_port;
    let addr = Ipv4Addr::from_str(host).map(
        |x| SocketAddr::V4(SocketAddrV4::new(x, port))).unwrap_or_else(
        |_| {
            println!("Invalid host address");
            exit(1);
        }
        );

    let launcher = if opts.connect_existing {
        BrowserLauncher::None
    } else {
        BrowserLauncher::BinaryLauncher(Path::new(&opts.binary).to_path_buf())
    };

    let settings = MarionetteSettings::new(opts.marionette_port,
                                           launcher);

    //TODO: what if binary isn't a valid path?
    start(addr, MarionetteHandler::new(settings));
}
<|endoftext|>


<commit_before>use std::cell::Cell;

struct Coordinates {
    x: Cell<i32>,
    y: Cell<i32>,
}

fn main() {
    let origin = Coordinates { x: Cell::new(0), y: Cell::new(0) };

    println!("X is {:?} and Y is {:?}", origin.x, origin.y);

    move_up(&origin.y);
    move_up(&origin.y);

    println!("X is {:?} and Y is {:?}", origin.x, origin.y);
}

fn move_up(y_cord: &Cell<i32>) -> &Cell<i32> {
    let new_val = y_cord.get();
    y_cord.set(new_val + 1);
    y_cord // no semicolon here because it is the return value
}
<commit_msg>added some comments<commit_after>use std::cell::Cell;

struct Coordinates {
    x: Cell<i32>,
    y: Cell<i32>,
}

fn main() {
    let origin = Coordinates { x: Cell::new(0), y: Cell::new(0) };

    println!("X is {:?} and Y is {:?}", origin.x, origin.y);

    move_up(&origin.y); // borrowing the value
    move_up(&origin.y); // so that I can change it

    println!("X is {:?} and Y is {:?}", origin.x, origin.y);
}

fn move_up(y_cord: &Cell<i32>) -> &Cell<i32> {
    let new_val = y_cord.get();
    y_cord.set(new_val + 1);
    y_cord // no semicolon here because it is the return value
}
<|endoftext|>


<commit_before>#[macro_use]
extern crate clap;
extern crate globset;
extern crate env_logger;
extern crate libc;
#[macro_use]
extern crate log;
#[macro_use]
extern crate lazy_static;
extern crate notify;

#[cfg(unix)]
extern crate nix;
#[cfg(windows)]
extern crate winapi;
#[cfg(windows)]
extern crate kernel32;

#[cfg(test)]
extern crate mktemp;

mod cli;
mod gitignore;
mod notification_filter;
mod process;
mod signal;
mod watcher;

use std::collections::HashMap;
use std::env;
use std::path::Path;
use std::sync::{Arc, RwLock};
use std::sync::mpsc::{channel, Receiver};
use std::time::Duration;
use std::path::{PathBuf};

use notification_filter::NotificationFilter;
use process::Process;
use signal::Signal;
use watcher::{Event, Watcher};

fn find_gitignore(path: &Path) -> Option<PathBuf> {
    let mut p = path.to_owned();

    loop {
        let gitignore_path = p.join(".gitignore");
        if gitignore_path.exists() {
            return Some(gitignore_path);
        }

        // Stop if we see a .git directory
        if let Ok(metadata) = p.join(".git").metadata() {
            if metadata.is_dir() {
                break;
            }
        }

        if p.parent().is_none() {
            break;
        }

        p.pop();
    }

    None
}

fn init_logger(debug: bool) {
    let mut log_builder = env_logger::LogBuilder::new();
    let level = if debug {
        log::LogLevelFilter::Debug
    } else {
        log::LogLevelFilter::Warn
    };

    log_builder.format(|r| format!("*** {}", r.args()))
        .filter(None, level);
    log_builder.init().expect("unable to initialize logger");
}

fn main() {
    let args = cli::get_args();
    let child_process: Arc<RwLock<Option<Process>>> = Arc::new(RwLock::new(None));
    let weak_child = Arc::downgrade(&child_process);
    let kill = args.kill;

    signal::install_handler(move |sig: Signal| {
        if let Some(lock) = weak_child.upgrade() {
            let strong = lock.read().unwrap();
            if let Some(ref child) = *strong {
                match sig {
                    Signal::Terminate => {
                        if kill {
                            child.kill();
                        } else {
                            child.terminate();
                        }

                        child.wait();
                    },
                    Signal::Stop => {
                        child.pause();
                    },
                    Signal::Continue => {
                        child.resume();
                    }
                }
            }
        }
    });

    init_logger(args.debug);

    let cwd = env::current_dir()
        .expect("unable to get cwd")
        .canonicalize()
        .expect("unable to canonicalize cwd");

    let mut gitignore_file = None;
    if !args.no_vcs_ignore {
        if let Some(gitignore_path) = find_gitignore(&cwd) {
            debug!("Found .gitignore file: {:?}", gitignore_path);

            gitignore_file = gitignore::parse(&gitignore_path).ok();
        }
    }

    let filter = NotificationFilter::new(args.filters, args.ignores, gitignore_file)
        .expect("unable to create notification filter");

    let (tx, rx) = channel();
    let mut watcher = Watcher::new(tx, args.poll, args.poll_interval)
        .expect("unable to create watcher");

    if watcher.is_polling() {
        warn!("Polling for changes every {} ms", args.poll_interval);
    }

    let result = watcher.watch(cwd);
    if let Err(e) = result {
        error!("Unable to watch directory/subdirectory: {}", e);
        return;
    }

    // Start child process initially, if necessary
    if args.run_initially {
        if args.clear_screen {
            cli::clear_screen();
        }

        let mut guard = child_process.write().unwrap();
        *guard = Some(process::spawn(&args.cmd, vec![]));
    }

    loop {
        let paths = wait(&rx, &filter);
        if let Some(path) = paths.get(0) {
            debug!("Path updated: {:?}", path);
        }

        // Wait for current child process to exit
        {
            let guard = child_process.read().unwrap();

            if let Some(ref child) = *guard {
                if args.restart {
                    debug!("Killing child process");
                    child.kill();
                }

                debug!("Waiting for process to exit...");
                child.wait();
            }
        }

        // Launch child process
        if args.clear_screen {
            cli::clear_screen();
        }

        {
            let mut guard = child_process.write().unwrap();
            *guard = Some(process::spawn(&args.cmd, paths));
        }
    }
}

fn wait(rx: &Receiver<Event>, filter: &NotificationFilter) -> Vec<PathBuf> {
    let mut paths = vec![];
    let mut cache = HashMap::new();

    loop {
        let e = rx.recv().expect("error when reading event");

        if let Some(ref path) = e.path {
            // Ignore cache for the initial file. Otherwise, in
            // debug mode it's hard to track what's going on
            let excluded = filter.is_excluded(path);
            if !cache.contains_key(path) {
                cache.insert(path.to_owned(), excluded);
            }

            if !excluded {
                paths.push(path.to_owned());
                break;
            }
        }
    }

    // Wait for filesystem activity to cool off
    let timeout = Duration::from_millis(500);
    while let Ok(e) = rx.recv_timeout(timeout) {
        if let Some(ref path) = e.path {
            if cache.contains_key(path) {
                continue;
            }

            let excluded = filter.is_excluded(path);

            let p = path.to_owned();
            cache.insert(p.clone(), excluded);

            if !excluded {
                paths.push(p);
            }
        }
    }

    paths
}
<commit_msg>Fix sending SIGKILL by default when restarting processes<commit_after>#[macro_use]
extern crate clap;
extern crate globset;
extern crate env_logger;
extern crate libc;
#[macro_use]
extern crate log;
#[macro_use]
extern crate lazy_static;
extern crate notify;

#[cfg(unix)]
extern crate nix;
#[cfg(windows)]
extern crate winapi;
#[cfg(windows)]
extern crate kernel32;

#[cfg(test)]
extern crate mktemp;

mod cli;
mod gitignore;
mod notification_filter;
mod process;
mod signal;
mod watcher;

use std::collections::HashMap;
use std::env;
use std::path::Path;
use std::sync::{Arc, RwLock};
use std::sync::mpsc::{channel, Receiver};
use std::time::Duration;
use std::path::{PathBuf};

use notification_filter::NotificationFilter;
use process::Process;
use signal::Signal;
use watcher::{Event, Watcher};

fn find_gitignore(path: &Path) -> Option<PathBuf> {
    let mut p = path.to_owned();

    loop {
        let gitignore_path = p.join(".gitignore");
        if gitignore_path.exists() {
            return Some(gitignore_path);
        }

        // Stop if we see a .git directory
        if let Ok(metadata) = p.join(".git").metadata() {
            if metadata.is_dir() {
                break;
            }
        }

        if p.parent().is_none() {
            break;
        }

        p.pop();
    }

    None
}

fn init_logger(debug: bool) {
    let mut log_builder = env_logger::LogBuilder::new();
    let level = if debug {
        log::LogLevelFilter::Debug
    } else {
        log::LogLevelFilter::Warn
    };

    log_builder.format(|r| format!("*** {}", r.args()))
        .filter(None, level);
    log_builder.init().expect("unable to initialize logger");
}

fn main() {
    let args = cli::get_args();
    let child_process: Arc<RwLock<Option<Process>>> = Arc::new(RwLock::new(None));
    let weak_child = Arc::downgrade(&child_process);
    let kill = args.kill;

    signal::install_handler(move |sig: Signal| {
        if let Some(lock) = weak_child.upgrade() {
            let strong = lock.read().unwrap();
            if let Some(ref child) = *strong {
                match sig {
                    Signal::Terminate => {
                        if kill {
                            child.kill();
                        } else {
                            child.terminate();
                        }

                        child.wait();
                    },
                    Signal::Stop => {
                        child.pause();
                    },
                    Signal::Continue => {
                        child.resume();
                    }
                }
            }
        }
    });

    init_logger(args.debug);

    let cwd = env::current_dir()
        .expect("unable to get cwd")
        .canonicalize()
        .expect("unable to canonicalize cwd");

    let mut gitignore_file = None;
    if !args.no_vcs_ignore {
        if let Some(gitignore_path) = find_gitignore(&cwd) {
            debug!("Found .gitignore file: {:?}", gitignore_path);

            gitignore_file = gitignore::parse(&gitignore_path).ok();
        }
    }

    let filter = NotificationFilter::new(args.filters, args.ignores, gitignore_file)
        .expect("unable to create notification filter");

    let (tx, rx) = channel();
    let mut watcher = Watcher::new(tx, args.poll, args.poll_interval)
        .expect("unable to create watcher");

    if watcher.is_polling() {
        warn!("Polling for changes every {} ms", args.poll_interval);
    }

    let result = watcher.watch(cwd);
    if let Err(e) = result {
        error!("Unable to watch directory/subdirectory: {}", e);
        return;
    }

    // Start child process initially, if necessary
    if args.run_initially {
        if args.clear_screen {
            cli::clear_screen();
        }

        let mut guard = child_process.write().unwrap();
        *guard = Some(process::spawn(&args.cmd, vec![]));
    }

    loop {
        let paths = wait(&rx, &filter);
        if let Some(path) = paths.get(0) {
            debug!("Path updated: {:?}", path);
        }

        // Wait for current child process to exit
        {
            let guard = child_process.read().unwrap();

            if let Some(ref child) = *guard {
                if args.restart {
                    debug!("Stopping child process");
                    if kill {
                        child.kill();
                    } else {
                        child.terminate();
                    }
                }

                debug!("Waiting for process to exit...");
                child.wait();
            }
        }

        // Launch child process
        if args.clear_screen {
            cli::clear_screen();
        }

        {
            let mut guard = child_process.write().unwrap();
            *guard = Some(process::spawn(&args.cmd, paths));
        }
    }
}

fn wait(rx: &Receiver<Event>, filter: &NotificationFilter) -> Vec<PathBuf> {
    let mut paths = vec![];
    let mut cache = HashMap::new();

    loop {
        let e = rx.recv().expect("error when reading event");

        if let Some(ref path) = e.path {
            // Ignore cache for the initial file. Otherwise, in
            // debug mode it's hard to track what's going on
            let excluded = filter.is_excluded(path);
            if !cache.contains_key(path) {
                cache.insert(path.to_owned(), excluded);
            }

            if !excluded {
                paths.push(path.to_owned());
                break;
            }
        }
    }

    // Wait for filesystem activity to cool off
    let timeout = Duration::from_millis(500);
    while let Ok(e) = rx.recv_timeout(timeout) {
        if let Some(ref path) = e.path {
            if cache.contains_key(path) {
                continue;
            }

            let excluded = filter.is_excluded(path);

            let p = path.to_owned();
            cache.insert(p.clone(), excluded);

            if !excluded {
                paths.push(p);
            }
        }
    }

    paths
}
<|endoftext|>


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

struct Graph {
	vertices: HashMap<i64, Box<Vertex>>,
}

impl Graph {
	fn new() -> Box<Graph> {
		let vertices: HashMap<i64, Box<Vertex>> = HashMap::new();
		Box::new(Graph{vertices:vertices})
	}
	fn add_vertex(&mut self) -> VertexProxy {
		let new_id = 1;
		let v = Vertex::new(new_id);

		let ptr: *const Box<Vertex> = &v as *const Box<Vertex>;

		self.vertices.insert(new_id, v);

		// return the proxy which knows it's own id
		VertexProxy{id:new_id, v: ptr}
	}
}

struct Vertex {
	id: i64,
	edges: *mut Vec<Edge>,
}

impl Vertex {
	fn new(id: i64) -> Box<Vertex> {

		let mut edges : Vec<Edge> = Vec::new();
		let edges_ptr :	*mut Vec<Edge> = &mut edges;
		let vertex = Vertex{id:id, edges:edges_ptr};
		let mut v = Box::new(vertex);
		return v;
	}

	fn query(self) {

	}
}

struct VertexProxy {
	id: i64,
	v: *const Box<Vertex>,
}

//let i: u32 = 1;
//// explicit cast
//let p_imm: *const u32 = &i as *const u32;
//let mut m: u32 = 2;
//// implicit coercion
//let p_mut: *mut u32 = &mut m;
//
//unsafe {
//let ref_imm: &u32 = &*p_imm;
//let ref_mut: &mut u32 = &mut *p_mut;
//}

impl VertexProxy {
	fn add_edge(& self, to_vertex: &VertexProxy) {
		unsafe {
			let in_vertex = &* self .v;
			let out_vertex = &*to_vertex.v;
		}
		// create the edge
		let e = Edge{from_vertex: self.v,
					 to_vertex:to_vertex.v};

	}
}

struct Edge {
	from_vertex: *const Box<Vertex>,
	to_vertex: *const Box<Vertex>
}

fn main() {
	let mut g = Graph::new();

    println!("Hello, world!");
}

#[test]
fn test_unsafe_vertex() {
	let mut g = Graph::new();
	let mut v1 = g.add_vertex();
}

#[test]
fn test_add_edge() {
	let mut g = Graph::new();
	let mut v1 = g.add_vertex();
	let mut v2 = g.add_vertex();
	v2.add_edge(&v2);

}
<commit_msg>derive show<commit_after>use std::mem;
use std::collections::HashMap;
use std::mem::transmute;

struct Graph {
	vertices: HashMap<i64, Box<Vertex>>,
}

impl Graph {
	fn new() -> Box<Graph> {
		let vertices: HashMap<i64, Box<Vertex>> = HashMap::new();
		Box::new(Graph{vertices:vertices})
	}
	fn add_vertex(&mut self) -> VertexProxy {
		let new_id = 1;
		let v = Vertex::new(new_id);

		let ptr: *const Box<Vertex> = &v as *const Box<Vertex>;

		self.vertices.insert(new_id, v);

		// return the proxy which knows it's own id
		VertexProxy{id:new_id, v: ptr}
	}
}

struct Vertex {
	id: i64,
	edges: *mut Vec<Edge>,
}

impl Vertex {
	fn new(id: i64) -> Box<Vertex> {

		let mut edges : Vec<Edge> = Vec::new();
		let edges_ptr :	*mut Vec<Edge> = &mut edges;
		let vertex = Vertex{id:id, edges:edges_ptr};
		let mut v = Box::new(vertex);
		return v;
	}

	fn query(self) {

	}
}

#[derive(Show)]
struct VertexProxy {
	id: i64,
	v: *const Box<Vertex>,
}

//let i: u32 = 1;
//// explicit cast
//let p_imm: *const u32 = &i as *const u32;
//let mut m: u32 = 2;
//// implicit coercion
//let p_mut: *mut u32 = &mut m;
//
//unsafe {
//let ref_imm: &u32 = &*p_imm;
//let ref_mut: &mut u32 = &mut *p_mut;
//}

impl VertexProxy {
	fn add_edge(& self, to_vertex: &VertexProxy) {
		unsafe {
			let in_vertex =  &* self .v;
			let out_vertex = &*to_vertex.v;
		}
		// create the edge
		let e = Edge{from_vertex: self.v,
					 to_vertex:   to_vertex.v};

	}
}

struct Edge {
	from_vertex: *const Box<Vertex>,
	to_vertex: *const Box<Vertex>
}

fn main() {
	let mut g = Graph::new();

    println!("Hello, world!");
}

#[test]
fn test_unsafe_vertex() {
	let mut g = Graph::new();
	let mut v1 = g.add_vertex();
}

#[test]
fn test_add_edge() {
	let mut g = Graph::new();
	let mut v1 = g.add_vertex();
	let mut v2 = g.add_vertex();
	v2.add_edge(&v2);

}
<|endoftext|>


<commit_before>extern crate rustc_serialize;
extern crate docopt;

use std::io::{Write, BufRead};
use docopt::Docopt;

static LF: char = '\n';
static NULL: char = '\0';
static VERSION: &'static str = "0.0.1";
static USAGE: &'static str = "
Usage: wc [options] FILE...
       wc [options] --files0-from=F
       wc (-h|--help)
       wc (-v|--version)

Print newline, word, and byte counts for each FILE, and a total line if
more than one FILE is specified. With no FILE, or when FILE is -,
read standard input. A word is a non-zero-length sequence of characters
delimited by white space.
The options below may be used to select which counts are printed, always in
the following order: newline, word, character, byte, maximum line length.

Options:
   -c, --bytes            print the byte counts
   -m, --chars            print the character counts
   -l, --lines            print the newline counts
   --files0-from=F        read input from the files specified by NUL-terminated
                          names in file F; If F is - then read names from
                          standard input
   -L, --max-line-length  print the length of the longest line
   -w, --words            print the word counts
   -h, --help             display this help and quit
   -v, --version          output version information and exit
";

#[allow(non_snake_case)]
#[derive(RustcDecodable, Debug)]
struct Args {
    arg_FILE: Vec<String>,
    flag_bytes: bool,
    flag_chars: bool,
    flag_lines: bool,
    flag_max_line_length: bool,
    flag_words: bool,
    flag_help: bool,
    flag_version: bool,
    flag_files0_from: String,
}

#[derive(Debug)]
enum ProcessingError {
    IO(std::io::Error),
    Utf8(std::str::Utf8Error),
}

impl std::fmt::Display for ProcessingError {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        match *self {
            ProcessingError::IO(ref e) => write!(f, "{}", e),
            ProcessingError::Utf8(ref e) => write!(f, "{}", e),
        }
    }
}

impl From<std::io::Error> for ProcessingError {
    fn from(e: std::io::Error) -> ProcessingError {
        ProcessingError::IO(e)
    }
}
impl From<std::str::Utf8Error> for ProcessingError {
    fn from(e: std::str::Utf8Error) -> ProcessingError {
        ProcessingError::Utf8(e)
    }
}

#[derive(Debug)]
struct FileInfo{
    bytes: usize,
    chars: usize,
    lines: usize,
    words: usize,
    max_line_length: usize,
}

type FileInfoResult = Result<FileInfo, ProcessingError>;

fn process_reader<T: std::io::Read>(reader: T) -> FileInfoResult {
    let mut info = FileInfo{
        bytes: 0,
        chars: 0,
        lines: 0,
        words: 0,
        max_line_length: 0,
    };
    // TODO: Only to as much processing as is absolutely necessary to
    // provide the data we will end up printing.
    let mut rbuf = std::io::BufReader::new(reader);
    let mut lbuf = Vec::new();
    loop {
        let size = try!(rbuf.read_until(LF as u8, &mut lbuf));
        info.bytes += size;
        if size == 0 {
            break;
        }
        // Create a scope because we're going to borrow lbuf and
        // the borrow must end before we can clear it.
        {
            // TODO: Handle files which are not utf8-encoded. Right
            // now we get an error here.
            let line = try!(std::str::from_utf8(&lbuf));
            let size = line.chars().count();
            info.max_line_length = match line.chars().last() {
                Some(c) => {
                    if c == LF {
                        info.lines += 1;
                        std::cmp::max(info.max_line_length, size - 1)
                    } else {
                        std::cmp::max(info.max_line_length, size)
                    }
                },
                None => std::cmp::max(info.max_line_length, size),
            };
            info.chars += size;
            let mut words: Vec<&str> = line
                .split(|c: char| c.is_whitespace())
                .collect();
            words.retain(|s: &&str| s.len() > 0);
            info.words += words.len();
        }
        lbuf.clear()
    }
    Ok(info)
}

macro_rules! println_stderr(
    ($($arg:tt)*) => (
        match writeln!(&mut ::std::io::stderr(), $($arg)* ) {
            Ok(_) => {},
            Err(x) => panic!("Unable to write to stderr: {}", x),
        }
        )
        );

// type NullDelimitedFileResult<'a> = Result<Vec<String>, ProcessingError>;
type NullDelimitedFileResult = Result<Vec<String>, ProcessingError>;

fn split_file_on_nulls<T: std::io::Read>(file: T) -> NullDelimitedFileResult {
    let mut rbuf = std::io::BufReader::new(file);
    let mut result = Vec::new();
    let mut lbuf = Vec::new();
    loop {
        let size = try!(rbuf.read_until(NULL as u8, &mut lbuf));
        if size == 0 {
            break;
        }
        {
            let line = try!(std::str::from_utf8(&lbuf));
            result.push(line.trim_right_matches(NULL).to_string());
        }
        lbuf.clear()
    }
    Ok(result)
}

fn main() {
    let args: Args = Docopt::new(USAGE)
        .and_then(|d| d.decode())
        .unwrap_or_else(|e| e.exit());

    if args.flag_version {
        println!("wc v{}", VERSION);
        std::process::exit(0);
    }

    if args.flag_files0_from.len() != 0  && args.arg_FILE.len() != 0 {
        println_stderr!("wc: file operands cannot be combined with --files0-from");
        println_stderr!("Try 'wc --help' for more information");
        std::process::exit(1);
    }

    let mut files: Vec<String> = Vec::new();
    if args.flag_files0_from.len() != 0 {
        let split_results = match args.flag_files0_from.as_ref() {
            "-" => split_file_on_nulls(std::io::stdin()),
            _ => {
                match std::fs::File::open(&args.flag_files0_from) {
                    Ok(f) => split_file_on_nulls(f),
                    Err(e) => {
                        println_stderr!("wc: cannot open {} for reading: {}",
                                        args.flag_files0_from, e);
                        std::process::exit(1);
                    },
                }
            },
        };
        match split_results {
            Ok(parts) => files.extend(parts),
            Err(e) => {
                println_stderr!("wc: error reading {}: {}",
                                args.flag_files0_from, e);
                std::process::exit(1);
            },
        };
        if &args.flag_files0_from == "-" && files.contains(&"-".to_string()) {
            println_stderr!("wc: when reading file names from stdin, no file name of '-' allowed");
            std::process::exit(1);
        }
    } else {
        files.extend(args.arg_FILE);
    };

    let mut results = Vec::new();
    let mut totals = FileInfo{
        bytes: 0,
        chars: 0,
        lines: 0,
        words: 0,
        max_line_length: 0,
    };
    for filename in files {
        let result = match filename.as_ref() {
            "-" => process_reader(std::io::stdin()),
            _ => {
                let file = std::fs::File::open(filename.to_string());
                match file {
                    Ok(f) => process_reader(f),
                    Err(e) => Err(ProcessingError::IO(e)),
                }
            }
        };
        match result {
            Ok(ref r) => {
                totals.chars += r.chars;
                totals.lines += r.lines;
                totals.bytes += r.bytes;
                totals.words += r.words;
                totals.max_line_length = std::cmp::max(totals.max_line_length,
                                                       r.max_line_length);
            },
            Err(_) => {},
        }
        results.push((filename, result));
    }

    // This is used for formatting. The number in the byte count will
    // be the largest, and so will be the widest string, so it's
    // suitable for a field width.
    let field_size = totals.bytes.to_string().len();

    if results.len() > 1 {
        results.push(("total".to_string(), Ok(totals)));
    }

    let mut errors_encountered = false;

    // Present the results
    for data in results.iter() {
        let (ref filename, ref result) = *data;
        match *result {
            Ok(ref r) => {
                // let mut parts = Vec::new();
                let mut requested_field = false;
                if args.flag_lines {
                    print!("{:1$} ", r.lines, field_size);
                    requested_field = true;
                }
                if args.flag_words {
                    print!("{:1$} ", r.words, field_size);
                    requested_field = true;
                }
                if args.flag_bytes {
                    print!("{:1$} ", r.bytes, field_size);
                    requested_field = true;
                }
                if args.flag_chars {
                    print!("{:1$} ", r.chars, field_size);
                    requested_field = true;
                }
                if args.flag_max_line_length {
                    print!("{:1$} ", r.max_line_length, field_size);
                    requested_field = true;
                }
                if !requested_field {
                    print!("{:1$} ", r.lines, field_size);
                    print!("{:1$} ", r.words, field_size);
                    print!("{:1$} ", r.bytes, field_size);
                }
                println!("{}", filename);
            },
            Err(ref e) => {
                errors_encountered = true;
                println_stderr!("wc: {}: {}", filename, e);
            },
        }
    }

    if errors_encountered {
        std::process::exit(1);
    }
}
<commit_msg>Reduce overuse of matching<commit_after>extern crate rustc_serialize;
extern crate docopt;

use std::io::{Write, BufRead};
use docopt::Docopt;

static LF: char = '\n';
static NULL: char = '\0';
static VERSION: &'static str = "0.0.1";
static USAGE: &'static str = "
Usage: wc [options] FILE...
       wc [options] --files0-from=F
       wc (-h|--help)
       wc (-v|--version)

Print newline, word, and byte counts for each FILE, and a total line if
more than one FILE is specified. With no FILE, or when FILE is -,
read standard input. A word is a non-zero-length sequence of characters
delimited by white space.
The options below may be used to select which counts are printed, always in
the following order: newline, word, character, byte, maximum line length.

Options:
   -c, --bytes            print the byte counts
   -m, --chars            print the character counts
   -l, --lines            print the newline counts
   --files0-from=F        read input from the files specified by NUL-terminated
                          names in file F; If F is - then read names from
                          standard input
   -L, --max-line-length  print the length of the longest line
   -w, --words            print the word counts
   -h, --help             display this help and quit
   -v, --version          output version information and exit
";

#[allow(non_snake_case)]
#[derive(RustcDecodable, Debug)]
struct Args {
    arg_FILE: Vec<String>,
    flag_bytes: bool,
    flag_chars: bool,
    flag_lines: bool,
    flag_max_line_length: bool,
    flag_words: bool,
    flag_help: bool,
    flag_version: bool,
    flag_files0_from: String,
}

#[derive(Debug)]
enum ProcessingError {
    IO(std::io::Error),
    Utf8(std::str::Utf8Error),
}

impl std::fmt::Display for ProcessingError {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        match *self {
            ProcessingError::IO(ref e) => write!(f, "{}", e),
            ProcessingError::Utf8(ref e) => write!(f, "{}", e),
        }
    }
}

impl From<std::io::Error> for ProcessingError {
    fn from(e: std::io::Error) -> ProcessingError {
        ProcessingError::IO(e)
    }
}
impl From<std::str::Utf8Error> for ProcessingError {
    fn from(e: std::str::Utf8Error) -> ProcessingError {
        ProcessingError::Utf8(e)
    }
}

#[derive(Debug)]
struct FileInfo{
    bytes: usize,
    chars: usize,
    lines: usize,
    words: usize,
    max_line_length: usize,
}

type FileInfoResult = Result<FileInfo, ProcessingError>;

fn process_reader<T: std::io::Read>(reader: T) -> FileInfoResult {
    let mut info = FileInfo{
        bytes: 0,
        chars: 0,
        lines: 0,
        words: 0,
        max_line_length: 0,
    };
    // TODO: Only to as much processing as is absolutely necessary to
    // provide the data we will end up printing.
    let mut rbuf = std::io::BufReader::new(reader);
    let mut lbuf = Vec::new();
    loop {
        let size = try!(rbuf.read_until(LF as u8, &mut lbuf));
        info.bytes += size;
        if size == 0 {
            break;
        }
        // Create a scope because we're going to borrow lbuf and
        // the borrow must end before we can clear it.
        {
            // TODO: Handle files which are not utf8-encoded. Right
            // now we get an error here.
            let line = try!(std::str::from_utf8(&lbuf));
            let size = line.chars().count();
            let last = line.chars().last().unwrap_or(NULL);
            info.max_line_length = if last == LF {
                info.lines += 1;
                std::cmp::max(info.max_line_length, size - 1)
            } else {
                std::cmp::max(info.max_line_length, size)
            };
            info.chars += size;
            let mut words: Vec<&str> = line
                .split(|c: char| c.is_whitespace())
                .collect();
            words.retain(|s: &&str| s.len() > 0);
            info.words += words.len();
        }
        lbuf.clear()
    }
    Ok(info)
}

macro_rules! println_stderr(
    ($($arg:tt)*) => (
        match writeln!(&mut ::std::io::stderr(), $($arg)* ) {
            Ok(_) => {},
            Err(x) => panic!("Unable to write to stderr: {}", x),
        }
        )
        );

// type NullDelimitedFileResult<'a> = Result<Vec<String>, ProcessingError>;
type NullDelimitedFileResult = Result<Vec<String>, ProcessingError>;

fn split_file_on_nulls<T: std::io::Read>(file: T) -> NullDelimitedFileResult {
    let mut rbuf = std::io::BufReader::new(file);
    let mut result = Vec::new();
    let mut lbuf = Vec::new();
    loop {
        let size = try!(rbuf.read_until(NULL as u8, &mut lbuf));
        if size == 0 {
            break;
        }
        {
            let line = try!(std::str::from_utf8(&lbuf));
            result.push(line.trim_right_matches(NULL).to_string());
        }
        lbuf.clear()
    }
    Ok(result)
}

fn main() {
    let args: Args = Docopt::new(USAGE)
        .and_then(|d| d.decode())
        .unwrap_or_else(|e| e.exit());

    if args.flag_version {
        println!("wc v{}", VERSION);
        std::process::exit(0);
    }

    if args.flag_files0_from.len() != 0  && args.arg_FILE.len() != 0 {
        println_stderr!("wc: file operands cannot be combined with --files0-from");
        println_stderr!("Try 'wc --help' for more information");
        std::process::exit(1);
    }

    let mut files: Vec<String> = Vec::new();
    if args.flag_files0_from.len() != 0 {
        let split_results = match args.flag_files0_from.as_ref() {
            "-" => split_file_on_nulls(std::io::stdin()),
            _ => {
                match std::fs::File::open(&args.flag_files0_from) {
                    Ok(f) => split_file_on_nulls(f),
                    Err(e) => {
                        println_stderr!("wc: cannot open {} for reading: {}",
                                        args.flag_files0_from, e);
                        std::process::exit(1);
                    },
                }
            },
        };
        match split_results {
            Ok(parts) => files.extend(parts),
            Err(e) => {
                println_stderr!("wc: error reading {}: {}",
                                args.flag_files0_from, e);
                std::process::exit(1);
            },
        };
        if &args.flag_files0_from == "-" && files.contains(&"-".to_string()) {
            println_stderr!("wc: when reading file names from stdin, no file name of '-' allowed");
            std::process::exit(1);
        }
    } else {
        files.extend(args.arg_FILE);
    };

    let mut results = Vec::new();
    let mut totals = FileInfo{
        bytes: 0,
        chars: 0,
        lines: 0,
        words: 0,
        max_line_length: 0,
    };
    for filename in files {
        let result = match filename.as_ref() {
            "-" => process_reader(std::io::stdin()),
            _ => {
                let file = std::fs::File::open(filename.to_string());
                match file {
                    Ok(f) => process_reader(f),
                    Err(e) => Err(ProcessingError::IO(e)),
                }
            }
        };
        match result {
            Ok(ref r) => {
                totals.chars += r.chars;
                totals.lines += r.lines;
                totals.bytes += r.bytes;
                totals.words += r.words;
                totals.max_line_length = std::cmp::max(totals.max_line_length,
                                                       r.max_line_length);
            },
            Err(_) => {},
        }
        results.push((filename, result));
    }

    // This is used for formatting. The number in the byte count will
    // be the largest, and so will be the widest string, so it's
    // suitable for a field width.
    let field_size = totals.bytes.to_string().len();

    if results.len() > 1 {
        results.push(("total".to_string(), Ok(totals)));
    }

    let mut errors_encountered = false;

    // Present the results
    for data in results.iter() {
        let (ref filename, ref result) = *data;
        match *result {
            Ok(ref r) => {
                // let mut parts = Vec::new();
                let mut requested_field = false;
                if args.flag_lines {
                    print!("{:1$} ", r.lines, field_size);
                    requested_field = true;
                }
                if args.flag_words {
                    print!("{:1$} ", r.words, field_size);
                    requested_field = true;
                }
                if args.flag_bytes {
                    print!("{:1$} ", r.bytes, field_size);
                    requested_field = true;
                }
                if args.flag_chars {
                    print!("{:1$} ", r.chars, field_size);
                    requested_field = true;
                }
                if args.flag_max_line_length {
                    print!("{:1$} ", r.max_line_length, field_size);
                    requested_field = true;
                }
                if !requested_field {
                    print!("{:1$} ", r.lines, field_size);
                    print!("{:1$} ", r.words, field_size);
                    print!("{:1$} ", r.bytes, field_size);
                }
                println!("{}", filename);
            },
            Err(ref e) => {
                errors_encountered = true;
                println_stderr!("wc: {}: {}", filename, e);
            },
        }
    }

    if errors_encountered {
        std::process::exit(1);
    }
}
<|endoftext|>