<commit_before>use std::fs::File;
use std::io::{Read, Write, Result};

mod buffer;
#[cfg(test)]
mod parser_tests;
#[cfg(test)]
mod interp_tests;

#[derive(Debug, Clone, Copy, PartialEq)]
enum Instruction {
    MoveLeft(usize),
    MoveRight(usize),
    Inc(u8),
    Dec(u8),
    Input,
    Output,
    LoopEntry(usize),
    LoopExit(usize),
}

use Instruction::*;

#[derive(Debug, PartialEq)]
struct Program {
    instructions: Vec<Instruction>,
}

impl Program {
    pub fn new(text: String) -> Program {
        // open brackets while parsing
        let mut bracket_stack = Vec::new();
        // idx to patch with index of LoopEntry to patch in
        let mut relocs = Vec::new();
        // "lex" token stream - aka skip whitespace
        let token = text.chars()
            .filter(move |c| {
                        *c == '>' || *c == '<' || *c == '+' || *c == '-' || *c == '.' ||
                        *c == ',' || *c == '[' || *c == ']'
                    });
        let mut instructions = token
            .enumerate()
            .map(|(idx, c)| {
                match c {
                    '<' => MoveLeft(1),
                    '>' => MoveRight(1),
                    '+' => Inc(1),
                    '-' => Dec(1),
                    '.' => Output,
                    ',' => Input,
                    '[' => {
                        bracket_stack.push(idx);
                        // value must be patched later
                        LoopEntry(std::usize::MAX)
                    }
                    ']' => {
                        if let Some(loop_entry) = bracket_stack.pop() {
                            relocs.push((loop_entry, idx));
                            LoopExit(loop_entry)
                        } else {
                            panic!("Unbalanced {:?} at pc={:}", c, idx);
                        }
                    }
                    c => panic!("Unknown instruction {:?} at pc={:}", c, idx),
                }
            })
            .collect::<Vec<_>>();
        if let Some(unbalanced_idx) = bracket_stack.pop() {
            panic!("Unbalanced {:?} at pc={:}", '[', unbalanced_idx);
        }
        for (idx, value) in relocs {
            if idx >= instructions.len() || LoopEntry(std::usize::MAX) != instructions[idx] {
                panic!("Unexpected instruction {:?} at pc={:} for reloc",
                       instructions[idx],
                       idx);
            } else {
                instructions[idx] = LoopEntry(value);
            }
        }
        Program { instructions: instructions }
    }

    pub fn optimize(&self) -> Program {
        // optimized program
        let mut instructions:Vec<Instruction> = Vec::new();
        // open brackets while parsing
        let mut bracket_stack = Vec::new();
        // idx to patch with index of LoopEntry to patch in
        let mut relocs = Vec::new();

        for (idx, instr) in self.instructions.iter().enumerate() {
            match *instr {
                MoveLeft(offset) => {
                    let last_instr = instructions.last().cloned();
                    if let Some(MoveLeft(old_offset)) = last_instr {
                        let last_idx = instructions.len() - 1;
                        instructions[last_idx] = MoveLeft(old_offset + offset);
                    }
                    else {
                        instructions.push(*instr);
                    }
                }
                MoveRight(offset) => {
                    let last_instr = instructions.last().cloned();
                    if let Some(MoveRight(old_offset)) = last_instr {
                        let last_idx = instructions.len() - 1;
                        instructions[last_idx] = MoveRight(old_offset + offset);
                    }
                    else {
                        instructions.push(*instr);
                    }
                }
                Inc(val) => {
                    let last_instr = instructions.last().cloned();
                    if let Some(Inc(old_val)) = last_instr {
                        let last_idx = instructions.len() - 1;
                        instructions[last_idx] = Inc(old_val.wrapping_add(val));
                    }
                    else {
                        instructions.push(*instr);
                    }
                }
                Dec(val) => {
                    let last_instr = instructions.last().cloned();
                    if let Some(Dec(old_val)) = last_instr {
                        let last_idx = instructions.len() - 1;
                        instructions[last_idx] = Dec(old_val.wrapping_add(val));
                    }
                    else {
                        instructions.push(*instr);
                    }
                }
                LoopEntry(_) => {
                    bracket_stack.push(instructions.len());
                    instructions.push(LoopEntry(std::usize::MAX));
                }
                LoopExit(_) => {
                    if let Some(loop_entry) = bracket_stack.pop() {
                        relocs.push((loop_entry, instructions.len()));
                        instructions.push(LoopExit(loop_entry));
                    } else {
                        panic!("Unbalanced {:?} at pc={:}", instr, idx);
                    }
                }
                instr => instructions.push(instr),
            }
        }
        if let Some(unbalanced_idx) = bracket_stack.pop() {
            panic!("Unbalanced {:?} at pc={:}", '[', unbalanced_idx);
        }
        for (idx, value) in relocs {
            if idx >= instructions.len() || LoopEntry(std::usize::MAX) != instructions[idx] {
                panic!("Unexpected instruction {:?} at pc={:} for reloc",
                       instructions[idx],
                       idx);
            } else {
                instructions[idx] = LoopEntry(value);
            }
        }
        Program { instructions: instructions }
    }

    pub fn interp(&self, input: &mut Read, output: &mut Write) {
        let mut memory = vec![0u8; 30000];

        let mut pc: usize = 0;
        let mut dataptr: usize = 0;

        fn get_char(input: &mut Read) -> u8 {
            let mut buf = [0u8; 1];
            match input.read(&mut buf) {
                Err(_) => panic!("Cannot read from stdin"),
                Ok(_) => buf[0],
            }
        }

        while pc < self.instructions.len() {
            match self.instructions[pc] {
                MoveLeft(offset) => dataptr -= offset,
                MoveRight(offset) => dataptr += offset,
                Inc(increment) => memory[dataptr] = memory[dataptr].wrapping_add(increment),
                Dec(decrement) => memory[dataptr] = memory[dataptr].wrapping_sub(decrement),
                Output => write!(output, "{:}", memory[dataptr] as char).expect("Output error"),
                Input => memory[dataptr] = get_char(input),
                LoopEntry(target) => {
                    if 0 == memory[dataptr] {
                        pc = target;
                    }
                }
                LoopExit(target) => {
                    if 0 != memory[dataptr] {
                        pc = target;
                    }
                }
                // ref c => panic!("Unknown instruction {:?} at pc={:}", c, pc),
            }
            pc += 1;
        }
    }
}

fn load_program(fname: String) -> Result<Program> {
    let mut file = File::open(fname)?;
    let mut contents: Vec<u8> = Vec::new();
    file.read_to_end(&mut contents)?;
    let text = String::from_utf8(contents).unwrap();

    Ok(Program::new(text))
}

fn main() {
    match load_program("examples/mandelbrot.bf".to_string()) {
        Ok(ref mut p) => {
            let p = p.optimize();
            //println!("{:?}", p);
            p.interp(&mut std::io::stdin(), &mut std::io::stdout());
        }
        Err(err) => panic!("Cannot read file because {:?}", err),
    }
}
<commit_msg>reformat<commit_after>use std::fs::File;
use std::io::{Read, Write, Result};

mod buffer;
#[cfg(test)]
mod parser_tests;
#[cfg(test)]
mod interp_tests;

#[derive(Debug, Clone, Copy, PartialEq)]
enum Instruction {
    MoveLeft(usize),
    MoveRight(usize),
    Inc(u8),
    Dec(u8),
    Input,
    Output,
    LoopEntry(usize),
    LoopExit(usize),
}

use Instruction::*;

#[derive(Debug, PartialEq)]
struct Program {
    instructions: Vec<Instruction>,
}

impl Program {
    pub fn new(text: String) -> Program {
        // open brackets while parsing
        let mut bracket_stack = Vec::new();
        // idx to patch with index of LoopEntry to patch in
        let mut relocs = Vec::new();
        // "lex" token stream - aka skip whitespace
        let token = text.chars()
            .filter(move |c| {
                        *c == '>' || *c == '<' || *c == '+' || *c == '-' || *c == '.' ||
                        *c == ',' || *c == '[' || *c == ']'
                    });
        let mut instructions = token
            .enumerate()
            .map(|(idx, c)| {
                match c {
                    '<' => MoveLeft(1),
                    '>' => MoveRight(1),
                    '+' => Inc(1),
                    '-' => Dec(1),
                    '.' => Output,
                    ',' => Input,
                    '[' => {
                        bracket_stack.push(idx);
                        // value must be patched later
                        LoopEntry(std::usize::MAX)
                    }
                    ']' => {
                        if let Some(loop_entry) = bracket_stack.pop() {
                            relocs.push((loop_entry, idx));
                            LoopExit(loop_entry)
                        } else {
                            panic!("Unbalanced {:?} at pc={:}", c, idx);
                        }
                    }
                    c => panic!("Unknown instruction {:?} at pc={:}", c, idx),
                }
            })
            .collect::<Vec<_>>();
        if let Some(unbalanced_idx) = bracket_stack.pop() {
            panic!("Unbalanced {:?} at pc={:}", '[', unbalanced_idx);
        }
        for (idx, value) in relocs {
            if idx >= instructions.len() || LoopEntry(std::usize::MAX) != instructions[idx] {
                panic!("Unexpected instruction {:?} at pc={:} for reloc",
                       instructions[idx],
                       idx);
            } else {
                instructions[idx] = LoopEntry(value);
            }
        }
        Program { instructions: instructions }
    }

    pub fn optimize(&self) -> Program {
        // optimized program
        let mut instructions: Vec<Instruction> = Vec::new();
        // open brackets while parsing
        let mut bracket_stack = Vec::new();
        // idx to patch with index of LoopEntry to patch in
        let mut relocs = Vec::new();

        for (idx, instr) in self.instructions.iter().enumerate() {
            match *instr {
                MoveLeft(offset) => {
                    let last_instr = instructions.last().cloned();
                    if let Some(MoveLeft(old_offset)) = last_instr {
                        let last_idx = instructions.len() - 1;
                        instructions[last_idx] = MoveLeft(old_offset + offset);
                    } else {
                        instructions.push(*instr);
                    }
                }
                MoveRight(offset) => {
                    let last_instr = instructions.last().cloned();
                    if let Some(MoveRight(old_offset)) = last_instr {
                        let last_idx = instructions.len() - 1;
                        instructions[last_idx] = MoveRight(old_offset + offset);
                    } else {
                        instructions.push(*instr);
                    }
                }
                Inc(val) => {
                    let last_instr = instructions.last().cloned();
                    if let Some(Inc(old_val)) = last_instr {
                        let last_idx = instructions.len() - 1;
                        instructions[last_idx] = Inc(old_val.wrapping_add(val));
                    } else {
                        instructions.push(*instr);
                    }
                }
                Dec(val) => {
                    let last_instr = instructions.last().cloned();
                    if let Some(Dec(old_val)) = last_instr {
                        let last_idx = instructions.len() - 1;
                        instructions[last_idx] = Dec(old_val.wrapping_add(val));
                    } else {
                        instructions.push(*instr);
                    }
                }
                LoopEntry(_) => {
                    bracket_stack.push(instructions.len());
                    instructions.push(LoopEntry(std::usize::MAX));
                }
                LoopExit(_) => {
                    if let Some(loop_entry) = bracket_stack.pop() {
                        relocs.push((loop_entry, instructions.len()));
                        instructions.push(LoopExit(loop_entry));
                    } else {
                        panic!("Unbalanced {:?} at pc={:}", instr, idx);
                    }
                }
                instr => instructions.push(instr),
            }
        }
        if let Some(unbalanced_idx) = bracket_stack.pop() {
            panic!("Unbalanced {:?} at pc={:}", '[', unbalanced_idx);
        }
        for (idx, value) in relocs {
            if idx >= instructions.len() || LoopEntry(std::usize::MAX) != instructions[idx] {
                panic!("Unexpected instruction {:?} at pc={:} for reloc",
                       instructions[idx],
                       idx);
            } else {
                instructions[idx] = LoopEntry(value);
            }
        }
        Program { instructions: instructions }
    }

    pub fn interp(&self, input: &mut Read, output: &mut Write) {
        let mut memory = vec![0u8; 30000];

        let mut pc: usize = 0;
        let mut dataptr: usize = 0;

        fn get_char(input: &mut Read) -> u8 {
            let mut buf = [0u8; 1];
            match input.read(&mut buf) {
                Err(_) => panic!("Cannot read from stdin"),
                Ok(_) => buf[0],
            }
        }

        while pc < self.instructions.len() {
            match self.instructions[pc] {
                MoveLeft(offset) => dataptr -= offset,
                MoveRight(offset) => dataptr += offset,
                Inc(increment) => memory[dataptr] = memory[dataptr].wrapping_add(increment),
                Dec(decrement) => memory[dataptr] = memory[dataptr].wrapping_sub(decrement),
                Output => write!(output, "{:}", memory[dataptr] as char).expect("Output error"),
                Input => memory[dataptr] = get_char(input),
                LoopEntry(target) => {
                    if 0 == memory[dataptr] {
                        pc = target;
                    }
                }
                LoopExit(target) => {
                    if 0 != memory[dataptr] {
                        pc = target;
                    }
                }
                // ref c => panic!("Unknown instruction {:?} at pc={:}", c, pc),
            }
            pc += 1;
        }
    }
}

fn load_program(fname: String) -> Result<Program> {
    let mut file = File::open(fname)?;
    let mut contents: Vec<u8> = Vec::new();
    file.read_to_end(&mut contents)?;
    let text = String::from_utf8(contents).unwrap();

    Ok(Program::new(text))
}

fn main() {
    match load_program("examples/mandelbrot.bf".to_string()) {
        Ok(ref mut p) => {
            let p = p.optimize();
            //println!("{:?}", p);
            p.interp(&mut std::io::stdin(), &mut std::io::stdout());
        }
        Err(err) => panic!("Cannot read file because {:?}", err),
    }
}
<|endoftext|>


<commit_before>//! Implements `StdTimeManager`.

use std::sync::RwLock;
use std::time::{SystemTime, Duration};
use board::*;
use depth::*;
use value::*;
use search_executor::*;
use hash_table::Variation;
use search_node::SearchNode;
use time_manager::{TimeManager, RemainingTime};
use uci::{SetOption, OptionDescription};

/// Implements the `TimeManager` trait.
pub struct StdTimeManager {
    started_at: SystemTime,
    depth: Depth,
    value: Value,
    extrapolation_points: Vec<(f64, f64)>,
    hard_limit: f64,
    allotted_time: f64,
    must_play: bool,
}

impl<T> TimeManager<T> for StdTimeManager
    where T: SearchExecutor<ReportData = Vec<Variation>>
{
    fn new(position: &T::SearchNode, time: &RemainingTime) -> StdTimeManager {
        // Get our remaining time and increment (in milliseconds).
        let (t, inc) = if position.board().to_move == WHITE {
            (time.white_millis as f64, time.winc_millis as f64)
        } else {
            (time.black_millis as f64, time.binc_millis as f64)
        };

        // Get the number of moves until the next time control, or if
        // not available, guess the number of moves to the end of the
        // game.
        let n = time.movestogo.unwrap_or(40) as f64;
        debug_assert!(n >= 1.0);

        // Calculate the total time we have.
        let time_heap = t + inc * (n - 1.0);

        // Set a hard limit for the time we will spend on this
        // move. Thinking longer that that would be reckless.
        let hard_limit = (t / n.sqrt() + inc).min(t - 1000.0);

        StdTimeManager {
            started_at: SystemTime::now(),
            depth: 0,
            value: VALUE_UNKNOWN,
            extrapolation_points: Vec::with_capacity(32),
            hard_limit: if position.legal_moves().len() > 1 {
                hard_limit
            } else {
                // When there is only one legal move, the engine is
                // allowed to think just a fraction of a second in
                // order to find a good ponder move.
                hard_limit.min(500.0)
            },
            allotted_time: if *PONDERING_IS_ALLOWED.read().unwrap() {
                // Statistically, the move we ponder will be played in
                // 50% of the cases. Therefore, in principal we should
                // add half of opponent's thinking time to our time
                // heap. In reality we do not know how opponent's time
                // will be spend, so we speculatively increase our
                // time heap by 50%.
                1.5 * time_heap / n
            } else {
                time_heap / n
            },
            must_play: false,
        }
    }

    #[allow(unused_variables)]
    fn must_play(&mut self,
                 search_executor: &mut T,
                 report: Option<&SearchReport<Vec<Variation>>>)
                 -> bool {
        if !self.must_play {
            let mut is_finished = false;
            if let Some(r) = report {
                if r.depth > self.depth {
                    self.depth = r.depth;
                    is_finished = self.is_deep_enough(r);
                }
            }
            self.must_play = is_finished || elapsed_millis(&self.started_at) > self.hard_limit;
        }
        self.must_play
    }
}

impl SetOption for StdTimeManager {
    fn options() -> Vec<(String, OptionDescription)> {
        vec![("Ponder".to_string(), OptionDescription::Check { default: false })]
    }

    fn set_option(name: &str, value: &str) {
        if name == "Ponder" {
            match value {
                "true" => *PONDERING_IS_ALLOWED.write().unwrap() = true,
                "false" => *PONDERING_IS_ALLOWED.write().unwrap() = false,
                _ => (),
            }
        }
    }
}

impl StdTimeManager {
    /// A helper method. It decides whether we should stop now or we
    /// should search deeper.
    fn is_deep_enough(&mut self, report: &SearchReport<Vec<Variation>>) -> bool {
        if report.searched_nodes < 100 {
            return false; // We ignore the first few depths.
        }
        let t = elapsed_millis(&self.started_at);
        let depth = report.depth as f64;
        let searched_nodes = report.searched_nodes as f64;

        // We maintain a list of data points so as to be able to
        // intelligently guess how much time it will take for the
        // next search depth to complete. (We apply an exponential
        // regression over the last `M` points in the list.)
        const M: usize = 5;
        self.extrapolation_points.push((depth, searched_nodes.ln()));
        let expected_duration = match self.extrapolation_points.len() {
            n if n >= M => {
                let last_m = &self.extrapolation_points[n - M..];
                let factor = (extrapolate(last_m, depth + 1.0).exp() / searched_nodes).max(1.0);

                // Update `BRANCHING_FACTOR`. This is an average
                // branching factor calculated over the last few
                // moves.
                if n == M {
                    let mut bf = BRANCHING_FACTOR.write().unwrap();
                    *bf = (*bf * 2.0 + factor) / 3.0;
                }

                factor * t
            }
            _ => *BRANCHING_FACTOR.read().unwrap() * t,
        };

        // We may need to revise the allotted time if position's
        // evaluation has changed a lot with the newly completed
        // depth.
        //
        // TODO: `25` must be bound to pawn's value.
        if (expected_duration > self.allotted_time) &&
           (self.value != VALUE_UNKNOWN && report.value != VALUE_UNKNOWN) &&
           (self.value as isize - report.value as isize).abs() >= 25 {
            self.allotted_time = expected_duration;
        }
        self.value = report.value;

        // We try to stay as close as possible to the allotted
        // time, without crossing the hard limit.
        expected_duration > self.hard_limit ||
        (expected_duration > self.allotted_time &&
         expected_duration - self.allotted_time > self.allotted_time - t)
    }
}

lazy_static! {
    static ref PONDERING_IS_ALLOWED: RwLock<bool> = RwLock::new(false);
    static ref BRANCHING_FACTOR: RwLock<f64> = RwLock::new(2.0);
}

/// A helper function. It calculates the elapsed milliseconds since a
/// given time.
fn elapsed_millis(since: &SystemTime) -> f64 {
    let d = since.elapsed().unwrap_or(Duration::from_millis(0));
    (1000 * d.as_secs()) as f64 + (d.subsec_nanos() / 1_000_000) as f64
}

/// A helper function. It linearly extrapolates the value y(x) using
/// the (x, y) values in `points` as a reference.
fn extrapolate(points: &[(f64, f64)], x: f64) -> f64 {
    debug_assert!(points.len() > 1);
    let sum_x = points.iter().fold(0.0, |acc, &p| acc + p.0);
    let sum_y = points.iter().fold(0.0, |acc, &p| acc + p.1);
    let sum_xx = points.iter().fold(0.0, |acc, &p| acc + p.0 * p.0);
    let sum_xy = points.iter().fold(0.0, |acc, &p| acc + p.0 * p.1);
    let n = points.len() as f64;
    let slope = (n * sum_xy - sum_x * sum_y) / (n * sum_xx - sum_x * sum_x);
    let intercept = (sum_y - slope * sum_x) / n;
    slope * x + intercept
}

#[cfg(test)]
mod tests {
    #[test]
    fn linear_regression() {
        use super::extrapolate;
        let points = vec![(21.0, 1.0), (22.0, 2.0), (23.0, 3.0), (24.0, 4.0)];
        let x = 25.0;
        let y = extrapolate(&points, x);
        assert!(4.99 < y && y < 5.01);
    }
}
<commit_msg>Do not use `search_nodes` in StdTimeManager::must_play()<commit_after>//! Implements `StdTimeManager`.

use std::sync::RwLock;
use std::time::{SystemTime, Duration};
use board::*;
use depth::*;
use value::*;
use search_executor::*;
use hash_table::Variation;
use search_node::SearchNode;
use time_manager::{TimeManager, RemainingTime};
use uci::{SetOption, OptionDescription};

/// Implements the `TimeManager` trait.
pub struct StdTimeManager {
    started_at: SystemTime,
    depth: Depth,
    value: Value,
    extrapolation_points: Vec<(f64, f64)>,
    hard_limit: f64,
    allotted_time: f64,
    must_play: bool,
}

impl<T> TimeManager<T> for StdTimeManager
    where T: SearchExecutor<ReportData = Vec<Variation>>
{
    fn new(position: &T::SearchNode, time: &RemainingTime) -> StdTimeManager {
        // Get our remaining time and increment (in milliseconds).
        let (t, inc) = if position.board().to_move == WHITE {
            (time.white_millis as f64, time.winc_millis as f64)
        } else {
            (time.black_millis as f64, time.binc_millis as f64)
        };

        // Get the number of moves until the next time control, or if
        // not available, guess the number of moves to the end of the
        // game.
        let n = time.movestogo.unwrap_or(40) as f64;
        debug_assert!(n >= 1.0);

        // Calculate the total time we have.
        let time_heap = t + inc * (n - 1.0);

        // Set a hard limit for the time we will spend on this
        // move. Thinking longer that that would be reckless.
        let hard_limit = (t / n.sqrt() + inc).min(t - 1000.0);

        StdTimeManager {
            started_at: SystemTime::now(),
            depth: 0,
            value: VALUE_UNKNOWN,
            extrapolation_points: Vec::with_capacity(32),
            hard_limit: if position.legal_moves().len() > 1 {
                hard_limit
            } else {
                // When there is only one legal move, the engine is
                // allowed to think just a fraction of a second in
                // order to find a good ponder move.
                hard_limit.min(500.0)
            },
            allotted_time: if *PONDERING_IS_ALLOWED.read().unwrap() {
                // Statistically, the move we ponder will be played in
                // 50% of the cases. Therefore, in principal we should
                // add half of opponent's thinking time to our time
                // heap. In reality we do not know how opponent's time
                // will be spend, so we speculatively increase our
                // time heap by 50%.
                1.5 * time_heap / n
            } else {
                time_heap / n
            },
            must_play: false,
        }
    }

    #[allow(unused_variables)]
    fn must_play(&mut self,
                 search_executor: &mut T,
                 report: Option<&SearchReport<Vec<Variation>>>)
                 -> bool {
        if !self.must_play {
            let mut is_finished = false;
            if let Some(r) = report {
                if r.depth > self.depth {
                    self.depth = r.depth;
                    is_finished = self.is_deep_enough(r);
                }
            }
            self.must_play = is_finished || elapsed_millis(&self.started_at) > self.hard_limit;
        }
        self.must_play
    }
}

impl SetOption for StdTimeManager {
    fn options() -> Vec<(String, OptionDescription)> {
        vec![("Ponder".to_string(), OptionDescription::Check { default: false })]
    }

    fn set_option(name: &str, value: &str) {
        if name == "Ponder" {
            match value {
                "true" => *PONDERING_IS_ALLOWED.write().unwrap() = true,
                "false" => *PONDERING_IS_ALLOWED.write().unwrap() = false,
                _ => (),
            }
        }
    }
}

impl StdTimeManager {
    /// A helper method. It decides whether we should stop now or we
    /// should search deeper.
    fn is_deep_enough(&mut self, report: &SearchReport<Vec<Variation>>) -> bool {
        let t = elapsed_millis(&self.started_at);
        if t == 0.0 || report.searched_nodes < 100 {
            // We ignore the first few depths.
            return false;
        }

        // We maintain a list of data points so as to be able to
        // intelligently guess how much time it will take for the
        // next search depth to complete. (We apply an exponential
        // regression over the last `M` points in the list.)
        const M: usize = 5;
        let x = report.depth as f64;
        let y = t.ln();
        self.extrapolation_points.push((x, y));
        let expected_duration = match self.extrapolation_points.len() {
            n if n >= M => {
                let last_m = &self.extrapolation_points[n - M..];
                let factor = (extrapolate_y(last_m, x + 1.0).exp() / t).max(1.0);

                // let x_max = extrapolate_x(last_m, y_max);
                // let slope = (y_max - y) / (x_max - x);

                // Update `BRANCHING_FACTOR`. This is an average
                // branching factor calculated over the last few
                // moves.
                if n == M {
                    let mut bf = BRANCHING_FACTOR.write().unwrap();
                    *bf = (*bf * 2.0 + factor) / 3.0;
                }

                factor * t
            }
            _ => *BRANCHING_FACTOR.read().unwrap() * t,
        };

        // We may need to revise the allotted time if position's
        // evaluation has changed a lot with the newly completed
        // depth.
        //
        // TODO: `25` must be bound to pawn's value.
        if (expected_duration > self.allotted_time) &&
           (self.value != VALUE_UNKNOWN && report.value != VALUE_UNKNOWN) &&
           (self.value as isize - report.value as isize).abs() >= 25 {
            self.allotted_time = expected_duration;
        }
        self.value = report.value;

        // We try to stay as close as possible to the allotted
        // time, without crossing the hard limit.
        1.33 * expected_duration > self.hard_limit ||
        (expected_duration > self.allotted_time &&
         expected_duration - self.allotted_time > self.allotted_time - t)
    }
}

lazy_static! {
    static ref PONDERING_IS_ALLOWED: RwLock<bool> = RwLock::new(false);
    static ref BRANCHING_FACTOR: RwLock<f64> = RwLock::new(2.0);
}

/// A helper function. It calculates the elapsed milliseconds since a
/// given time.
fn elapsed_millis(since: &SystemTime) -> f64 {
    let d = since.elapsed().unwrap_or(Duration::from_millis(0));
    (1000 * d.as_secs()) as f64 + (d.subsec_nanos() / 1_000_000) as f64
}

/// A helper function. It linearly extrapolates the value y(x) using
/// the (x, y) values in `points` as a reference.
fn extrapolate_y(points: &[(f64, f64)], x: f64) -> f64 {
    debug_assert!(points.len() > 1);
    let sum_x = points.iter().fold(0.0, |acc, &p| acc + p.0);
    let sum_y = points.iter().fold(0.0, |acc, &p| acc + p.1);
    let sum_xx = points.iter().fold(0.0, |acc, &p| acc + p.0 * p.0);
    let sum_xy = points.iter().fold(0.0, |acc, &p| acc + p.0 * p.1);
    let n = points.len() as f64;
    let slope = (n * sum_xy - sum_x * sum_y) / (n * sum_xx - sum_x * sum_x);
    let intercept = (sum_y - slope * sum_x) / n;
    slope * x + intercept
}

/// A helper function. It linearly extrapolates the value x(y) using
/// the (x, y) values in `points` as a reference.
fn extrapolate_x(points: &[(f64, f64)], y: f64) -> f64 {
    debug_assert!(points.len() > 1);
    let sum_x = points.iter().fold(0.0, |acc, &p| acc + p.0);
    let sum_y = points.iter().fold(0.0, |acc, &p| acc + p.1);
    let sum_xx = points.iter().fold(0.0, |acc, &p| acc + p.0 * p.0);
    let sum_xy = points.iter().fold(0.0, |acc, &p| acc + p.0 * p.1);
    let n = points.len() as f64;
    let slope = (n * sum_xy - sum_x * sum_y) / (n * sum_xx - sum_x * sum_x);
    let intercept = (sum_y - slope * sum_x) / n;
    (y - intercept) / slope.max(0.01)
}

#[cfg(test)]
mod tests {
    #[test]
    fn linear_regression() {
        use super::{extrapolate_x, extrapolate_y};
        let points = vec![(21.0, 1.0), (22.0, 2.0), (23.0, 3.0), (24.0, 4.0)];
        let x = 25.0;
        let y = extrapolate_y(&points, x);
        assert!(4.99 < y && y < 5.01);
        let y = 5.0;
        let x = extrapolate_x(&points, y);
        assert!(24.99 < x && x < 25.01);
    }
}
<|endoftext|>


<commit_before>#[macro_use]
extern crate lazy_static;
extern crate regex;
extern crate hyper;
extern crate hubcaps;
extern crate afterparty;
extern crate serde_json;
extern crate rustc_serialize;
extern crate toml;

use std::collections::{HashMap, HashSet};
use std::fs::File;
use std::env;
use std::io::Read;

use hyper::{Client, Server, Url};
use hyper::header::{Headers, Authorization, Bearer, UserAgent};
use hubcaps::{Credentials, Github};
use afterparty::*;
use serde_json::Value;
use regex::*;
use lazy_static::*;
use toml::decode_str;

#[derive(RustcEncodable, RustcDecodable)]
struct RepoData {
    pub qa_user: String,
    pub qa_flags: Vec<String>,
}

#[derive(RustcEncodable, RustcDecodable)]
struct ConfigData {
    pub api_root: String,
    pub repos: HashMap<String, RepoData>,
}

fn read_config_file(file_name: &str) -> ConfigData {
    let mut config_file = File::open(file_name).expect("Couldn't open config file");
    let mut str = String::new();
    config_file.read_to_string(&mut str).expect("Couldn't read config file");
    let toml: Option<ConfigData> = decode_str(&str);
    match toml {
        Some(cfg) => cfg,
        _ => panic!("Couldn't decode config file"),
    }
}

#[test]
fn it_reads_toml() {
    let config_data = read_config_file("test-data/test.toml");
    assert_eq!(config_data.api_root, "github.ibm.com/api/v3");
    let steve = config_data.repos.get("lkgrele/steve").unwrap();
    assert_eq!(steve.qa_user, "leif");
    assert_eq!(steve.qa_flags, ["qa", "test"]);
    let raul = config_data.repos.get("raul/robot").unwrap();
    assert_eq!(raul.qa_user, "raul");
    assert_eq!(raul.qa_flags, ["qa"]);
}

fn main() {
    let mut hub = Hub::new();
    hub.handle("pull_request", |delivery: &Delivery| {
        match delivery.payload {
            Event::PullRequest { number, ref pull_request, ref repository, .. } => {
                if pull_request.merged {
                    handle_pr(number, &pull_request.commits_url, &repository.full_name)
                }
            }
            _ => (),
        }
    });

    Server::http("0.0.0.0").unwrap().handle(hub).unwrap();
}

fn handle_pr(number: u64, commits_url: &str, repository: &str) {
    let config_data = read_config_file(".steve");
    let repo_config = config_data.repos.get(&repository.to_owned());
    let auth_token = env::var_os("STEVE_GITHUB_TOKEN")
        .expect("Missing github token!")
        .into_string()
        .unwrap();

    match repo_config {
        Some(config) => {
            update_issues(number,
                          &commits_url,
                          &auth_token,
                          &config,
                          &config_data.api_root,
                          &repository)
        }
        None => (), //just don't do anything if we don't have a config for this repo
    }
}

fn update_issues(number: u64,
                 commits_url: &str,
                 auth_token: &str,
                 config: &RepoData,
                 api_root: &str,
                 repository: &str) {
    let client = Client::new();
    let mut headers = Headers::new();
    headers.set(Authorization(Bearer { token: auth_token.to_owned() }));
    headers.set(UserAgent("steve".to_owned()));
    let commits = client.get(commits_url).headers(headers).send().unwrap();
    assert_eq!(commits.status, hyper::Ok); //replace with actual error handling

    let data: Value = serde_json::from_reader(commits).unwrap();
    let arr = data.as_array().unwrap();
    let mut issues = HashSet::new();
    for val in arr {
        // needs real error handling, shouldn't panic when it gets bad json
        let message = val.as_object().unwrap().get("message").unwrap().as_string().unwrap();
        get_issues(&mut issues, &message);
    }

    for issue in issues {
        update_issue(&client, auth_token, issue, config, &repository, api_root);
    }
}

fn update_issue(client: &Client,
                auth_token: &str,
                issue_num: u64,
                config: &RepoData,
                repository: &str,
                api_root: &str) {
    let github = Github::host(api_root,
                              "steve",
                              client,
                              Credentials::Token(auth_token.to_owned()));
    let (owner, repo_name) = match repository.split("/").collect::<Vec<_>>() {
        strs => (strs[0], strs[1]),
    };
    github.repo(repo_name, owner)
        .issue(issue_num)
        .labels()
        .add(config.qa_flags.iter().map(String::as_str).collect());
    let mut headers = Headers::new();
    headers.set(Authorization(Bearer { token: auth_token.to_owned() }));
    headers.set(UserAgent("steve".to_owned()));
    let update_json = format!("{{\"assignee\":\"{}\"}}", config.qa_user);
    let url = Url::parse(&format!("{}/repos/{}/issues/{}", api_root, repository, issue_num))
        .expect("bad url");
    let res = client.patch(url)
        .body(&update_json)
        .headers(headers.to_owned())
        .send()
        .unwrap();
    assert_eq!(res.status, hyper::Ok);
}

fn get_issues(numbers: &mut HashSet<u64>, message: &str) {
    lazy_static! {
        static ref RE: Regex = Regex::new(r"(?i)needs\sqa:?\s#(\d+)").unwrap();
    }
    for cap in RE.captures_iter(message) {
        if let Some(num) = cap.at(1) {
            numbers.insert(num.parse::<u64>().unwrap());
        }
    }
}
<commit_msg>Clean up a couple things.<commit_after>#[macro_use]
extern crate lazy_static;
extern crate regex;
extern crate hyper;
extern crate hubcaps;
extern crate afterparty;
extern crate serde_json;
extern crate rustc_serialize;
extern crate toml;

use std::collections::{HashMap, HashSet};
use std::fs::File;
use std::env;
use std::io::Read;

use hyper::{Client, Server, Url};
use hyper::header::{Headers, Authorization, Bearer, UserAgent};
use hubcaps::{Credentials, Github};
use afterparty::*;
use serde_json::Value;
use regex::Regex;
use toml::decode_str;

#[derive(RustcEncodable, RustcDecodable)]
struct RepoData {
    pub qa_user: String,
    pub qa_flags: Vec<String>,
}

#[derive(RustcEncodable, RustcDecodable)]
struct ConfigData {
    pub api_root: String,
    pub repos: HashMap<String, RepoData>,
}

fn read_config_file(file_name: &str) -> ConfigData {
    let mut config_file = File::open(file_name).expect("Couldn't open config file");
    let mut str = String::new();
    config_file.read_to_string(&mut str).expect("Couldn't read config file");
    let toml: Option<ConfigData> = decode_str(&str);
    match toml {
        Some(cfg) => cfg,
        _ => panic!("Couldn't decode config file"),
    }
}

#[test]
fn it_reads_toml() {
    let config_data = read_config_file("test-data/test.toml");
    assert_eq!(config_data.api_root, "github.ibm.com/api/v3");
    let steve = config_data.repos.get("lkgrele/steve").unwrap();
    assert_eq!(steve.qa_user, "leif");
    assert_eq!(steve.qa_flags, ["qa", "test"]);
    let raul = config_data.repos.get("raul/robot").unwrap();
    assert_eq!(raul.qa_user, "raul");
    assert_eq!(raul.qa_flags, ["qa"]);
}

fn main() {
    let mut hub = Hub::new();
    hub.handle("pull_request", |delivery: &Delivery| {
        match delivery.payload {
            Event::PullRequest { number, ref pull_request, ref repository, .. } => {
                if pull_request.merged {
                    handle_pr(number, &pull_request.commits_url, &repository.full_name)
                }
            }
            _ => (),
        }
    });

    Server::http("0.0.0.0").unwrap().handle(hub).unwrap();
}

fn handle_pr(number: u64, commits_url: &str, repository: &str) {
    let config_data = read_config_file(".steve");
    let repo_config = config_data.repos.get(&repository.to_owned());
    let auth_token = env::var_os("STEVE_GITHUB_TOKEN")
        .expect("Missing github token!")
        .into_string()
        .unwrap();

    match repo_config {
        Some(config) => {
            update_issues(number,
                          &commits_url,
                          &auth_token,
                          &config,
                          &config_data.api_root,
                          &repository)
        }
        None => (), //just don't do anything if we don't have a config for this repo
    }
}

fn update_issues(number: u64,
                 commits_url: &str,
                 auth_token: &str,
                 config: &RepoData,
                 api_root: &str,
                 repository: &str) {
    let client = Client::new();
    let mut headers = Headers::new();
    headers.set(Authorization(Bearer { token: auth_token.to_owned() }));
    headers.set(UserAgent("steve".to_owned()));
    let commits = client.get(commits_url).headers(headers).send().unwrap();
    assert_eq!(commits.status, hyper::Ok); //replace with actual error handling

    let data: Value = serde_json::from_reader(commits).unwrap();
    let arr = data.as_array().unwrap();
    let mut issues = HashSet::new();
    for val in arr {
        // needs real error handling, shouldn't panic when it gets bad json
        let message = val.as_object().unwrap().get("message").unwrap().as_string().unwrap();
        get_issues(&mut issues, &message);
    }

    for issue in issues {
        update_issue(&client, auth_token, issue, config, &repository, api_root);
    }
}

fn update_issue(client: &Client,
                auth_token: &str,
                issue_num: u64,
                config: &RepoData,
                repository: &str,
                api_root: &str) {
    let github = Github::host(api_root,
                              "steve",
                              client,
                              Credentials::Token(auth_token.to_owned()));
    let (owner, repo_name) = match repository.split("/").collect::<Vec<_>>() {
        strs => (strs[0], strs[1]),
    };
    let _ = github.repo(repo_name, owner)
        .issue(issue_num)
        .labels()
        .add(config.qa_flags.iter().map(String::as_str).collect());
    let mut headers = Headers::new();
    headers.set(Authorization(Bearer { token: auth_token.to_owned() }));
    headers.set(UserAgent("steve".to_owned()));
    let update_json = format!("{{\"assignee\":\"{}\"}}", config.qa_user);
    let url = Url::parse(&format!("{}/repos/{}/issues/{}", api_root, repository, issue_num))
        .expect("bad url");
    let res = client.patch(url)
        .body(&update_json)
        .headers(headers.to_owned())
        .send()
        .unwrap();
    assert_eq!(res.status, hyper::Ok);
}

fn get_issues(numbers: &mut HashSet<u64>, message: &str) {
    lazy_static! {
        static ref RE: Regex = Regex::new(r"(?i)needs\sqa:?\s#(\d+)").unwrap();
    }
    for cap in RE.captures_iter(message) {
        if let Some(num) = cap.at(1) {
            numbers.insert(num.parse::<u64>().unwrap());
        }
    }
}
<|endoftext|>


<commit_before>extern crate classreader;
extern crate zip;

use classreader::*;
use std::env;
use std::fs::File;

fn main() {
    env::args().nth(1).expect("usage: javamoose <class or jar file>...");
    let mut args = env::args();
    args.next(); // Skip exe name
    for f in args {
        if f.ends_with("class") {
            process_class_file(&f);
        } else if f.ends_with("jar") {
            process_jar_file(&f);
        } else {
            println!("Ignoring unknown file type {}", f);
        }

    }
    println!("Done!");
}

fn process_class_file(file_name: &String) {
    println!("Loading class file {}", file_name);
    println!("======================================================");
    let class = ClassReader::new_from_path(&file_name).unwrap();
    process_class(&class);
}

fn process_jar_file(file_name: &String) {
    let file = File::open(file_name).expect("couldn't find a file!");
    let mut zip = zip::ZipArchive::new(file).expect("could not read JAR");
    for i in 0..zip.len() {
        let mut class_file = zip.by_index(i).unwrap();
        if class_file.name().ends_with("class") {
            println!("Reading {}", class_file.name());
            let class = ClassReader::new_from_reader(&mut class_file).unwrap();
            process_class(&class);
        }
    }
}

fn process_class(class: &Class) {
    assert_eq!(0xCAFEBABE, class.magic);
    println!("class: {}", get_class_name(&class));
    println!("method count: {}", class.methods.len());
    println!("field count:  {}", class.fields.len());
    println!("code size:    {}", get_total_code_size(&class));
    for method in &class.methods {
        println!("\tmethod: {}{}", get_string(&class, method.name_index as usize), get_string(&class, method.descriptor_index as usize));
        println!("\t\tsize:       {}", get_method_size(&method));
        println!("\t\tlines:      {}", get_method_lines(&method));
        println!("\t\tcomplexity: {}", get_method_complexity(&method));
    }

    println!("======================================================");
}

/// Get constant from a pool, correcting for java's 1-based indexes.
fn get_const(class: &Class, i: usize) -> &ConstantPoolInfo {
    &class.constant_pool[i - 1]
}

/// Get string from constant pool
fn get_string(class: &Class, index: usize) -> String {
    match get_const(class, index) {
        &ConstantPoolInfo::Utf8(ref s) => s.clone(),
        _ => "?".to_string()
    }
}

fn get_class_name(class: &Class) -> String {
    match get_const(class, class.this_class as usize) {
        &ConstantPoolInfo::Class(index) => get_string(class, index as usize),
        _ => "?".to_string()
    }
}

fn get_total_code_size(class: &Class) -> usize {
    class.methods.iter().fold(0usize, |sum, method| sum + get_method_size(method))
}

/// Compute method size in byte code instructions
/// If multiple code segments are present in a method (not a javac output) sum it up
fn get_method_size(method: &Method) -> usize {
    method.attributes.iter().fold(0usize, |sum, a| sum + match a {
            &Attribute::Code{ref code, ..} => code.len(),
            _ => 0
        })
}

/// Compute method size in lines of code.
/// If multiple code segments or multiple line number attrbitues are present in a method (not a javac output) sum it up
fn get_method_lines(method: &Method) -> u16 {
    method.attributes.iter().fold(0, |sum, a| sum + match a {
            &Attribute::Code{ref attributes, ..} => attributes.iter().fold(0, |sum, a| sum + match a {
                &Attribute::LineNumberTable(ref line_table) => get_opt_line_number(line_table.last()) - get_opt_line_number(line_table.first()),
                _ => 0
            }),
            _ => 0
        })

}

fn get_opt_line_number(line: Option<&LineNumber>) -> u16 {
    line.map(|x| x.line_number).unwrap_or(0)
}

fn get_method_complexity(method: &Method) -> u16 {
    method.attributes.iter().fold(0, |sum, a| sum + match a {
            &Attribute::Code{ref code, ..} => get_code_complexity(code),
            _ => 0
        })
}

/// Using complexity definition from http://www.leepoint.net/principles_and_practices/complexity/complexity-java-method.html
/// Start with 1 for the method. Add one for each of the following flow-related elements that are found in the method.
/// Returns     Each return that isn't the last statement of a method.
/// Selection   if, else, case, default.
/// Loops   for, while, do-while, break, and continue.
/// Operators   &&, ||, ?, and :
/// Exceptions  catch, finally, throw, or throws clause.
/// Bytecode reference: http://www.angelibrary.com/computer/java_21days/ch32.htm
fn get_code_complexity(code: &Vec<(u32, Instruction)>) -> u16 {
    // println!("{:?}", code);
    // We start from zero to account for always-present final return - it will add the "starting" 1 for us.
    code.iter().fold(0, |sum, instruction| sum + match instruction.1 {
        Instruction::areturn => 1,
        Instruction::athrow => 1,
        Instruction::dreturn => 1,
        Instruction::freturn => 1,
        Instruction::if_acmpeq(..) => 1,
        Instruction::if_acmpne(..) => 1,
        Instruction::if_icmpeq(..) => 1,
        Instruction::if_icmpne(..) => 1,
        Instruction::if_icmplt(..) => 1,
        Instruction::if_icmpge(..) => 1,
        Instruction::if_icmpgt(..) => 1,
        Instruction::if_icmple(..) => 1,
        Instruction::ifeq(..) => 1,
        Instruction::ifne(..) => 1,
        Instruction::iflt(..) => 1,
        Instruction::ifge(..) => 1,
        Instruction::ifgt(..) => 1,
        Instruction::ifle(..) => 1,
        Instruction::ifnonnull(..) => 1,
        Instruction::ifnull(..) => 1,
        Instruction::ireturn => 1,
        Instruction::lookupswitch(_, ref jumps) => jumps.len() as u16, // tricky - lets use the size of the jump table
        Instruction::lreturn => 1,
        Instruction::return_ => 1,
        Instruction::tableswitch(_, _, ref jumps) => jumps.len() as u16, // tricky - lets use the size of the jump table
        // Subroutines in the method. Currently - do not count them towards the complexity. They are used to compile "finally", but I do not see them as branching really. Finally block is executed every time
        // Instruction::jsr(..) => 1,
        // Instruction::jsr_w(..) => 1,
        // Instruction::ret => 1,
        // Instruction::ret_w => 1,
        _ => 0
    })
}

<commit_msg>Temporary workaround for the line numbers problem<commit_after>extern crate classreader;
extern crate zip;

use classreader::*;
use std::env;
use std::fs::File;

fn main() {
    env::args().nth(1).expect("usage: javamoose <class or jar file>...");
    let mut args = env::args();
    args.next(); // Skip exe name
    for f in args {
        if f.ends_with("class") {
            process_class_file(&f);
        } else if f.ends_with("jar") {
            process_jar_file(&f);
        } else {
            println!("Ignoring unknown file type {}", f);
        }

    }
    println!("Done!");
}

fn process_class_file(file_name: &String) {
    println!("Loading class file {}", file_name);
    println!("======================================================");
    let class = ClassReader::new_from_path(&file_name).unwrap();
    process_class(&class);
}

fn process_jar_file(file_name: &String) {
    let file = File::open(file_name).expect("couldn't find a file!");
    let mut zip = zip::ZipArchive::new(file).expect("could not read JAR");
    for i in 0..zip.len() {
        let mut class_file = zip.by_index(i).unwrap();
        if class_file.name().ends_with("class") {
            println!("Reading {}", class_file.name());
            let class = ClassReader::new_from_reader(&mut class_file).unwrap();
            process_class(&class);
        }
    }
}

fn process_class(class: &Class) {
    assert_eq!(0xCAFEBABE, class.magic);
    println!("class: {}", get_class_name(&class));
    println!("method count: {}", class.methods.len());
    println!("field count:  {}", class.fields.len());
    println!("code size:    {}", get_total_code_size(&class));
    for method in &class.methods {
        println!("\tmethod: {}{}", get_string(&class, method.name_index as usize), get_string(&class, method.descriptor_index as usize));
        println!("\t\tsize:       {}", get_method_size(&method));
        println!("\t\tlines:      {}", get_method_lines(&method));
        println!("\t\tcomplexity: {}", get_method_complexity(&method));
    }

    println!("======================================================");
}

/// Get constant from a pool, correcting for java's 1-based indexes.
fn get_const(class: &Class, i: usize) -> &ConstantPoolInfo {
    &class.constant_pool[i - 1]
}

/// Get string from constant pool
fn get_string(class: &Class, index: usize) -> String {
    match get_const(class, index) {
        &ConstantPoolInfo::Utf8(ref s) => s.clone(),
        _ => "?".to_string()
    }
}

fn get_class_name(class: &Class) -> String {
    match get_const(class, class.this_class as usize) {
        &ConstantPoolInfo::Class(index) => get_string(class, index as usize),
        _ => "?".to_string()
    }
}

fn get_total_code_size(class: &Class) -> usize {
    class.methods.iter().fold(0usize, |sum, method| sum + get_method_size(method))
}

/// Compute method size in byte code instructions
/// If multiple code segments are present in a method (not a javac output) sum it up
fn get_method_size(method: &Method) -> usize {
    method.attributes.iter().fold(0usize, |sum, a| sum + match a {
            &Attribute::Code{ref code, ..} => code.len(),
            _ => 0
        })
}

/// Compute method size in lines of code.
/// If multiple code segments or multiple line number attrbitues are present in a method (not a javac output) sum it up
fn get_method_lines(method: &Method) -> u16 {
    method.attributes.iter().fold(0, |sum, a| sum + match a {
            &Attribute::Code{ref attributes, ..} => attributes.iter().fold(0, |sum, a| sum + match a {
                &Attribute::LineNumberTable(ref line_table) => {
                    let end = get_opt_line_number(line_table.last());
                    let start = get_opt_line_number(line_table.first());
                    if start > end { // TODO: Investigate when it happens
                        0
                    } else {
                        end - start
                    }
                },
                _ => 0
            }),
            _ => 0
        })

}

fn get_opt_line_number(line: Option<&LineNumber>) -> u16 {
    line.map(|x| x.line_number).unwrap_or(0)
}

fn get_method_complexity(method: &Method) -> u16 {
    method.attributes.iter().fold(0, |sum, a| sum + match a {
            &Attribute::Code{ref code, ..} => get_code_complexity(code),
            _ => 0
        })
}

/// Using complexity definition from http://www.leepoint.net/principles_and_practices/complexity/complexity-java-method.html
/// Start with 1 for the method. Add one for each of the following flow-related elements that are found in the method.
/// Returns     Each return that isn't the last statement of a method.
/// Selection   if, else, case, default.
/// Loops   for, while, do-while, break, and continue.
/// Operators   &&, ||, ?, and :
/// Exceptions  catch, finally, throw, or throws clause.
/// Bytecode reference: http://www.angelibrary.com/computer/java_21days/ch32.htm
fn get_code_complexity(code: &Vec<(u32, Instruction)>) -> u16 {
    // println!("{:?}", code);
    // We start from zero to account for always-present final return - it will add the "starting" 1 for us.
    code.iter().fold(0, |sum, instruction| sum + match instruction.1 {
        Instruction::areturn => 1,
        Instruction::athrow => 1,
        Instruction::dreturn => 1,
        Instruction::freturn => 1,
        Instruction::if_acmpeq(..) => 1,
        Instruction::if_acmpne(..) => 1,
        Instruction::if_icmpeq(..) => 1,
        Instruction::if_icmpne(..) => 1,
        Instruction::if_icmplt(..) => 1,
        Instruction::if_icmpge(..) => 1,
        Instruction::if_icmpgt(..) => 1,
        Instruction::if_icmple(..) => 1,
        Instruction::ifeq(..) => 1,
        Instruction::ifne(..) => 1,
        Instruction::iflt(..) => 1,
        Instruction::ifge(..) => 1,
        Instruction::ifgt(..) => 1,
        Instruction::ifle(..) => 1,
        Instruction::ifnonnull(..) => 1,
        Instruction::ifnull(..) => 1,
        Instruction::ireturn => 1,
        Instruction::lookupswitch(_, ref jumps) => jumps.len() as u16, // tricky - lets use the size of the jump table
        Instruction::lreturn => 1,
        Instruction::return_ => 1,
        Instruction::tableswitch(_, _, ref jumps) => jumps.len() as u16, // tricky - lets use the size of the jump table
        // Subroutines in the method. Currently - do not count them towards the complexity. They are used to compile "finally", but I do not see them as branching really. Finally block is executed every time
        // Instruction::jsr(..) => 1,
        // Instruction::jsr_w(..) => 1,
        // Instruction::ret => 1,
        // Instruction::ret_w => 1,
        _ => 0
    })
}

<|endoftext|>


<commit_before>use std::io;
use std::io::prelude::*;
use std::net::TcpStream;
use std::fs::File;

extern crate chrono;
use chrono::*;

extern crate stats;

extern crate clap;
use clap::{Arg, App, ArgMatches};

mod http_status;
mod log_parser;
use log_parser::log_events::*;
mod request_response_matcher;
use request_response_matcher::RequestResponsePair;
mod filter;
mod analyzer;
mod render;

// http://stackoverflow.com/a/27590832/376138
macro_rules! println_stderr(
    ($($arg:tt)*) => { {
        let r = writeln!(&mut ::std::io::stderr(), $($arg)*);
        r.expect("failed printing to stderr");
    } }
);

fn parse_args<'a>() -> ArgMatches<'a> {
    App::new("Request.log Analyzer")
        .arg(Arg::with_name("filename")
            .index(1)
            .value_name("FILE")
            .required(false)
            .help("Log file to analyze, defaults to stdin")
            .takes_value(true))
        .arg(Arg::with_name("time_filter_minutes")
            .value_name("MINUTES")
            .short("t")
            .help("Limit to the last n minutes")
            .takes_value(true))
        .arg(Arg::with_name("include_term")
            .value_name("TERM")
            .long("include")
            .help("Only includes lines that contain this term")
            .takes_value(true))
        .arg(Arg::with_name("exclude_term")
            .value_name("TERM")
            .long("exclude")
            .help("Excludes lines that contain this term")
            .takes_value(true))
        .arg(Arg::with_name("graphite-server")
            .value_name("GRAPHITE_SERVER")
            .long("graphite-server")
            .help("Send values to this Graphite server instead of stdout")
            .takes_value(true))
        .arg(Arg::with_name("graphite-port")
            .value_name("GRAPHITE_PORT")
            .long("graphite-port")
            .takes_value(true)
            .default_value("2003"))
        .arg(Arg::with_name("graphite-prefix")
            .value_name("GRAPHITE_PREFIX")
            .long("graphite-prefix")
            .help("Prefix for Graphite key, e.g. 'servers.prod.publisher1'")
            .takes_value(true))
        .get_matches()
}

fn main() {
    let args = parse_args();

    let filename = args.value_of("filename").unwrap_or("-");

    let time_filter = match args.value_of("time_filter_minutes") {
        Some(minutes) => Some(Duration::minutes(minutes.parse().unwrap())),
        None => None
    };

    let mut input: Box<io::Read> = match filename {
        "-" => Box::new(io::stdin()),
        _ => Box::new(File::open(filename).unwrap())
    };

    let reader = io::BufReader::new(input);

    let lines = reader.lines();

    let mut requests: Vec<Request> = Vec::new();
    let mut responses: Vec<Response> = Vec::new();
    let mut pairs: Vec<RequestResponsePair> = Vec::new();

    for line in lines {
        let line_value = &line.unwrap();

        if line_value.contains("->") {
            match Request::new_from_log_line(&line_value) {
                Ok(r) => {
                    // if time_filter.is_none() ||
                    //   (time_filter.is_some() && r.is_between_times(UTC::now().with_timezone(&r.time.timezone()) - time_filter.unwrap(), UTC::now().with_timezone(&r.time.timezone()))) {
                        requests.push(r);
                    // }
                },
                Err(err) => println_stderr!("Skipped a line: {}", err)
            }
        }

        if line_value.contains("<-") {
            match Response::new_from_log_line(&line_value) {
                Ok(r) => responses.push(r),
                Err(err) => println_stderr!("Skipped a line: {}", err)
            }
        }

        // for ref request in &requests {
        if requests.len() == 0 {
            continue;
        }
        for request_index in 0..(requests.len() - 1) {
            let matching_response_index: Option<usize> = responses.iter().position(|response| requests[request_index].id == response.id );

            if matching_response_index.is_some() {
                let request = requests.remove(request_index);
                let response = responses.remove(matching_response_index.unwrap());

                let pair = RequestResponsePair {
                    request: request,
                    response: response
                };
                // println!("request {:?} has response {:?}", request, response);
                // println!("Requests len {:?}", requests.len());
                // println!("========================================", );
                println!("{:?}", pair);
            }
        }
    }

    // let (requests, responses) = log_parser::parse(&mut input);
    //
    // let pairs = request_response_matcher::pair_requests_responses(requests, responses);
    //
    // let conditions = filter::FilterConditions {
    //     include_terms: match args.value_of("include_term") {
    //         Some(value) => Some(vec![value.to_string()]),
    //         None => None
    //     },
    //     exclude_terms: match args.value_of("exclude_term") {
    //         Some(value) => Some(vec![value.to_string()]),
    //         None => None
    //     },
    //     latest_time: time_filter,
    // };
    //
    // let filtered_pairs = filter::filter(pairs, conditions);
    //
    // let result = analyzer::analyze(&filtered_pairs);
    //
    // match result {
    //     Some(result) => {
    //         if args.is_present("graphite-server") {
    //             let stream = TcpStream::connect(
    //                 (
    //                     args.value_of("graphite-server").unwrap(),
    //                     args.value_of("graphite-port").unwrap().parse().unwrap()
    //                 )
    //             ).expect("Could not connect to the Graphite server");
    //
    //             let timezone = filtered_pairs[0].request.time.timezone();
    //
    //             render::render_graphite(result, UTC::now().with_timezone(&timezone), args.value_of("graphite-prefix"), stream);
    //         } else {
    //             render::render_terminal(result);
    //         }
    //     },
    //     None => println_stderr!("No matching log lines in file.")
    // }
}
<commit_msg>Wire up looped version to input and analysis<commit_after>use std::io;
use std::io::prelude::*;
use std::net::TcpStream;
use std::fs::File;

extern crate chrono;
use chrono::*;

extern crate stats;

extern crate clap;
use clap::{Arg, App, ArgMatches};

mod http_status;
mod log_parser;
use log_parser::log_events::*;
mod request_response_matcher;
use request_response_matcher::RequestResponsePair;
mod filter;
mod analyzer;
mod render;

// http://stackoverflow.com/a/27590832/376138
macro_rules! println_stderr(
    ($($arg:tt)*) => { {
        let r = writeln!(&mut ::std::io::stderr(), $($arg)*);
        r.expect("failed printing to stderr");
    } }
);

fn parse_args<'a>() -> ArgMatches<'a> {
    App::new("Request.log Analyzer")
        .arg(Arg::with_name("filename")
            .index(1)
            .value_name("FILE")
            .required(false)
            .help("Log file to analyze, defaults to stdin")
            .takes_value(true))
        .arg(Arg::with_name("time_filter_minutes")
            .value_name("MINUTES")
            .short("t")
            .help("Limit to the last n minutes")
            .takes_value(true))
        .arg(Arg::with_name("include_term")
            .value_name("TERM")
            .long("include")
            .help("Only includes lines that contain this term")
            .takes_value(true))
        .arg(Arg::with_name("exclude_term")
            .value_name("TERM")
            .long("exclude")
            .help("Excludes lines that contain this term")
            .takes_value(true))
        .arg(Arg::with_name("graphite-server")
            .value_name("GRAPHITE_SERVER")
            .long("graphite-server")
            .help("Send values to this Graphite server instead of stdout")
            .takes_value(true))
        .arg(Arg::with_name("graphite-port")
            .value_name("GRAPHITE_PORT")
            .long("graphite-port")
            .takes_value(true)
            .default_value("2003"))
        .arg(Arg::with_name("graphite-prefix")
            .value_name("GRAPHITE_PREFIX")
            .long("graphite-prefix")
            .help("Prefix for Graphite key, e.g. 'servers.prod.publisher1'")
            .takes_value(true))
        .get_matches()
}

fn main() {
    let args = parse_args();

    let filename = args.value_of("filename").unwrap_or("-");

    let time_filter = match args.value_of("time_filter_minutes") {
        Some(minutes) => Some(Duration::minutes(minutes.parse().unwrap())),
        None => None
    };

    let mut input: Box<io::Read> = match filename {
        "-" => Box::new(io::stdin()),
        _ => Box::new(File::open(filename).unwrap())
    };

    let reader = io::BufReader::new(input);

    let lines = reader.lines();

    let mut requests: Vec<Request> = Vec::new();
    let mut responses: Vec<Response> = Vec::new();
    let mut times: Vec<i64> = Vec::new();

    // We need to store 1 Request in order to determine the timezone later
    let mut first_request: Option<Request> = None;

    let conditions = filter::FilterConditions {
        include_terms: match args.value_of("include_term") {
            Some(value) => Some(vec![value.to_string()]),
            None => None
        },
        exclude_terms: match args.value_of("exclude_term") {
            Some(value) => Some(vec![value.to_string()]),
            None => None
        },
        latest_time: time_filter,
    };

    for line in lines {
        let line_value = &line.unwrap();

        if line_value.contains("->") {
            match Request::new_from_log_line(&line_value) {
                Ok(r) => {
                    if first_request.is_none() {
                        first_request = Some(r.clone());
                    }
                    requests.push(r);
                },
                Err(err) => println_stderr!("Skipped a line: {}", err)
            }
        }

        if line_value.contains("<-") {
            match Response::new_from_log_line(&line_value) {
                Ok(r) => responses.push(r),
                Err(err) => println_stderr!("Skipped a line: {}", err)
            }
        }

        if requests.len() == 0 {
            continue;
        }
        for request_index in 0..(requests.len() - 1) {
            let matching_response_index: Option<usize> = responses.iter().position(|response| requests[request_index].id == response.id );

            if matching_response_index.is_some() {
                let request = requests.remove(request_index);
                let response = responses.remove(matching_response_index.unwrap());

                let pair = RequestResponsePair {
                    request: request,
                    response: response
                };

                if filter::matches_filter(&pair, &conditions) {
                    times.push(pair.response.response_time.num_milliseconds());
                }
            }
        }
    }

    let result = analyzer::analyze(&times);

    match result {
        Some(result) => {
            if args.is_present("graphite-server") {
                let stream = TcpStream::connect(
                    (
                        args.value_of("graphite-server").unwrap(),
                        args.value_of("graphite-port").unwrap().parse().unwrap()
                    )
                ).expect("Could not connect to the Graphite server");

                let timezone = first_request.unwrap().time.timezone();

                render::render_graphite(result, UTC::now().with_timezone(&timezone), args.value_of("graphite-prefix"), stream);
            } else {
                render::render_terminal(result);
            }
        },
        None => println_stderr!("No matching log lines in file.")
    }
}
<|endoftext|>


<commit_before>#![feature(custom_attribute, custom_derive, plugin)]
#![plugin(serde_macros)]

extern crate hyper;
extern crate serde;

mod entity;
mod error;

use entity::ZipResult;
use error::ZipError;
use hyper::Client;
use serde::json;
use std::io::Read;

fn main() {
    let mut client = Client::new();
    let result = std::env::args().nth(1).ok_or(ZipError::Input)
        .and_then(|candidate| query(&mut client, candidate))
        .and_then(|(candidate, response)| parse_result(candidate, response));

    match result {
        Ok(zip_result) => println!("{}", zip_result),
        Err(e) => println!("{}", e),
    }
}

fn query(client: &mut Client, candidate: String) -> Result<(String, String), ZipError> {
    match client.get(&format!("http://api.zippopotam.us/us/{}", candidate)).send() {
        Ok(mut res) => Ok((candidate, read_response(&mut res))),
        _ => Err(ZipError::API),
    }
}

#[allow(unused)]
fn read_response(response: &mut hyper::client::response::Response) -> String {
    let mut buf = String::new();
    response.read_to_string(&mut buf);
    buf
}

fn parse_result(candidate: String, result: String) -> Result<ZipResult, ZipError> {
    match json::from_str(&result) {
        Ok(result) => Ok(result),
        Err(_) => Err(ZipError::Invalid(candidate)),
    }
}
<commit_msg>This also works for some odd reason. >.><commit_after>#![feature(custom_attribute, custom_derive, plugin)]
#![plugin(serde_macros)]

extern crate hyper;
extern crate serde;

mod entity;
mod error;

use entity::ZipResult;
use error::ZipError;
use hyper::Client;
use serde::json;
use std::io::Read;

fn main() {
    let result = std::env::args().nth(1).ok_or(ZipError::Input)
        .and_then(|candidate| query(&mut Client::new(), candidate))
        .and_then(|(candidate, response)| parse_result(candidate, response));

    match result {
        Ok(zip_result) => println!("{}", zip_result),
        Err(e) => println!("{}", e),
    }
}

fn query(client: &mut Client, candidate: String) -> Result<(String, String), ZipError> {
    match client.get(&format!("http://api.zippopotam.us/us/{}", candidate)).send() {
        Ok(mut res) => Ok((candidate, read_response(&mut res))),
        _ => Err(ZipError::API),
    }
}

#[allow(unused)]
fn read_response(response: &mut hyper::client::response::Response) -> String {
    let mut buf = String::new();
    response.read_to_string(&mut buf);
    buf
}

fn parse_result(candidate: String, result: String) -> Result<ZipResult, ZipError> {
    match json::from_str(&result) {
        Ok(result) => Ok(result),
        Err(_) => Err(ZipError::Invalid(candidate)),
    }
}
<|endoftext|>


<commit_before>extern crate discord;
extern crate serde_json;

use std::fs::File;
use std::io::Read;
use discord::{Discord, State};
use discord::model::{Event, ChannelId, UserId};

fn main() {
    // Read and set config vars
    let mut file = File::open("config.json").unwrap();
    let mut config = String::new();
    file.read_to_string(&mut config).unwrap();

    let json: serde_json::Value = serde_json::from_str(&config).unwrap();
    let bot_tokens = json.find_path(&["bot-token"]).unwrap();
    let welcome_message = json.find_path(&["welcome-message"]).unwrap();

    info(&format!("[bot-token has been set to {} from config", bot_tokens));
    info(&format!("welcome-message has been set to {} from the config",
                  welcome_message));

    // Login to the API
    let discord = Discord::from_bot_token(bot_tokens.as_string().unwrap()).expect("Login Fail");

    // establish websocket and voice connection
    let (mut connection, ready) = discord.connect().expect("connect failed");
    println!("[Ready] {} is serving {} servers",
             ready.user.username,
             ready.servers.len());
    let mut state = State::new(ready);

    // receive events forever
    loop {
        let event = match connection.recv_event() {
            Ok(event) => event,
            Err(err) => {
                warning(&format!("Receive error: {:?}", err));
                if let discord::Error::WebSocket(..) = err {
                    // Handle the websocket connection being dropped
                    let (new_connection, ready) = discord.connect().expect("connect failed");
                    connection = new_connection;
                    state = State::new(ready);
                    println!("[Ready] Reconnected successfully.");
                }
                if let discord::Error::Closed(..) = err {
                    break;
                }
                continue;
            }
        };
        state.update(&event);

        match event {
            Event::MessageCreate(message) => {
                use std::ascii::AsciiExt;
                // safeguard: stop if the message is from us
                if message.author.id == state.user().id {
                    continue;
                }

                // reply to a command if there was one
                let mut split = message.content.split(" ");
                let first_word = split.next().unwrap_or("");
                let argument = split.next().unwrap_or("");

                if first_word.eq_ignore_ascii_case("!help") {
                    try_twice(&discord,
                              &message.channel_id,
                              &format!("Here's the help that @{} wanted:\n\n'!dj' Allows you to \
                                        play YT videos in whatever voice channel you are \
                                        in.\nHas two sub commands 'stop' Stops the current \
                                        song, & 'quit' causes the bot to exit to VC\n\n'!help' \
                                        Shows this output.",
                                       message.author.name));

                    break;
                } else if first_word.eq_ignore_ascii_case("!dj") {
                    let vchan = state.find_voice_user(message.author.id);
                    if argument.eq_ignore_ascii_case("stop") {
                        vchan.map(|(sid, _)| connection.voice(sid).stop());
                    } else if argument.eq_ignore_ascii_case("quit") {
                        vchan.map(|(sid, _)| connection.drop_voice(sid));
                    } else {
                        let output = if let Some((server_id, channel_id)) = vchan {
                            match discord::voice::open_ytdl_stream(&argument) {
                                Ok(stream) => {
                                    let voice = connection.voice(server_id);
                                    voice.set_deaf(true);
                                    voice.connect(channel_id);
                                    voice.play(stream);
                                    String::new()
                                }
                                Err(error) => format!("Error: {}", error),
                            }
                        } else {
                            "You must be in a voice channel to DJ".to_owned()
                        };
                        if output.len() > 0 {
                            warn(discord.send_message(&message.channel_id, &output, "", false));
                        }
                    }
                } else if first_word.eq_ignore_ascii_case("!quit") {
                    if message.author.id == UserId(5417) {
                        std::process::exit(1);
                    } else {
                        try_twice(&discord,
                                  &message.channel_id,
                                  "Your not authorized to do that");
                        warning("Somebody not authorized tried to kill me.");
                    }
                }
            }
            Event::VoiceStateUpdate(server_id, _) => {
                // If someone moves/hangs up, and we are in a voice channel,
                if let Some(cur_channel) = connection.voice(server_id).current_channel() {
                    // and our current voice channel is empty, disconnect from voice
                    if let Some(srv) = state.servers().iter().find(|srv| srv.id == server_id) {
                        if srv.voice_states
                            .iter()
                            .filter(|vs| vs.channel_id == Some(cur_channel))
                            .count() <= 1 {
                            connection.voice(server_id).disconnect();
                        }
                    }
                }
            }
            Event::ServerMemberAdd(server_joined_id, member) => {
                let channel_id = ChannelId(server_joined_id.0);

                for server in state.servers() {
                    if server.id == server_joined_id {
                        try_twice(&discord,
                                  &channel_id,
                                  &format!("Welcome {} to {}! {}",
                                           member.user.name,
                                           server.name,
                                           welcome_message));
                        break;
                    }
                }
            }
            _ => {} // discard other events
        }
    }
}

fn warn<T, E: ::std::fmt::Debug>(result: Result<T, E>) {
    match result {
        Ok(_) => {}
        Err(err) => println!("[Warning] {:?}", err),
    }
}

fn try_twice(discord: &Discord, channel: &ChannelId, message: &str) {
    let result = discord.send_message(channel, message, "", false);
    match result {
        Ok(_) => {} // nothing to do, it was sent - the `Ok()` contains a `Message` if you want it
        Err(discord::Error::RateLimited(milliseconds)) => {
            let sleep_duration = std::time::Duration::from_millis(milliseconds);

            warning(&format!("We were rate limited for {:?} milliseconds.",
                             sleep_duration));
            std::thread::sleep(sleep_duration);
            try_twice(discord, channel, message);
        }
        _ => {} // discard all other events
    }
}

fn warning(output: &str) {
    println!("[Warning] {}", output);
}
fn info(output: &str) {
    println!("[Info] {}", output);
}
<commit_msg>Improve help command<commit_after>extern crate discord;
extern crate serde_json;

use std::fs::File;
use std::io::Read;
use discord::{Discord, State};
use discord::model::{Event, ChannelId, UserId};

fn main() {
    // Read and set config vars
    let mut file = File::open("config.json").unwrap();
    let mut config = String::new();
    file.read_to_string(&mut config).unwrap();

    let json: serde_json::Value = serde_json::from_str(&config).unwrap();
    let bot_tokens = json.find_path(&["bot-token"]).unwrap();
    let welcome_message = json.find_path(&["welcome-message"]).unwrap();

    info(&format!("[bot-token has been set to {} from config", bot_tokens));
    info(&format!("welcome-message has been set to {} from the config",
                  welcome_message));

    // Login to the API
    let discord = Discord::from_bot_token(bot_tokens.as_string().unwrap()).expect("Login Fail");

    // establish websocket and voice connection
    let (mut connection, ready) = discord.connect().expect("connect failed");
    println!("[Ready] {} is serving {} servers",
             ready.user.username,
             ready.servers.len());
    let mut state = State::new(ready);

    // receive events forever
    loop {
        let event = match connection.recv_event() {
            Ok(event) => event,
            Err(err) => {
                warning(&format!("Receive error: {:?}", err));
                if let discord::Error::WebSocket(..) = err {
                    // Handle the websocket connection being dropped
                    let (new_connection, ready) = discord.connect().expect("connect failed");
                    connection = new_connection;
                    state = State::new(ready);
                    println!("[Ready] Reconnected successfully.");
                }
                if let discord::Error::Closed(..) = err {
                    break;
                }
                continue;
            }
        };
        state.update(&event);

        match event {
            Event::MessageCreate(message) => {
                use std::ascii::AsciiExt;
                // safeguard: stop if the message is from us
                if message.author.id == state.user().id {
                    continue;
                }

                // reply to a command if there was one
                let mut split = message.content.split(" ");
                let first_word = split.next().unwrap_or("");
                let argument = split.next().unwrap_or("");

                if first_word.eq_ignore_ascii_case("!help") {
                    if argument.eq_ignore_ascii_case("dj") {
                        try_twice(&discord,
                                  &message.channel_id,
                                  &format!("``!dj`` Plays YouTube videos in Voice Chat\n``!dj \
                                            stop`` Stops the current playing song.\n``!dj \
                                            quit`` Stops the current playing song, and exits \
                                            the Voice Chat."));
                    } else {
                        try_twice(&discord,
                                  &message.channel_id,
                                  &format!("Here's the help that @{} wanted:\n\n``!dj`` Plays \
                                            YouTube videos in Voice Chat. See ``!help dj`` for \
                                            more info\n\n``!help`` Shows this output.",
                                           message.author.name));
                    }
                } else if first_word.eq_ignore_ascii_case("!dj") {
                    let vchan = state.find_voice_user(message.author.id);
                    if argument.eq_ignore_ascii_case("stop") {
                        vchan.map(|(sid, _)| connection.voice(sid).stop());
                    } else if argument.eq_ignore_ascii_case("quit") {
                        vchan.map(|(sid, _)| connection.drop_voice(sid));
                    } else {
                        let output = if let Some((server_id, channel_id)) = vchan {
                            match discord::voice::open_ytdl_stream(&argument) {
                                Ok(stream) => {
                                    let voice = connection.voice(server_id);
                                    voice.set_deaf(true);
                                    voice.connect(channel_id);
                                    voice.play(stream);
                                    String::new()
                                }
                                Err(error) => format!("Error: {}", error),
                            }
                        } else {
                            "You must be in a voice channel to DJ".to_owned()
                        };
                        if output.len() > 0 {
                            warn(discord.send_message(&message.channel_id, &output, "", false));
                        }
                    }
                } else if first_word.eq_ignore_ascii_case("!quit") {
                    if message.author.id == UserId(5417) {
                        std::process::exit(1);
                    } else {
                        try_twice(&discord,
                                  &message.channel_id,
                                  "Your not authorized to do that");
                        warning("Somebody not authorized tried to kill me.");
                    }
                }
            }
            Event::VoiceStateUpdate(server_id, _) => {
                // If someone moves/hangs up, and we are in a voice channel,
                if let Some(cur_channel) = connection.voice(server_id).current_channel() {
                    // and our current voice channel is empty, disconnect from voice
                    if let Some(srv) = state.servers().iter().find(|srv| srv.id == server_id) {
                        if srv.voice_states
                            .iter()
                            .filter(|vs| vs.channel_id == Some(cur_channel))
                            .count() <= 1 {
                            connection.voice(server_id).disconnect();
                        }
                    }
                }
            }
            Event::ServerMemberAdd(server_joined_id, member) => {
                let channel_id = ChannelId(server_joined_id.0);

                for server in state.servers() {
                    if server.id == server_joined_id {
                        try_twice(&discord,
                                  &channel_id,
                                  &format!("Welcome {} to {}! {}",
                                           member.user.name,
                                           server.name,
                                           welcome_message));
                    }
                }
            }
            _ => {} // discard other events
        }
    }
}

fn warn<T, E: ::std::fmt::Debug>(result: Result<T, E>) {
    match result {
        Ok(_) => {}
        Err(err) => println!("[Warning] {:?}", err),
    }
}

fn try_twice(discord: &Discord, channel: &ChannelId, message: &str) {
    let result = discord.send_message(channel, message, "", false);
    match result {
        Ok(_) => {} // nothing to do, it was sent - the `Ok()` contains a `Message` if you want it
        Err(discord::Error::RateLimited(milliseconds)) => {
            let sleep_duration = std::time::Duration::from_millis(milliseconds);

            warning(&format!("We were rate limited for {:?} milliseconds.",
                             sleep_duration));
            std::thread::sleep(sleep_duration);
            try_twice(discord, channel, message);
        }
        _ => {} // discard all other events
    }
}

fn warning(output: &str) {
    println!("[Warning] {}", output);
}
fn info(output: &str) {
    println!("[Info] {}", output);
}
<|endoftext|>


<commit_before>//#![cfg_attr(feature = "serde_derive", feature(proc_macro))]

//#[cfg(feature = "serde_derive")]
//#[macro_use]
//extern crate serde_derive;

extern crate env_logger;
extern crate futures;
extern crate tokio_core;
//extern crate serde;
//extern crate serde_json;

// TCP or UDP?
// Can we get non-persistent TCP connections which don't have buffer stalls?
// Or should we work on UDP to make sure we have control over whether or not
// messages are sent / received?

// Note on the copy(reader, writer) function, it's very simple.
// The function takes data given to reader and copies it over to writer, writing back
// whatever was written. The 'copy' might as well be called 'echo'.

// Also, in this case, when an incoming socket happens, we cannot handle the split
// without a future, since the connection will persist far longer than the 'for_each'
// iteration will, therefore it must be run asynchronously, to avoid only being able
// to handle one client at a time.

use std::net::SocketAddr;
use std::io::{Write, Read};
use std::str::{from_utf8};

use futures::Future;
use futures::stream::Stream;

use tokio_core::io::{copy, Io};
use tokio_core::net::{TcpListener, TcpStream};
use tokio_core::reactor::Core;

//use serde_json::*;

//mod server;
//mod client;
mod data;

use data::Data;

struct Writer;

impl Write for Writer {
    fn write(&mut self, buf : &[u8]) -> Result<usize, std::io::Error> {
        //println!("Recieved : {}", from_utf8(buf).unwrap().to_string());
        //let msg = from_utf8(buf).unwrap().to_string();
        //let val : Data = serde_json::from_slice(&buf);
        Ok(buf.len())
    }
    fn flush(&mut self) -> Result<(), std::io::Error> {
        Ok(())
    }
}

// We're gonna read some JSON
fn main() {
    let addr = "127.0.0.1:8080".to_string().parse::<SocketAddr>().unwrap();
    let mut core = Core::new().unwrap();
    let handle = core.handle();
    let socket = TcpListener::bind(&addr, &handle).unwrap();

    let core_loop = socket.incoming().for_each(|(stream, addr)| {
        let message =
            futures::lazy(move || {
                Ok(stream.split())
            }).and_then(|(r, _w)| {
                copy(r, Writer)
            }).map(move |amt| {
                println!("Said hello to client {} at {}!", amt, addr);
            }).map_err(|e| {
                panic!("Error: {}", e);
            });

        handle.spawn(message);

        Ok(())
    });

    core.run(core_loop).unwrap();
}

<commit_msg>Next: Implement a SOCK handshake, or some other service layer protocol<commit_after>//#![cfg_attr(feature = "serde_derive", feature(proc_macro))]

//#[cfg(feature = "serde_derive")]
//#[macro_use]
//extern crate serde_derive;

extern crate env_logger;
extern crate futures;
extern crate tokio_core;
//extern crate serde;
//extern crate serde_json;

// TCP or UDP?
// Can we get non-persistent TCP connections which don't have buffer stalls?
// Or should we work on UDP to make sure we have control over whether or not
// messages are sent / received?

// Note on the copy(reader, writer) function, it's very simple.
// The function takes data given to reader and copies it over to writer, writing back
// whatever was written. The 'copy' might as well be called 'echo'.

// Also, in this case, when an incoming socket happens, we cannot handle the split
// without a future, since the connection will persist far longer than the 'for_each'
// iteration will, therefore it must be run asynchronously, to avoid only being able
// to handle one client at a time.

use std::net::SocketAddr;
use std::io::{Write, Read};
use std::str::{from_utf8};

use futures::Future;
use futures::stream::Stream;

use tokio_core::io::{copy, Io};
use tokio_core::net::{TcpListener, TcpStream};
use tokio_core::reactor::Core;

//use serde_json::*;

//mod server;
//mod client;
mod data;

use data::Data;

struct Writer;

impl Write for Writer {
    fn write(&mut self, buf : &[u8]) -> Result<usize, std::io::Error> {
        print!("Recieved : {}", from_utf8(buf).unwrap().to_string());
        //let msg = from_utf8(buf).unwrap().to_string();
        //let val : Data = serde_json::from_slice(&buf);
        Ok(buf.len())
    }
    fn flush(&mut self) -> Result<(), std::io::Error> {
        Ok(())
    }
}

// We're gonna read some JSON
fn main() {
    let addr = "127.0.0.1:8080".to_string().parse::<SocketAddr>().unwrap();
    let mut core = Core::new().unwrap();
    let handle = core.handle();
    let socket = TcpListener::bind(&addr, &handle).unwrap();

    let core_loop = socket.incoming().for_each(|(stream, addr)| {
        let message =
            futures::lazy(move || {
                Ok(stream.split())
            }).and_then(|(r, _w)| {
                copy(r, Writer)
            }).map(move |amt| {
                println!("Said hello to client {} at {}!", amt, addr);
            }).map_err(|e| {
                panic!("Error: {}", e);
            });

        handle.spawn(message);

        Ok(())
    });

    core.run(core_loop).unwrap();
}

<|endoftext|>