<commit_before>extern crate rand;
extern crate termion;

use std::collections::VecDeque;
use std::io::{self, BufRead, BufReader, Cursor, Read, Write};
use std::thread::sleep;
use std::time::{Duration, Instant};

use rand::Rng;
use termion::raw::IntoRawMode;
use termion::{async_stdin, clear, color, cursor, style};

mod object {
    use super::color;

    pub type Object = u8;
    pub const SPACE: Object = 0;
    pub const BARRIER: Object = 1;
    pub const SNAKE_HEAD: Object = 2;
    pub const SNAKE_BODY: Object = 3;
    pub const FOOD: Object = 4;

    pub const COLORS: [color::Bg<color::Rgb>; 5] = [
        color::Bg(color::Rgb(224, 224, 224)),
        color::Bg(color::Rgb(0, 0, 0)),
        color::Bg(color::Rgb(153, 0, 0)),
        color::Bg(color::Rgb(255, 0, 0)),
        color::Bg(color::Rgb(0, 153, 0)),
    ];
}

// TODO(damnever): make speed and game map configurable.
const SPACE_MARK: char = '.';
const BARRIER_MARK: char = '*';
const MAX_FOOD: usize = 10;
const SPEEDS: [Duration; 10] = [
    Duration::from_millis(600),
    Duration::from_millis(500),
    Duration::from_millis(400),
    Duration::from_millis(300),
    Duration::from_millis(200),
    Duration::from_millis(100),
    Duration::from_millis(80),
    Duration::from_millis(50),
    Duration::from_millis(30),
    Duration::from_millis(11),
];
const DEFAULT_MAP: &'static str = "**...............................................**
*.................................................*
...................................................
...................................................
...................................................
...................................................
...................................................
...................................................
*..................................................
*.................................................*
*.................................................*
*.................................................*
*.................................................*
*.................................................*
..................................................*
...................................................
...................................................
...................................................
...................................................
...................................................
...................................................
*.................................................*
**...............................................**
";

#[derive(Debug, Clone, Copy)]
enum Key {
    Up,
    Down,
    Left,
    Right,
    Restart,
    Quit,
}

impl Key {
    fn from(key_byte: u8, default: Key) -> Key {
        let key = key_byte as char;
        match key {
            'w' | 'k' => Key::Up,
            'd' | 'l' => Key::Right,
            's' | 'j' => Key::Down,
            'a' | 'h' => Key::Left,
            'r' => Key::Restart,
            'q' => Key::Quit,
            _ => default,
        }
    }

    fn rand_direction(rng: &mut rand::ThreadRng) -> Key {
        let directions = [Key::Up, Key::Down, Key::Left, Key::Right];
        directions[rng.gen_range(0, 4)]
    }
}

struct Game<R, W: Write> {
    init_pos: usize,
    rows: usize,
    cols: usize,
    score_to_speed: usize,
    score: usize,
    food: usize,
    spaces: usize,
    snake: VecDeque<usize>,
    map: Vec<object::Object>,
    stdin: R,
    stdout: W,
    rng: rand::ThreadRng,
}

impl<R: Read, W: Write> Game<R, W> {
    fn new(stdin: R, stdout: W, map: &mut Vec<u8>, cols: usize) -> Self {
        let init_pos = map.len() / 2;
        map[init_pos] = object::SNAKE_HEAD;
        let mut game = Game {
            init_pos: init_pos,
            rows: map.len() / cols,
            cols: cols,
            score_to_speed: map.len() / 3 / SPEEDS.len(),
            score: 0,
            food: 0,
            spaces: 0,
            snake: VecDeque::new(),
            map: map.to_owned(),
            stdin: stdin,
            stdout: stdout,
            rng: rand::thread_rng(),
        };
        game.reset();

        game
    }

    fn start(&mut self) {
        self.feed();
        self.draw();
        let mut speed = self.try_speed_up();
        let mut keys_buf = [0u8; 23]; // Whatever
        let mut prev_direction = Key::rand_direction(&mut self.rng);
        let mut start = Instant::now();

        loop {
            let keys_num = self.stdin.read(&mut keys_buf).unwrap();
            let mut key = prev_direction;
            if keys_num > 0 {
                key = Key::from(keys_buf[keys_num - 1], prev_direction);
            }

            let game_over = match key {
                Key::Up => self.up(),
                Key::Right => self.right(),
                Key::Down => self.down(),
                Key::Left => self.left(),
                Key::Restart => {
                    self.reset();
                    self.start();
                    return;
                }
                Key::Quit => return,
            };
            prev_direction = key;

            self.feed();
            self.draw();
            if game_over {
                self.draw_menu();
                // Continue to sleep to avoid busy loop.
            }

            let elapsed = start.elapsed();
            if speed > elapsed {
                sleep(speed - elapsed);
            }
            start = Instant::now();
            speed = self.try_speed_up();
        }
    }

    fn reset(&mut self) {
        self.snake.clear();
        self.snake.push_back(self.init_pos);
        self.score = 0;
        self.food = 0;
        self.spaces = 0;
        for i in 0..(self.rows * self.cols) {
            let obj = self.map[i];
            if obj != object::SPACE && obj != object::BARRIER {
                self.map[i] = object::SPACE;
            }
            if obj == object::SPACE && self.init_pos != i {
                self.spaces += 1;
            }
        }
        self.map[self.init_pos] = object::SNAKE_HEAD;
    }

    fn snake_head_pos(&self) -> (usize, usize) {
        let pos = self.snake[0];
        (pos / self.cols, pos % self.cols)
    }

    fn pos(&mut self, x: usize, y: usize) -> usize {
        let x = if x > self.rows {
            // overflow
            self.rows - 1
        } else if x == self.rows {
            0
        } else {
            x
        };
        let y = if y > self.cols {
            // overflow
            self.cols - 1
        } else if y == self.cols {
            0
        } else {
            y
        };

        x * self.cols + y
    }

    fn up(&mut self) -> bool {
        let (x, y) = self.snake_head_pos();
        self.move_to(x - 1, y)
    }

    fn down(&mut self) -> bool {
        let (x, y) = self.snake_head_pos();
        self.move_to(x + 1, y)
    }

    fn left(&mut self) -> bool {
        let (x, y) = self.snake_head_pos();
        self.move_to(x, y - 1)
    }

    fn right(&mut self) -> bool {
        let (x, y) = self.snake_head_pos();
        self.move_to(x, y + 1)
    }

    fn move_to(&mut self, x: usize, y: usize) -> bool {
        let pos = self.pos(x, y);
        match self.map[pos] {
            object::BARRIER | object::SNAKE_BODY => true,
            object::FOOD => {
                self.snake.push_front(pos);
                self.map[pos] = object::SNAKE_HEAD;
                self.map[self.snake[1]] = object::SNAKE_BODY;
                self.food -= 1;
                self.score += 1;
                false
            }
            object::SPACE => {
                self.snake.push_front(pos);
                self.map[pos] = object::SNAKE_HEAD;
                self.map[self.snake[1]] = object::SNAKE_BODY;
                let tail_pos = self.snake.pop_back().unwrap();
                self.map[tail_pos] = object::SPACE;
                false
            }
            _ => unreachable!(),
        }
    }

    fn feed(&mut self) {
        // TODO(damnever): maintain a space vector..
        while MAX_FOOD > self.food && self.spaces > 0 {
            let idx = self.rng.gen_range(0, self.map.len());
            if self.map[idx] == object::SPACE {
                self.map[idx] = object::FOOD;
                self.food += 1;
                self.spaces -= 1;
            }
        }
    }

    fn try_speed_up(&mut self) -> Duration {
        let mut idx = self.score / self.score_to_speed;
        if idx >= SPEEDS.len() {
            idx = SPEEDS.len() - 1;
        }

        SPEEDS[idx]
    }

    fn draw(&mut self) {
        write!(
            self.stdout,
            "{}{}{}",
            clear::All,
            style::Reset,
            cursor::Goto(1, 1)
        )
        .unwrap();

        let bg_border = color::Bg(color::Rgb(255, 255, 204));
        let width = (self.cols + 2) * 2;
        // Header
        let vir_line = format!(
            "{}{:spaces$}{}\n\r",
            bg_border,
            " ",
            style::Reset,
            spaces = width,
        );

        self.stdout.write(vir_line.as_bytes()).unwrap();
        let score = self.score.to_string();
        write!(
            self.stdout,
            "{}  SCORE: {}{:space$}{}\n\r",
            style::Bold,
            score,
            " ",
            style::Reset,
            space = width - 9 - score.len()
        )
        .unwrap();

        // Body
        let mut line: String = String::new();
        line.push_str(vir_line.as_str());
        self.stdout.write(line.as_bytes()).unwrap();
        for row in 0..self.rows {
            line.clear();
            line.push_str(format!("{}  {}", bg_border, style::Reset).as_str());
            for col in 0..self.cols {
                let bg = object::COLORS[self.map[row * self.cols + col] as usize];
                line.push_str(format!("{}{:2}{}", bg, " ", style::Reset).as_str());
            }
            line.push_str(format!("{}  {}\n\r", bg_border, style::Reset).as_str());
            self.stdout.write(line.as_bytes()).unwrap();
        }
        line.clear();
        line.push_str(vir_line.as_str());
        self.stdout.write(line.as_bytes()).unwrap();
        self.stdout.flush().unwrap();
    }

    fn draw_menu(&mut self) {
        let start = (self.cols / 3) as u16;
        let bg = color::Bg(color::Rgb(128, 128, 128));
        write!(
            self.stdout,
            "{}{}{}  GAME OVER!   {}\n\r",
            cursor::Goto(start, 4),
            bg,
            style::Bold,
            style::Reset
        )
        .unwrap();
        write!(
            self.stdout,
            "{}{}  restart: r   {}\n\r",
            cursor::Goto(start, 5),
            bg,
            style::Reset
        )
        .unwrap();
        write!(
            self.stdout,
            "{}{}  quit: q      {}\n\r",
            cursor::Goto(start, 6),
            bg,
            style::Reset
        )
        .unwrap();
        write!(
            self.stdout,
            "{}{}               {}\n\r",
            cursor::Goto(start, 7),
            bg,
            style::Reset
        )
        .unwrap();
        self.stdout.flush().unwrap();
    }
}

impl<R, W: Write> Drop for Game<R, W> {
    fn drop(&mut self) {
        write!(
            self.stdout,
            "{}{}{}",
            clear::All,
            style::Reset,
            cursor::Goto(1, 1)
        )
        .unwrap();
    }
}

fn read_map() -> (Vec<u8>, usize) {
    let mut map = Vec::new();
    let reader = BufReader::new(Cursor::new(DEFAULT_MAP));
    let mut cols = 0usize;

    for line in reader.lines() {
        let line = line.unwrap();
        if cols != 0 && line.len() != cols {
            panic!("column number msmatch with previous one");
        } else {
            cols = line.len();
        }
        for c in line.chars() {
            if c == SPACE_MARK {
                map.push(object::SPACE);
            } else if c == BARRIER_MARK {
                map.push(object::BARRIER);
            } else {
                panic!("unknown mark {}", c);
            }
        }
    }

    (map, cols)
}

pub fn main() {
    let (map, cols) = read_map();
    let rows = map.len() / cols;

    let stdout = io::stdout();
    let stdout = stdout.lock().into_raw_mode().unwrap();
    let stderr = io::stderr();
    let mut stderr = stderr.lock();

    let termsize = termion::terminal_size().ok();
    let (termwidth, termheight) = termsize.map(|(w, h)| (w - 2, h - 2)).unwrap();
    if termheight < (rows + 4) as u16 || termwidth < (cols + 2) as u16 * 2 {
        write!(
            stderr,
            "{}{}terminal size must satisfy with (width >= {}, height >= {}){}",
            style::Bold,
            color::Fg(color::Red),
            rows + 4,
            (cols + 2) * 2,
            style::Reset
        )
        .unwrap();
    } else {
        let mut map = map;
        let mut g = Game::new(async_stdin(), stdout, &mut map, cols);
        g.start();
    }
}
<commit_msg>snake: support custom map file<commit_after>extern crate rand;
extern crate termion;

use std::collections::VecDeque;
use std::env;
use std::fs;
use std::io::{self, BufRead, BufReader, Cursor, Read, Write};
use std::process;
use std::thread::sleep;
use std::time::{Duration, Instant};

use rand::Rng;
use termion::raw::IntoRawMode;
use termion::{async_stdin, clear, color, cursor, style};

mod object {
    use super::color;

    pub type Object = u8;
    pub const SPACE: Object = 0;
    pub const BARRIER: Object = 1;
    pub const SNAKE_HEAD: Object = 2;
    pub const SNAKE_BODY: Object = 3;
    pub const FOOD: Object = 4;

    pub const COLORS: [color::Bg<color::Rgb>; 5] = [
        color::Bg(color::Rgb(224, 224, 224)),
        color::Bg(color::Rgb(0, 0, 0)),
        color::Bg(color::Rgb(153, 0, 0)),
        color::Bg(color::Rgb(255, 0, 0)),
        color::Bg(color::Rgb(0, 153, 0)),
    ];
}

// TODO(damnever): make speed and game map configurable.
const SPACE_MARK: char = '.';
const BARRIER_MARK: char = '*';
const MAX_FOOD: usize = 10;
const SPEEDS: [Duration; 10] = [
    Duration::from_millis(600),
    Duration::from_millis(500),
    Duration::from_millis(400),
    Duration::from_millis(300),
    Duration::from_millis(200),
    Duration::from_millis(100),
    Duration::from_millis(80),
    Duration::from_millis(50),
    Duration::from_millis(30),
    Duration::from_millis(11),
];
const DEFAULT_MAP: &'static str = "**...............................................**
*.................................................*
...................................................
...................................................
...................................................
...................................................
...................................................
...................................................
*..................................................
*.................................................*
*.................................................*
*.................................................*
*.................................................*
*.................................................*
..................................................*
...................................................
...................................................
...................................................
...................................................
...................................................
...................................................
*.................................................*
**...............................................**
";

#[derive(Debug, Clone, Copy)]
enum Key {
    Up,
    Down,
    Left,
    Right,
    Restart,
    Quit,
}

impl Key {
    fn from(key_byte: u8, default: Key) -> Key {
        let key = key_byte as char;
        match key {
            'w' | 'k' => Key::Up,
            'd' | 'l' => Key::Right,
            's' | 'j' => Key::Down,
            'a' | 'h' => Key::Left,
            'r' => Key::Restart,
            'q' => Key::Quit,
            _ => default,
        }
    }

    fn rand_direction(rng: &mut rand::rngs::ThreadRng) -> Key {
        let directions = [Key::Up, Key::Down, Key::Left, Key::Right];
        directions[rng.gen_range(0, 4)]
    }
}

struct Game<R, W: Write> {
    init_pos: usize,
    rows: usize,
    cols: usize,
    score_to_speed: usize,
    score: usize,
    food: usize,
    spaces: usize,
    snake: VecDeque<usize>,
    map: Vec<object::Object>,
    stdin: R,
    stdout: W,
    rng: rand::rngs::ThreadRng,
}

impl<R: Read, W: Write> Game<R, W> {
    fn new(stdin: R, stdout: W, map: &mut Vec<u8>, cols: usize) -> Self {
        let init_pos = map.len() / 2;
        map[init_pos] = object::SNAKE_HEAD;
        let mut game = Game {
            init_pos: init_pos,
            rows: map.len() / cols,
            cols: cols,
            score_to_speed: map.len() / 3 / SPEEDS.len(),
            score: 0,
            food: 0,
            spaces: 0,
            snake: VecDeque::new(),
            map: map.to_owned(),
            stdin: stdin,
            stdout: stdout,
            rng: rand::thread_rng(),
        };
        game.reset();

        game
    }

    fn start(&mut self) {
        self.feed();
        self.draw();
        let mut speed = self.try_speed_up(true);
        let mut keys_buf = [0u8; 23]; // Whatever
        let mut prev_direction = Key::rand_direction(&mut self.rng);
        let mut start = Instant::now();

        loop {
            let keys_num = self.stdin.read(&mut keys_buf).unwrap();
            let mut key = prev_direction;
            if keys_num > 0 {
                key = Key::from(keys_buf[keys_num - 1], prev_direction);
            }

            let game_over = match key {
                Key::Up => self.up(),
                Key::Right => self.right(),
                Key::Down => self.down(),
                Key::Left => self.left(),
                Key::Restart => {
                    self.reset();
                    self.start();
                    return;
                }
                Key::Quit => return,
            };
            prev_direction = key;

            self.feed();
            self.draw();
            if game_over {
                self.draw_menu();
                // Continue to sleep to avoid busy loop.
            }

            let elapsed = start.elapsed();
            if speed > elapsed {
                sleep(speed - elapsed);
            }
            start = Instant::now();
            speed = self.try_speed_up(game_over);
        }
    }

    fn reset(&mut self) {
        self.snake.clear();
        self.snake.push_back(self.init_pos);
        self.score = 0;
        self.food = 0;
        self.spaces = 0;
        for i in 0..(self.rows * self.cols) {
            let obj = self.map[i];
            if obj != object::SPACE && obj != object::BARRIER {
                self.map[i] = object::SPACE;
            }
            if obj == object::SPACE && self.init_pos != i {
                self.spaces += 1;
            }
        }
        self.map[self.init_pos] = object::SNAKE_HEAD;
    }

    fn snake_head_pos(&self) -> (usize, usize) {
        let pos = self.snake[0];
        (pos / self.cols, pos % self.cols)
    }

    fn pos(&mut self, x: usize, y: usize) -> usize {
        let x = if x > self.rows {
            // overflow
            self.rows - 1
        } else if x == self.rows {
            0
        } else {
            x
        };
        let y = if y > self.cols {
            // overflow
            self.cols - 1
        } else if y == self.cols {
            0
        } else {
            y
        };

        x * self.cols + y
    }

    fn up(&mut self) -> bool {
        let (x, y) = self.snake_head_pos();
        self.move_to(x - 1, y)
    }

    fn down(&mut self) -> bool {
        let (x, y) = self.snake_head_pos();
        self.move_to(x + 1, y)
    }

    fn left(&mut self) -> bool {
        let (x, y) = self.snake_head_pos();
        self.move_to(x, y - 1)
    }

    fn right(&mut self) -> bool {
        let (x, y) = self.snake_head_pos();
        self.move_to(x, y + 1)
    }

    fn move_to(&mut self, x: usize, y: usize) -> bool {
        let pos = self.pos(x, y);
        match self.map[pos] {
            object::BARRIER | object::SNAKE_BODY => true,
            object::FOOD => {
                self.snake.push_front(pos);
                self.map[pos] = object::SNAKE_HEAD;
                self.map[self.snake[1]] = object::SNAKE_BODY;
                self.food -= 1;
                self.score += 1;
                false
            }
            object::SPACE => {
                self.snake.push_front(pos);
                self.map[pos] = object::SNAKE_HEAD;
                self.map[self.snake[1]] = object::SNAKE_BODY;
                let tail_pos = self.snake.pop_back().unwrap();
                self.map[tail_pos] = object::SPACE;
                false
            }
            _ => unreachable!(),
        }
    }

    fn feed(&mut self) {
        // TODO(damnever): maintain a space vector..
        while MAX_FOOD > self.food && self.spaces > 0 {
            let idx = self.rng.gen_range(0, self.map.len());
            if self.map[idx] == object::SPACE {
                self.map[idx] = object::FOOD;
                self.food += 1;
                self.spaces -= 1;
            }
        }
    }

    fn try_speed_up(&mut self, init: bool) -> Duration {
        if init {
            SPEEDS[0]
        } else {
            let mut idx = self.score / self.score_to_speed;
            if idx >= SPEEDS.len() {
                idx = SPEEDS.len() - 1;
            }
            SPEEDS[idx]
        }
    }

    fn draw(&mut self) {
        write!(
            self.stdout,
            "{}{}{}",
            clear::All,
            style::Reset,
            cursor::Goto(1, 1)
        )
        .unwrap();

        let bg_border = color::Bg(color::Rgb(255, 255, 204));
        let width = (self.cols + 2) * 2;
        // Header
        let vir_line = format!(
            "{}{:spaces$}{}\n\r",
            bg_border,
            " ",
            style::Reset,
            spaces = width,
        );

        self.stdout.write(vir_line.as_bytes()).unwrap();
        let score = self.score.to_string();
        write!(
            self.stdout,
            "{}  SCORE: {}{:space$}{}\n\r",
            style::Bold,
            score,
            " ",
            style::Reset,
            space = width - 9 - score.len()
        )
        .unwrap();

        // Body
        let mut line: String = String::new();
        line.push_str(vir_line.as_str());
        self.stdout.write(line.as_bytes()).unwrap();
        for row in 0..self.rows {
            line.clear();
            line.push_str(format!("{}  {}", bg_border, style::Reset).as_str());
            for col in 0..self.cols {
                let bg = object::COLORS[self.map[row * self.cols + col] as usize];
                line.push_str(format!("{}{:2}{}", bg, " ", style::Reset).as_str());
            }
            line.push_str(format!("{}  {}\n\r", bg_border, style::Reset).as_str());
            self.stdout.write(line.as_bytes()).unwrap();
        }
        line.clear();
        line.push_str(vir_line.as_str());
        self.stdout.write(line.as_bytes()).unwrap();
        self.stdout.flush().unwrap();
    }

    fn draw_menu(&mut self) {
        let start = (self.cols / 3) as u16;
        let bg = color::Bg(color::Rgb(128, 128, 128));
        write!(
            self.stdout,
            "{}{}{}  GAME OVER!   {}\n\r",
            cursor::Goto(start, 4),
            bg,
            style::Bold,
            style::Reset
        )
        .unwrap();
        write!(
            self.stdout,
            "{}{}  restart: r   {}\n\r",
            cursor::Goto(start, 5),
            bg,
            style::Reset
        )
        .unwrap();
        write!(
            self.stdout,
            "{}{}  quit: q      {}\n\r",
            cursor::Goto(start, 6),
            bg,
            style::Reset
        )
        .unwrap();
        write!(
            self.stdout,
            "{}{}               {}\n\r",
            cursor::Goto(start, 7),
            bg,
            style::Reset
        )
        .unwrap();
        self.stdout.flush().unwrap();
    }
}

impl<R, W: Write> Drop for Game<R, W> {
    fn drop(&mut self) {
        write!(
            self.stdout,
            "{}{}{}",
            clear::All,
            style::Reset,
            cursor::Goto(1, 1)
        )
        .unwrap();
    }
}

fn parse_map(content: String) -> (Vec<u8>, usize) {
    let mut map = Vec::new();
    let reader = BufReader::new(Cursor::new(content));
    let mut cols = 0usize;

    for line in reader.lines() {
        let line = line.unwrap();
        if cols != 0 && line.len() != cols {
            panic!("column number msmatch with previous one");
        } else {
            cols = line.len();
        }
        for c in line.chars() {
            if c == SPACE_MARK {
                map.push(object::SPACE);
            } else if c == BARRIER_MARK {
                map.push(object::BARRIER);
            } else {
                panic!("unknown mark {}", c);
            }
        }
    }

    (map, cols)
}

pub fn main() {
    let args: Vec<String> = env::args().collect();
    let mut map_content = DEFAULT_MAP.to_string();
    if args.len() > 1 {
        match args[1].as_str() {
            "--help" | "-h" => {
                eprintln!("Usage: {} [OPTIONS]", &args[0]);
                eprintln!("\nOPTIONS:");
                eprintln!("  --help/-h      Show this message.");
                eprintln!(
                    "  --map/-m FILE  The map file path, '*' means wall, '.' means an empty space,"
                );
                eprintln!("                 the snake will start from the center of the map.");
                process::exit(1);
            }
            "--map" | "-m" => {
                if args.len() < 3 {
                    eprintln!(
                        "{}{}Missing file path.{}",
                        style::Bold,
                        color::Fg(color::Red),
                        style::Reset
                    );
                    process::exit(1);
                }
                let content = fs::read(&args[2]).unwrap_or_else(|err| {
                    eprintln!(
                        "{}{}Read file {}: {}{}",
                        style::Bold,
                        color::Fg(color::Red),
                        &args[2],
                        err,
                        style::Reset
                    );
                    process::exit(1);
                });
                map_content = String::from_utf8(content).unwrap();
            }
            _ => {}
        }
    }

    let (map, cols) = parse_map(map_content);
    let rows = map.len() / cols;

    let stdout = io::stdout();
    let stdout = stdout.lock().into_raw_mode().unwrap();
    let stderr = io::stderr();
    let mut stderr = stderr.lock();
    let termsize = termion::terminal_size().ok();
    let (termwidth, termheight) = termsize.map(|(w, h)| (w - 2, h - 2)).unwrap();
    if termheight < (rows + 4) as u16 || termwidth < (cols + 2) as u16 * 2 {
        writeln!(
            stderr,
            "{}{}terminal size must satisfy with (width >= {}, height >= {}){}",
            style::Bold,
            color::Fg(color::Red),
            rows + 4,
            (cols + 2) * 2,
            style::Reset
        )
        .unwrap();
        process::exit(1);
    }
    let mut map = map;
    let mut g = Game::new(async_stdin(), stdout, &mut map, cols);
    g.start();
}
<|endoftext|>


<commit_before>// Copyright (c) 2013-2015 Sandstorm Development Group, Inc. and contributors
// Licensed under the MIT License:
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

//! Dynamically typed value.

use capability::FromClientHook;
use private::capability::{ClientHook, PipelineHook, PipelineOp};
use private::layout::{PointerReader, PointerBuilder};
use traits::{FromPointerReader, FromPointerBuilder, SetPointerBuilder};
use Result;

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

impl <'a> ::traits::Owned<'a> for Owned {
    type Reader = Reader<'a>;
    type Builder = Builder<'a>;
}

impl ::traits::Pipelined for Owned {
    type Pipeline = Pipeline;
}

#[derive(Copy, Clone)]
pub struct Reader<'a> {
    reader : PointerReader<'a>
}

impl <'a> Reader<'a> {
    #[inline]
    pub fn new<'b>(reader : PointerReader<'b>) -> Reader<'b> {
        Reader { reader : reader }
    }

    #[inline]
    pub fn is_null(&self) -> bool {
        self.reader.is_null()
    }

    /// Gets the total size of the target and all of its children. Does not count far pointer overhead.
    pub fn total_size(&self) -> Result<::MessageSize> {
        self.reader.total_size()
    }

    #[inline]
    pub fn get_as<T: FromPointerReader<'a>>(&self) -> Result<T> {
        FromPointerReader::get_from_pointer(&self.reader)
    }

    pub fn get_as_capability<T: FromClientHook>(&self) -> Result<T> {
        Ok(FromClientHook::new(try!(self.reader.get_capability())))
    }

    //# Used by RPC system to implement pipelining. Applications
    //# generally shouldn't use this directly.
    pub fn get_pipelined_cap(&self, ops: &[PipelineOp]) -> Result<Box<ClientHook>> {
        let mut pointer = self.reader;

        for op in ops.iter() {
            match op {
                &PipelineOp::Noop =>  { }
                &PipelineOp::GetPointerField(idx) => {
                    pointer = try!(pointer.get_struct(::std::ptr::null())).get_pointer_field(idx as usize);
                }
            }
        }

        pointer.get_capability()
    }
}

impl <'a> FromPointerReader<'a> for Reader<'a> {
    fn get_from_pointer(reader: &PointerReader<'a>) -> Result<Reader<'a>> {
        Ok(Reader { reader: *reader })
    }
}

impl <'a> ::traits::SetPointerBuilder<Builder<'a>> for Reader<'a> {
    fn set_pointer_builder<'b>(mut pointer: ::private::layout::PointerBuilder<'b>,
                               value: Reader<'a>) -> Result<()> {
        pointer.copy_from(value.reader)
    }
}

pub struct Builder<'a> {
    builder: PointerBuilder<'a>
}

impl <'a> Builder<'a> {
    #[inline]
    pub fn new<'b>(builder : PointerBuilder<'a>) -> Builder<'a> {
        Builder { builder : builder }
    }

    pub fn borrow<'b>(&'b mut self) -> Builder<'b> {
        Builder { builder: self.builder.borrow() }
    }

    pub fn is_null(&self) -> bool {
        self.builder.is_null()
    }

    /// Gets the total size of the target and all of its children. Does not count far pointer overhead.
    pub fn total_size(&self) -> Result<::MessageSize> {
        self.builder.as_reader().total_size()
    }

    pub fn get_as<T : FromPointerBuilder<'a>>(self) -> Result<T> {
        FromPointerBuilder::get_from_pointer(self.builder)
    }

    pub fn init_as<T : FromPointerBuilder<'a>>(self) -> T {
        FromPointerBuilder::init_pointer(self.builder, 0)
    }

    pub fn initn_as<T: FromPointerBuilder<'a>>(self, size: u32) -> T {
        FromPointerBuilder::init_pointer(self.builder, size)
    }

    pub fn set_as<To, From : SetPointerBuilder<To>>(self, value : From) -> Result<()> {
        SetPointerBuilder::<To>::set_pointer_builder(self.builder, value)
    }

    // XXX value should be a user client.
    pub fn set_as_capability(&mut self, value: Box<ClientHook>) {
        self.builder.set_capability(value);
    }

    #[inline]
    pub fn clear(&mut self) {
        self.builder.clear()
    }

    #[inline]
    pub fn as_reader(self) -> Reader<'a> {
        Reader { reader : self.builder.as_reader() }
    }
}

impl <'a> FromPointerBuilder<'a> for Builder<'a> {
    fn init_pointer(builder: PointerBuilder<'a>, _len: u32) -> Builder<'a> {
        Builder { builder: builder }
    }
    fn get_from_pointer(builder: PointerBuilder<'a>) -> Result<Builder<'a>> {
        Ok(Builder { builder: builder })
    }
}

impl <'a> ::traits::ImbueMut<'a> for Builder<'a> {
    fn imbue_mut(&mut self, cap_table: &'a mut ::private::layout::CapTable) {
        self.builder.imbue(::private::layout::CapTableBuilder::Plain(cap_table));
    }
}

pub struct Pipeline {
    hook: Box<PipelineHook>,
    ops: Vec<PipelineOp>,
}

impl Pipeline {
    pub fn new(hook: Box<PipelineHook>) -> Pipeline {
        Pipeline { hook: hook, ops: Vec::new() }
    }

    pub fn noop(&self) -> Pipeline {
        Pipeline { hook : self.hook.add_ref(), ops : self.ops.clone() }
    }

    pub fn get_pointer_field(&self, pointer_index: u16) -> Pipeline {
        let mut new_ops = Vec::with_capacity(self.ops.len() + 1);
        for &op in self.ops.iter() {
            new_ops.push(op)
        }
        new_ops.push(PipelineOp::GetPointerField(pointer_index));
        Pipeline { hook : self.hook.add_ref(), ops: new_ops }
    }

    pub fn as_cap(&self) -> Box<ClientHook> {
        self.hook.get_pipelined_cap(&self.ops)
    }
}

<commit_msg>Imbue for any_pointer::Reader<commit_after>// Copyright (c) 2013-2015 Sandstorm Development Group, Inc. and contributors
// Licensed under the MIT License:
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

//! Dynamically typed value.

use capability::FromClientHook;
use private::capability::{ClientHook, PipelineHook, PipelineOp};
use private::layout::{PointerReader, PointerBuilder};
use traits::{FromPointerReader, FromPointerBuilder, SetPointerBuilder};
use Result;

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

impl <'a> ::traits::Owned<'a> for Owned {
    type Reader = Reader<'a>;
    type Builder = Builder<'a>;
}

impl ::traits::Pipelined for Owned {
    type Pipeline = Pipeline;
}

#[derive(Copy, Clone)]
pub struct Reader<'a> {
    reader : PointerReader<'a>
}

impl <'a> Reader<'a> {
    #[inline]
    pub fn new<'b>(reader : PointerReader<'b>) -> Reader<'b> {
        Reader { reader : reader }
    }

    #[inline]
    pub fn is_null(&self) -> bool {
        self.reader.is_null()
    }

    /// Gets the total size of the target and all of its children. Does not count far pointer overhead.
    pub fn total_size(&self) -> Result<::MessageSize> {
        self.reader.total_size()
    }

    #[inline]
    pub fn get_as<T: FromPointerReader<'a>>(&self) -> Result<T> {
        FromPointerReader::get_from_pointer(&self.reader)
    }

    pub fn get_as_capability<T: FromClientHook>(&self) -> Result<T> {
        Ok(FromClientHook::new(try!(self.reader.get_capability())))
    }

    //# Used by RPC system to implement pipelining. Applications
    //# generally shouldn't use this directly.
    pub fn get_pipelined_cap(&self, ops: &[PipelineOp]) -> Result<Box<ClientHook>> {
        let mut pointer = self.reader;

        for op in ops.iter() {
            match op {
                &PipelineOp::Noop =>  { }
                &PipelineOp::GetPointerField(idx) => {
                    pointer = try!(pointer.get_struct(::std::ptr::null())).get_pointer_field(idx as usize);
                }
            }
        }

        pointer.get_capability()
    }
}

impl <'a> FromPointerReader<'a> for Reader<'a> {
    fn get_from_pointer(reader: &PointerReader<'a>) -> Result<Reader<'a>> {
        Ok(Reader { reader: *reader })
    }
}

impl <'a> ::traits::SetPointerBuilder<Builder<'a>> for Reader<'a> {
    fn set_pointer_builder<'b>(mut pointer: ::private::layout::PointerBuilder<'b>,
                               value: Reader<'a>) -> Result<()> {
        pointer.copy_from(value.reader)
    }
}

impl <'a> ::traits::Imbue<'a> for Reader<'a> {
    fn imbue(&mut self, cap_table: &'a ::private::layout::CapTable) {
        self.reader.imbue(::private::layout::CapTableReader::Plain(cap_table));
    }
}

pub struct Builder<'a> {
    builder: PointerBuilder<'a>
}

impl <'a> Builder<'a> {
    #[inline]
    pub fn new<'b>(builder : PointerBuilder<'a>) -> Builder<'a> {
        Builder { builder : builder }
    }

    pub fn borrow<'b>(&'b mut self) -> Builder<'b> {
        Builder { builder: self.builder.borrow() }
    }

    pub fn is_null(&self) -> bool {
        self.builder.is_null()
    }

    /// Gets the total size of the target and all of its children. Does not count far pointer overhead.
    pub fn total_size(&self) -> Result<::MessageSize> {
        self.builder.as_reader().total_size()
    }

    pub fn get_as<T : FromPointerBuilder<'a>>(self) -> Result<T> {
        FromPointerBuilder::get_from_pointer(self.builder)
    }

    pub fn init_as<T : FromPointerBuilder<'a>>(self) -> T {
        FromPointerBuilder::init_pointer(self.builder, 0)
    }

    pub fn initn_as<T: FromPointerBuilder<'a>>(self, size: u32) -> T {
        FromPointerBuilder::init_pointer(self.builder, size)
    }

    pub fn set_as<To, From : SetPointerBuilder<To>>(self, value : From) -> Result<()> {
        SetPointerBuilder::<To>::set_pointer_builder(self.builder, value)
    }

    // XXX value should be a user client.
    pub fn set_as_capability(&mut self, value: Box<ClientHook>) {
        self.builder.set_capability(value);
    }

    #[inline]
    pub fn clear(&mut self) {
        self.builder.clear()
    }

    #[inline]
    pub fn as_reader(self) -> Reader<'a> {
        Reader { reader : self.builder.as_reader() }
    }
}

impl <'a> FromPointerBuilder<'a> for Builder<'a> {
    fn init_pointer(builder: PointerBuilder<'a>, _len: u32) -> Builder<'a> {
        Builder { builder: builder }
    }
    fn get_from_pointer(builder: PointerBuilder<'a>) -> Result<Builder<'a>> {
        Ok(Builder { builder: builder })
    }
}

impl <'a> ::traits::ImbueMut<'a> for Builder<'a> {
    fn imbue_mut(&mut self, cap_table: &'a mut ::private::layout::CapTable) {
        self.builder.imbue(::private::layout::CapTableBuilder::Plain(cap_table));
    }
}

pub struct Pipeline {
    hook: Box<PipelineHook>,
    ops: Vec<PipelineOp>,
}

impl Pipeline {
    pub fn new(hook: Box<PipelineHook>) -> Pipeline {
        Pipeline { hook: hook, ops: Vec::new() }
    }

    pub fn noop(&self) -> Pipeline {
        Pipeline { hook : self.hook.add_ref(), ops : self.ops.clone() }
    }

    pub fn get_pointer_field(&self, pointer_index: u16) -> Pipeline {
        let mut new_ops = Vec::with_capacity(self.ops.len() + 1);
        for &op in self.ops.iter() {
            new_ops.push(op)
        }
        new_ops.push(PipelineOp::GetPointerField(pointer_index));
        Pipeline { hook : self.hook.add_ref(), ops: new_ops }
    }

    pub fn as_cap(&self) -> Box<ClientHook> {
        self.hook.get_pipelined_cap(&self.ops)
    }
}

<|endoftext|>


<commit_before>use boxcars::crc::calc_crc;
use boxcars::*;
use criterion::{black_box, criterion_group, criterion_main, Criterion, Throughput};

fn bench_crc(c: &mut Criterion) {
    let data = include_bytes!("../assets/replays/good/rumble.replay");
    let mut group = c.benchmark_group("crc_throughput");
    group.throughput(Throughput::Bytes(data.len() as u64));
    group.bench_function("bench_crc", |b| {
        let data = include_bytes!("../assets/replays/good/rumble.replay");
        b.iter(|| {
            black_box(calc_crc(&data[..]));
        })
    });
    group.finish();
}

fn bench_json_serialization(c: &mut Criterion) {
    let data = include_bytes!("../assets/replays/good/3381.replay");
    let json_data_bytes = 19416972_u64;

    let mut group = c.benchmark_group("json_throughput");
    group.sample_size(10);
    group.throughput(Throughput::Bytes(data.len() as u64));
    group.bench_function("bench_json_serialization", |b| {
        let mut bytes = Vec::new();
        let replay = ParserBuilder::new(data)
            .on_error_check_crc()
            .parse()
            .unwrap();
        serde_json::to_writer(&mut bytes, &replay).unwrap();
        assert!(json_data_bytes == bytes.len() as u64);
        unsafe {
            bytes.set_len(0);
        };

        b.iter(|| {
            black_box(serde_json::to_writer(&mut bytes, &replay).is_ok());
            unsafe {
                bytes.set_len(0);
            };
        })
    });
    group.finish();
}

fn bench_parse_crc_body(c: &mut Criterion) {
    let data = include_bytes!("../assets/replays/good/3381.replay");
    let mut group = c.benchmark_group("parse_crc_body");
    group.throughput(Throughput::Bytes(data.len() as u64));
    group.sample_size(20);
    group.bench_function("bench_parse_crc_body", |b| {
        b.iter(|| {
            black_box(
                ParserBuilder::new(data)
                    .always_check_crc()
                    .must_parse_network_data()
                    .parse()
                    .is_ok(),
            )
        });
    });
    group.finish();
}

fn bench_parse_no_crc_body(c: &mut Criterion) {
    let data = include_bytes!("../assets/replays/good/3381.replay");
    let mut group = c.benchmark_group("parse_no_crc_body");
    group.throughput(Throughput::Bytes(data.len() as u64));
    group.sample_size(20);
    group.bench_function("bench_parse_no_crc_body", |b| {
        b.iter(|| {
            black_box(
                ParserBuilder::new(data)
                    .on_error_check_crc()
                    .must_parse_network_data()
                    .parse()
                    .is_ok(),
            )
        });
    });
    group.finish();
}

fn bench_parse_no_crc_no_body(c: &mut Criterion) {
    // Throughput not included in this benchmark as it is a bit confusing what the number reporting
    // represents. If parsing the header reports based on the entire size of the replay, it will be
    // some very large number (20 GB/s), else it may look deceptively small
    let data = include_bytes!("../assets/replays/good/3381.replay");
    let mut group = c.benchmark_group("parse_no_crc_no_body");
    group.bench_function("bench_parse_no_crc_no_body", |b| {
        b.iter(|| {
            black_box(
                ParserBuilder::new(data)
                    .on_error_check_crc()
                    .never_parse_network_data()
                    .parse()
                    .is_ok(),
            )
        });
    });
    group.finish();
}

fn bench_parse_crc_json(c: &mut Criterion) {
    let data = include_bytes!("../assets/replays/good/3381.replay");
    let mut group = c.benchmark_group("parse_crc_json");
    group.throughput(Throughput::Bytes(data.len() as u64));
    group.sample_size(10);
    group.bench_function("bench_parse_crc_json", |b| {
        // allocate a buffer big enough to hold all of the serialized data
        let mut bytes = Vec::with_capacity(2_usize.pow(25));
        b.iter(|| {
            let data = ParserBuilder::new(data).always_check_crc().parse().unwrap();
            black_box(serde_json::to_writer(&mut bytes, &data).is_ok());
            unsafe {
                bytes.set_len(0);
            }
        });
    });
    group.finish();
}

criterion_group!(
    benches,
    bench_crc,
    bench_json_serialization,
    bench_parse_crc_body,
    bench_parse_no_crc_body,
    bench_parse_no_crc_no_body,
    bench_parse_crc_json,
);

criterion_main!(benches);
<commit_msg>Fix json benchmark throughput numbers<commit_after>use boxcars::crc::calc_crc;
use boxcars::*;
use criterion::{black_box, criterion_group, criterion_main, Criterion, Throughput};

fn bench_crc(c: &mut Criterion) {
    let data = include_bytes!("../assets/replays/good/rumble.replay");
    let mut group = c.benchmark_group("crc_throughput");
    group.throughput(Throughput::Bytes(data.len() as u64));
    group.bench_function("bench_crc", |b| {
        let data = include_bytes!("../assets/replays/good/rumble.replay");
        b.iter(|| {
            black_box(calc_crc(&data[..]));
        })
    });
    group.finish();
}

fn bench_json_serialization(c: &mut Criterion) {
    let data = include_bytes!("../assets/replays/good/3381.replay");
    let json_data_bytes = 19416972_u64;

    let mut group = c.benchmark_group("json_throughput");
    group.sample_size(10);
    group.throughput(Throughput::Bytes(json_data_bytes));
    group.bench_function("bench_json_serialization", |b| {
        let mut bytes = Vec::new();
        let replay = ParserBuilder::new(data)
            .on_error_check_crc()
            .parse()
            .unwrap();
        serde_json::to_writer(&mut bytes, &replay).unwrap();
        assert!(json_data_bytes == bytes.len() as u64);
        unsafe {
            bytes.set_len(0);
        };

        b.iter(|| {
            black_box(serde_json::to_writer(&mut bytes, &replay).is_ok());
            unsafe {
                bytes.set_len(0);
            };
        })
    });
    group.finish();
}

fn bench_parse_crc_body(c: &mut Criterion) {
    let data = include_bytes!("../assets/replays/good/3381.replay");
    let mut group = c.benchmark_group("parse_crc_body");
    group.throughput(Throughput::Bytes(data.len() as u64));
    group.sample_size(20);
    group.bench_function("bench_parse_crc_body", |b| {
        b.iter(|| {
            black_box(
                ParserBuilder::new(data)
                    .always_check_crc()
                    .must_parse_network_data()
                    .parse()
                    .is_ok(),
            )
        });
    });
    group.finish();
}

fn bench_parse_no_crc_body(c: &mut Criterion) {
    let data = include_bytes!("../assets/replays/good/3381.replay");
    let mut group = c.benchmark_group("parse_no_crc_body");
    group.throughput(Throughput::Bytes(data.len() as u64));
    group.sample_size(20);
    group.bench_function("bench_parse_no_crc_body", |b| {
        b.iter(|| {
            black_box(
                ParserBuilder::new(data)
                    .on_error_check_crc()
                    .must_parse_network_data()
                    .parse()
                    .is_ok(),
            )
        });
    });
    group.finish();
}

fn bench_parse_no_crc_no_body(c: &mut Criterion) {
    // Throughput not included in this benchmark as it is a bit confusing what the number reporting
    // represents. If parsing the header reports based on the entire size of the replay, it will be
    // some very large number (20 GB/s), else it may look deceptively small
    let data = include_bytes!("../assets/replays/good/3381.replay");
    let mut group = c.benchmark_group("parse_no_crc_no_body");
    group.bench_function("bench_parse_no_crc_no_body", |b| {
        b.iter(|| {
            black_box(
                ParserBuilder::new(data)
                    .on_error_check_crc()
                    .never_parse_network_data()
                    .parse()
                    .is_ok(),
            )
        });
    });
    group.finish();
}

fn bench_parse_crc_json(c: &mut Criterion) {
    let data = include_bytes!("../assets/replays/good/3381.replay");
    let mut group = c.benchmark_group("parse_crc_json");
    group.throughput(Throughput::Bytes(data.len() as u64));
    group.sample_size(10);
    group.bench_function("bench_parse_crc_json", |b| {
        // allocate a buffer big enough to hold all of the serialized data
        let mut bytes = Vec::with_capacity(2_usize.pow(25));
        b.iter(|| {
            let data = ParserBuilder::new(data).always_check_crc().parse().unwrap();
            black_box(serde_json::to_writer(&mut bytes, &data).is_ok());
            unsafe {
                bytes.set_len(0);
            }
        });
    });
    group.finish();
}

criterion_group!(
    benches,
    bench_crc,
    bench_json_serialization,
    bench_parse_crc_body,
    bench_parse_no_crc_body,
    bench_parse_no_crc_no_body,
    bench_parse_crc_json,
);

criterion_main!(benches);
<|endoftext|>


<commit_before>use std::fmt::Debug;
use std::hash::Hash;
use std::rc::Rc;

use collections::*;

#[derive(Clone,Copy,Hash,Eq,PartialEq,Debug)]
pub enum Op { Plus, Minus, Times, Divide, ParanOpen, ParanClose }

#[derive(Clone,Copy,Hash,Eq,PartialEq,Debug)]
pub enum Tok {
  Num(isize),
  Op(Op)
}

pub enum Precedence { Higher, Lower, Equal }

fn pop<X:Clone+Copy+Hash+Eq+PartialEq+Debug>
  (stack:List<X>) -> (X, List<X>) {
    List::elim_arg(stack, (),
                   |_,_|    panic!("cannot pop an empty stack"),
                   |h,t, _| (h, t),
                   |_,t, _| pop(t))
}

fn push<X:Clone+Copy+Hash+Eq+PartialEq+Debug>
  (stack:List<X>, elm:X) -> List<X>
{
  List::cons(elm, stack)
}

fn precedence_check (top_op:&Op, next_op:&Op) -> Precedence {
  match (*top_op, *next_op) {
    (Op::Plus, Op::Plus) => Precedence::Equal,
    (Op::Plus, Op::Minus) => Precedence::Equal,
    (Op::Minus, Op::Plus) => Precedence::Equal,
    (Op::Minus, Op::Minus) => Precedence::Equal,

    (Op::Times, Op::Times) => Precedence::Equal,
    (Op::Divide, Op::Divide) => Precedence::Equal,

    (Op::Times, Op::Plus)   => Precedence::Higher,
    (Op::Times, Op::Minus)  => Precedence::Higher,
    (Op::Times, Op::Divide) => Precedence::Higher,

    (Op::Divide, Op::Plus)  => Precedence::Higher,
    (Op::Divide, Op::Minus) => Precedence::Higher,
    (Op::Divide, Op::Times) => Precedence::Higher,
    _ => Precedence::Lower
  }
}

pub fn check_ops_rec (ops:List<Op>, op:Op) -> bool {
  if List::is_empty(&ops) { true }
  else {
    let (top_op, popped_ops) = pop(ops);
    match precedence_check(&op, &top_op) {
      Precedence::Higher => check_ops_rec(popped_ops, top_op),
      _ => false
    }
  }
}

pub fn check_ops (ops:List<Op>) -> bool {
  if List::is_empty(&ops) { true }
  else {
    let (top_op, popped_ops) = pop(ops);
    check_ops_rec(popped_ops, top_op)
  }
}

pub fn postfix_of_infix(infix: Tree<Tok>) -> List<Tok> {
  let (ops, postfix) : (List<Op>, List<Tok>) =
    tree_fold_seq
    (infix, Dir2::Left, (List::Nil, List::Nil),
     Rc::new(|tok, (ops, postfix) : (List<Op>, List<Tok>)| {
       //assert!(check_ops(ops.clone()));
       match tok {
         Tok::Num(n) => (ops, push(postfix, Tok::Num(n))),
         Tok::Op(op) => {
           match op {
	    Op::ParanOpen => {(push(ops, op), postfix)}
            Op::ParanClose => {
              fn myloop (op:Op, ops:List<Op>, postfix:List<Tok>) -> (List<Op>, List<Tok>) {
                let (top_op, popped_ops) = pop(ops);
		match top_op {
		  Op::ParanOpen => {print!("hello");(popped_ops, postfix)},
                  _ => {myloop(op, popped_ops, push(postfix, Tok::Op(top_op)))},
                }
	      };
              myloop(op, ops, postfix)
	    }
	    _ => {
             if List::is_empty(&ops) { (push(ops, op), postfix) }
             else {
               fn myloop (op:Op, ops:List<Op>, postfix:List<Tok>) -> (List<Op>, List<Tok>) {
                 if List::is_empty(&ops) { (ops, postfix) } else {
                   let (top_op, popped_ops) = pop(ops);
                   match precedence_check (&top_op, &op) {
                     Precedence::Higher => { myloop (op, popped_ops, push(postfix, Tok::Op(top_op)))},
	             Precedence::Equal => {(popped_ops, push(postfix, Tok::Op(top_op)))},
                     _ => {(push(popped_ops, top_op), postfix)}
                   }
                 }};
               let (ops, postfix) = myloop(op, ops, postfix) ;
               (push(ops, op), postfix)
             }
           }}
         }
       }}),
     Rc::new(|_, a|   a),
     Rc::new(|_,_, a| a),
     );
  fn myloop (ops:List<Op>, postfix:List<Tok>) -> (List<Tok>) {
    if List::is_empty(&ops) { postfix } else {
      let (top_op, popped_ops) = pop(ops);
      myloop (popped_ops, push(postfix, Tok::Op(top_op)))
    }};
  myloop(ops, postfix)
}

#[test]
fn test_postfix_of_infix () {

  // 1 * 2 + 3
  let input = vec![
    NameElse::Else( Tok::Num(1) ),
    NameElse::Else( Tok::Op(Op::Times) ),
    NameElse::Else( Tok::Num(2) ),
    NameElse::Else( Tok::Op(Op::Plus) ),
    NameElse::Else( Tok::Num(3) ),
    ];
  let input : List<Tok> = list_of_vec(&input);
  let tree  : Tree<Tok> = tree_of_list(Dir2::Left, input);
  let ans = postfix_of_infix(tree);
  println!("{:?}", ans)
}

pub fn evaluate_postfix(input: Tree<Tok>) -> isize {
  let stack =
    tree_fold_seq
    (input, Dir2::Left, List::Nil,
     Rc::new(|tok, stack| {
       match tok {
         Tok::Op(op) => {
           let (x,stack) = pop(stack);
           let (y,stack) = pop(stack);
           let z = match op {
             Op::Plus   => y + x,
             Op::Minus  => y - x,
             Op::Times  => y * x,
             Op::Divide => y / x,
	     _ => panic!(),
           };
           push(stack, z)
         },
         Tok::Num(n) => {
           push(stack, n)
         }
       }}),
     Rc::new(|_, stack|   stack),
     Rc::new(|_,_, stack| stack),
     );
  let (x, stack) = pop(stack);
  assert!(List::is_empty(&stack));
  x
}

#[test]
fn test_eval_postfix () {
  let input = vec![
    NameElse::Else( Tok::Num(1) ),
    NameElse::Else( Tok::Num(2) ),
    NameElse::Else( Tok::Num(3) ),
    NameElse::Else( Tok::Op(Op::Plus) ),
    NameElse::Else( Tok::Op(Op::Plus) ),
    NameElse::Else( Tok::Num(22) ),
    NameElse::Else( Tok::Num(33) ),
    NameElse::Else( Tok::Op(Op::Plus) ),
    NameElse::Else( Tok::Op(Op::Plus) ),
    NameElse::Else( Tok::Num(12) ),
    NameElse::Else( Tok::Num(13) ),
    NameElse::Else( Tok::Op(Op::Plus) ),
    NameElse::Else( Tok::Op(Op::Plus) ),
    ];
  let input : List<Tok> = list_of_vec(&input);
  let tree  : Tree<Tok> = tree_of_list(Dir2::Left, input);
  let ans = evaluate_postfix(tree);
  assert_eq!(ans, 86)
}

#[test]
fn test_arith_eval () {

  // 1 * 2 + 3
  let input = vec![
    NameElse::Else( Tok::Num(1) ),
    NameElse::Else( Tok::Op(Op::Plus) ),
    NameElse::Else( Tok::Num(2) ),
    NameElse::Else( Tok::Op(Op::Minus) ),
    NameElse::Else( Tok::Num(3) ),
    NameElse::Else( Tok::Op(Op::Minus) ),
    NameElse::Else( Tok::Num(1) ),
    NameElse::Else( Tok::Op(Op::Minus) ),
    NameElse::Else( Tok::Num(2) ),
    NameElse::Else( Tok::Op(Op::Times) ),
    NameElse::Else( Tok::Num(2) ),
    // NameElse::Else( Tok::Op(Op::Divide) ),
    // NameElse::Else( Tok::Num(2) ),
    // NameElse::Else( Tok::Op(Op::Minus) ),
    // NameElse::Else( Tok::Num(3) ),
    ];
  println!("{:?}", &input);
  let input : List<Tok> = list_of_vec(&input);
  let tree  : Tree<Tok> = tree_of_list(Dir2::Left, input);
  let list  : List<Tok> = postfix_of_infix(tree);
  println!("{:?}", &list);
  //let input : List<Tok> = list_of_vec(&input);
  let tree  : Tree<Tok> = tree_of_list(Dir2::Right, list);
  let ans = evaluate_postfix(tree);
  println!("{:?}", ans)
}

#[test]
fn test_paran_eval () {

  // 1 * 2 + 3
  let input = vec![
    NameElse::Else( Tok::Num(2) ),
    NameElse::Else( Tok::Op(Op::Times) ),
    NameElse::Else( Tok::Op(Op::ParanOpen) ),
    //NameElse::Else( Tok::Num(1) ),
    //NameElse::Else( Tok::Op(Op::Plus) ),
    NameElse::Else( Tok::Num(2) ),
    NameElse::Else( Tok::Op(Op::Minus) ),
    NameElse::Else( Tok::Num(3) ),
    NameElse::Else( Tok::Op(Op::ParanClose) ),
    ];

    println!("{:?}", &input);
    let input : List<Tok> = list_of_vec(&input);
    let tree  : Tree<Tok> = tree_of_list(Dir2::Left, input);
    let list  : List<Tok> = postfix_of_infix(tree);
    println!("{:?}", &list);
    //let input : List<Tok> = list_of_vec(&input);
    let tree  : Tree<Tok> = tree_of_list(Dir2::Right, list);
    let ans = evaluate_postfix(tree);
    println!("{:?}", ans)
}

// fn infix_to_postfix(input: List<char>) -> List<List<char>> {
//   let mut postfix = List::Nil;
//   let mut operators = List::Nil;
//   let mut temp_operand = List::Nil;

//   //'''
//   // Match all chars in 'input'
//   //     If c is a digit, then push onto a Digit list temporarily until all digits in an operand are read (after which add the operand to the Postfix list)
//   //     If c is an operator, pop everything of higher predence and add it to postfix list, then add c to list of Operators
//   //'''
//   postfix
// }

// fn evaluate_postfix(input: List<List<char>>) -> i32 {

//   //'''
//   // Convert list to number and apply operation on two at a time
//   //'''

// }

// fn test_arith_parse() {
//   println!("Enter expression in infix format");
//   let mut input = String::new();
//   io::stdin().read_line(&mut input).ok().expect("Failed to parse input");

//   // '''
//   // Convert string to List<char> (reverse order)
//   // '''
//   let input_list = panic!("");

//   let postfix_expr = infix_to_postfix(input_list);
//   let result = evaluate_postfix(postfix_expr);
// }

<commit_msg>Add invariant check back with support for ParanOpen and ParanClose Ops<commit_after>use std::fmt::Debug;
use std::hash::Hash;
use std::rc::Rc;

use collections::*;

#[derive(Clone,Copy,Hash,Eq,PartialEq,Debug)]
pub enum Op { Plus, Minus, Times, Divide, ParanOpen, ParanClose }

#[derive(Clone,Copy,Hash,Eq,PartialEq,Debug)]
pub enum Tok {
  Num(isize),
  Op(Op)
}

pub enum Precedence { Higher, Lower, Equal }

fn pop<X:Clone+Copy+Hash+Eq+PartialEq+Debug>
  (stack:List<X>) -> (X, List<X>) {
    List::elim_arg(stack, (),
                   |_,_|    panic!("cannot pop an empty stack"),
                   |h,t, _| (h, t),
                   |_,t, _| pop(t))
}

fn push<X:Clone+Copy+Hash+Eq+PartialEq+Debug>
  (stack:List<X>, elm:X) -> List<X>
{
  List::cons(elm, stack)
}

fn precedence_check (top_op:&Op, next_op:&Op) -> Precedence {
  match (*top_op, *next_op) {
    (Op::Plus, Op::Plus) => Precedence::Equal,
    (Op::Plus, Op::Minus) => Precedence::Equal,
    (Op::Minus, Op::Plus) => Precedence::Equal,
    (Op::Minus, Op::Minus) => Precedence::Equal,

    (Op::Times, Op::Times) => Precedence::Equal,
    (Op::Divide, Op::Divide) => Precedence::Equal,

    (Op::Times, Op::Plus)   => Precedence::Higher,
    (Op::Times, Op::Minus)  => Precedence::Higher,
    (Op::Times, Op::Divide) => Precedence::Higher,

    (Op::Divide, Op::Plus)  => Precedence::Higher,
    (Op::Divide, Op::Minus) => Precedence::Higher,
    (Op::Divide, Op::Times) => Precedence::Higher,
    (_, Op::ParanOpen) => Precedence::Higher,
    _ => Precedence::Lower
  }
}

pub fn check_ops_rec (ops:List<Op>, op:Op) -> bool {
  if List::is_empty(&ops) { true }
  else {
    let (top_op, popped_ops) = pop(ops);
    match op {
      Op::ParanOpen => {
	check_ops_rec(popped_ops, top_op)
        },
      _ => {match precedence_check(&op, &top_op) {
        Precedence::Higher => check_ops_rec(popped_ops, top_op),
        _ => false
      }}
    }
  }
}

pub fn check_ops (ops:List<Op>) -> bool {
  if List::is_empty(&ops) { true }
  else {
    let (top_op, popped_ops) = pop(ops);
    check_ops_rec(popped_ops, top_op)
  }
}

pub fn postfix_of_infix(infix: Tree<Tok>) -> List<Tok> {
  let (ops, postfix) : (List<Op>, List<Tok>) =
    tree_fold_seq
    (infix, Dir2::Left, (List::Nil, List::Nil),
     Rc::new(|tok, (ops, postfix) : (List<Op>, List<Tok>)| {
       assert!(check_ops(ops.clone()));
       match tok {
         Tok::Num(n) => (ops, push(postfix, Tok::Num(n))),
         Tok::Op(op) => {
           match op {
	    Op::ParanOpen => {(push(ops, op), postfix)}
            Op::ParanClose => {
              fn myloop (op:Op, ops:List<Op>, postfix:List<Tok>) -> (List<Op>, List<Tok>) {
                let (top_op, popped_ops) = pop(ops);
		match top_op {
		  Op::ParanOpen => {print!("hello");(popped_ops, postfix)},
                  _ => {myloop(op, popped_ops, push(postfix, Tok::Op(top_op)))},
                }
	      };
              myloop(op, ops, postfix)
	    }
	    _ => {
             if List::is_empty(&ops) { (push(ops, op), postfix) }
             else {
               fn myloop (op:Op, ops:List<Op>, postfix:List<Tok>) -> (List<Op>, List<Tok>) {
                 if List::is_empty(&ops) { (ops, postfix) } else {
                   let (top_op, popped_ops) = pop(ops);
                   match precedence_check (&top_op, &op) {
                     Precedence::Higher => { myloop (op, popped_ops, push(postfix, Tok::Op(top_op)))},
	             Precedence::Equal => {(popped_ops, push(postfix, Tok::Op(top_op)))},
                     _ => {(push(popped_ops, top_op), postfix)}
                   }
                 }};
               let (ops, postfix) = myloop(op, ops, postfix) ;
               (push(ops, op), postfix)
             }
           }}
         }
       }}),
     Rc::new(|_, a|   a),
     Rc::new(|_,_, a| a),
     );
  fn myloop (ops:List<Op>, postfix:List<Tok>) -> (List<Tok>) {
    if List::is_empty(&ops) { postfix } else {
      let (top_op, popped_ops) = pop(ops);
      myloop (popped_ops, push(postfix, Tok::Op(top_op)))
    }};
  myloop(ops, postfix)
}

#[test]
fn test_postfix_of_infix () {

  // 1 * 2 + 3
  let input = vec![
    NameElse::Else( Tok::Num(1) ),
    NameElse::Else( Tok::Op(Op::Times) ),
    NameElse::Else( Tok::Num(2) ),
    NameElse::Else( Tok::Op(Op::Plus) ),
    NameElse::Else( Tok::Num(3) ),
    ];
  let input : List<Tok> = list_of_vec(&input);
  let tree  : Tree<Tok> = tree_of_list(Dir2::Left, input);
  let ans = postfix_of_infix(tree);
  println!("{:?}", ans)
}

pub fn evaluate_postfix(input: Tree<Tok>) -> isize {
  let stack =
    tree_fold_seq
    (input, Dir2::Left, List::Nil,
     Rc::new(|tok, stack| {
       match tok {
         Tok::Op(op) => {
           match op {
             Op::ParanClose => {panic!()},
             Op::ParanOpen => {panic!()},
             _ => {
               let (x,stack) = pop(stack);
               let (y,stack) = pop(stack);
               let z = match op {
                 Op::Plus   => y + x,
                 Op::Minus  => y - x,
                 Op::Times  => y * x,
                 Op::Divide => y / x,
                 _ => panic!(),
               };
               push(stack, z)
            }
          }
         },
         Tok::Num(n) => {
           push(stack, n)
         }
       }}),
     Rc::new(|_, stack|   stack),
     Rc::new(|_,_, stack| stack),
     );
  let (x, stack) = pop(stack);
  assert!(List::is_empty(&stack));
  x
}

#[test]
fn test_eval_postfix () {
  let input = vec![
    NameElse::Else( Tok::Num(1) ),
    NameElse::Else( Tok::Num(2) ),
    NameElse::Else( Tok::Num(3) ),
    NameElse::Else( Tok::Op(Op::Plus) ),
    NameElse::Else( Tok::Op(Op::Plus) ),
    NameElse::Else( Tok::Num(22) ),
    NameElse::Else( Tok::Num(33) ),
    NameElse::Else( Tok::Op(Op::Plus) ),
    NameElse::Else( Tok::Op(Op::Plus) ),
    NameElse::Else( Tok::Num(12) ),
    NameElse::Else( Tok::Num(13) ),
    NameElse::Else( Tok::Op(Op::Plus) ),
    NameElse::Else( Tok::Op(Op::Plus) ),
    ];
  let input : List<Tok> = list_of_vec(&input);
  let tree  : Tree<Tok> = tree_of_list(Dir2::Left, input);
  let ans = evaluate_postfix(tree);
  assert_eq!(ans, 86)
}

#[test]
fn test_arith_eval () {

  // 1 * 2 + 3
  let input = vec![
    NameElse::Else( Tok::Num(1) ),
    NameElse::Else( Tok::Op(Op::Plus) ),
    NameElse::Else( Tok::Num(2) ),
    NameElse::Else( Tok::Op(Op::Minus) ),
    NameElse::Else( Tok::Num(3) ),
    NameElse::Else( Tok::Op(Op::Minus) ),
    NameElse::Else( Tok::Num(1) ),
    NameElse::Else( Tok::Op(Op::Minus) ),
    NameElse::Else( Tok::Num(2) ),
    NameElse::Else( Tok::Op(Op::Times) ),
    NameElse::Else( Tok::Num(2) ),
    // NameElse::Else( Tok::Op(Op::Divide) ),
    // NameElse::Else( Tok::Num(2) ),
    // NameElse::Else( Tok::Op(Op::Minus) ),
    // NameElse::Else( Tok::Num(3) ),
    ];
  println!("{:?}", &input);
  let input : List<Tok> = list_of_vec(&input);
  let tree  : Tree<Tok> = tree_of_list(Dir2::Left, input);
  let list  : List<Tok> = postfix_of_infix(tree);
  println!("{:?}", &list);
  //let input : List<Tok> = list_of_vec(&input);
  let tree  : Tree<Tok> = tree_of_list(Dir2::Right, list);
  let ans = evaluate_postfix(tree);
  println!("{:?}", ans)
}

#[test]
fn test_paran_eval () {

  // 1 * 2 + 3
  let input = vec![
    NameElse::Else( Tok::Num(2) ),
    NameElse::Else( Tok::Op(Op::Times) ),
    NameElse::Else( Tok::Op(Op::ParanOpen) ),
    //NameElse::Else( Tok::Num(1) ),
    //NameElse::Else( Tok::Op(Op::Plus) ),
    NameElse::Else( Tok::Num(2) ),
    NameElse::Else( Tok::Op(Op::Minus) ),
    NameElse::Else( Tok::Num(3) ),
    NameElse::Else( Tok::Op(Op::ParanClose) ),
    ];

    println!("{:?}", &input);
    let input : List<Tok> = list_of_vec(&input);
    let tree  : Tree<Tok> = tree_of_list(Dir2::Left, input);
    let list  : List<Tok> = postfix_of_infix(tree);
    println!("{:?}", &list);
    //let input : List<Tok> = list_of_vec(&input);
    let tree  : Tree<Tok> = tree_of_list(Dir2::Right, list);
    let ans = evaluate_postfix(tree);
    println!("{:?}", ans)
}

// fn infix_to_postfix(input: List<char>) -> List<List<char>> {
//   let mut postfix = List::Nil;
//   let mut operators = List::Nil;
//   let mut temp_operand = List::Nil;

//   //'''
//   // Match all chars in 'input'
//   //     If c is a digit, then push onto a Digit list temporarily until all digits in an operand are read (after which add the operand to the Postfix list)
//   //     If c is an operator, pop everything of higher predence and add it to postfix list, then add c to list of Operators
//   //'''
//   postfix
// }

// fn evaluate_postfix(input: List<List<char>>) -> i32 {

//   //'''
//   // Convert list to number and apply operation on two at a time
//   //'''

// }

// fn test_arith_parse() {
//   println!("Enter expression in infix format");
//   let mut input = String::new();
//   io::stdin().read_line(&mut input).ok().expect("Failed to parse input");

//   // '''
//   // Convert string to List<char> (reverse order)
//   // '''
//   let input_list = panic!("");

//   let postfix_expr = infix_to_postfix(input_list);
//   let result = evaluate_postfix(postfix_expr);
// }

<|endoftext|>


<commit_before>use std::mem::size_of_val;

// This function borrows a slice
fn analyze_slice(slice: &[int]) {
    println!("first element of the slice: {}", slice[0]);
    println!("the slice has {} elements", slice.len());
}

fn main() {
    // fixed-size array (type signature is superfluous)
    let xs: [int, ..5] = [1, 2, 3, 4, 5];

    // indexing starts at 0
    println!("first element of the array: {}", xs[0]);
    println!("second element of the array: {}", xs[1]);

    // len() returns the size of the array
    println!("array size: {}", xs.last());

    // arrays are stack allocated
    println!("array occupies {} bytes", size_of_val(&xs));

    // out of bound indexing yields a runtime failure
    //println!("{}", xs[5]);

    // arrays can be automatically converted to slices
    println!("borrow the whole array as an slice");
    analyze_slice(xs);

    // slices can point to a section of an array
    println!("borrow a section of the array as an slice");
    analyze_slice(xs.slice(1, 4));
}
<commit_msg>array: s/last/len. Closes #3<commit_after>use std::mem::size_of_val;

// This function borrows a slice
fn analyze_slice(slice: &[int]) {
    println!("first element of the slice: {}", slice[0]);
    println!("the slice has {} elements", slice.len());
}

fn main() {
    // fixed-size array (type signature is superfluous)
    let xs: [int, ..5] = [1, 2, 3, 4, 5];

    // indexing starts at 0
    println!("first element of the array: {}", xs[0]);
    println!("second element of the array: {}", xs[1]);

    // len() returns the size of the array
    println!("array size: {}", xs.len());

    // arrays are stack allocated
    println!("array occupies {} bytes", size_of_val(&xs));

    // out of bound indexing yields a runtime failure
    //println!("{}", xs[5]);

    // arrays can be automatically converted to slices
    println!("borrow the whole array as an slice");
    analyze_slice(xs);

    // slices can point to a section of an array
    println!("borrow a section of the array as an slice");
    analyze_slice(xs.slice(1, 4));
}
<|endoftext|>


<commit_before>// Copyright 2014-2016 bluss and ndarray developers.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use serde::{self, Serialize, Deserialize};

use std::marker::PhantomData;

use imp_prelude::*;

use super::arraytraits::ARRAY_FORMAT_VERSION;
use super::Iter;
use Dim;
use dimension::DimPrivate;

/// **Requires crate feature `"serde"`**
impl<I> Serialize for Dim<I>
    where I: Serialize,
{
    fn serialize<Se>(&self, serializer: &mut Se) -> Result<(), Se::Error>
        where Se: serde::Serializer
    {
        self.ix().serialize(serializer)
    }
}

/// **Requires crate feature `"serde"`**
impl<I> Deserialize for Dim<I>
    where I: Deserialize,
{
    fn deserialize<D>(deserializer: &mut D) -> Result<Self, D::Error>
        where D: serde::de::Deserializer
    {
        I::deserialize(deserializer).map(Dim::new)
    }
}

/// **Requires crate feature `"serde"`**
impl<A, D, S> Serialize for ArrayBase<S, D>
    where A: Serialize,
          D: Dimension + Serialize,
          S: Data<Elem = A>

{
    fn serialize<Se>(&self, serializer: &mut Se) -> Result<(), Se::Error>
        where Se: serde::Serializer
    {
        let mut struct_state = try!(serializer.serialize_struct("Array", 3));
        try!(serializer.serialize_struct_elt(&mut struct_state, "v", ARRAY_FORMAT_VERSION));
        try!(serializer.serialize_struct_elt(&mut struct_state, "dim", self.raw_dimension()));
        try!(serializer.serialize_struct_elt(&mut struct_state, "data", Sequence(self.iter())));
        serializer.serialize_struct_end(struct_state)
    }
}

// private iterator wrapper
struct Sequence<'a, A: 'a, D>(Iter<'a, A, D>);

impl<'a, A, D> Serialize for Sequence<'a, A, D>
    where A: Serialize,
          D: Dimension + Serialize
{
    fn serialize<S>(&self, serializer: &mut S) -> Result<(), S::Error>
        where S: serde::Serializer
    {
        let iter = &self.0;
        let mut seq_state = try!(serializer.serialize_seq(Some(iter.len())));
        for elt in iter.clone() {
            try!(serializer.serialize_seq_elt(&mut seq_state, elt));
        }
        serializer.serialize_seq_end(seq_state)
    }
}

struct ArrayVisitor<S, Di> {
    _marker_a: PhantomData<S>,
    _marker_b: PhantomData<Di>,
}

enum ArrayField {
    Version,
    Dim,
    Data,
}

impl<S, Di> ArrayVisitor<S, Di> {
    pub fn new() -> Self {
        ArrayVisitor { _marker_a: PhantomData, _marker_b: PhantomData, }
    }
}

/// **Requires crate feature `"serde"`**
impl<A, Di, S> Deserialize for ArrayBase<S, Di>
    where A: Deserialize,
          Di: Deserialize + Dimension,
          S: DataOwned<Elem = A>
{
    fn deserialize<D>(deserializer: &mut D) -> Result<ArrayBase<S, Di>, D::Error>
        where D: serde::de::Deserializer
    {
        static FIELDS: &'static [&'static str] = &["v", "dim", "data"];

        deserializer.deserialize_struct("Array", FIELDS, ArrayVisitor::new())
    }
}

impl serde::de::Deserialize for ArrayField {
    fn deserialize<D>(deserializer: &mut D) -> Result<ArrayField, D::Error>
        where D: serde::de::Deserializer
    {
        struct ArrayFieldVisitor;

        impl serde::de::Visitor for ArrayFieldVisitor {
            type Value = ArrayField;

            fn visit_str<E>(&mut self, value: &str) -> Result<ArrayField, E>
                where E: serde::de::Error
            {
                match value {
                    "v" => Ok(ArrayField::Version),
                    "data" => Ok(ArrayField::Data),
                    "dim" => Ok(ArrayField::Dim),
                    _ => Err(serde::de::Error::custom("expected v, data, or dim")),
                }
            }
        }

        deserializer.deserialize(ArrayFieldVisitor)
    }
}

impl<A, Di, S> serde::de::Visitor for ArrayVisitor<S,Di>
    where A: Deserialize,
          Di: Deserialize + Dimension,
          S: DataOwned<Elem = A>
{
    type Value = ArrayBase<S, Di>;

    fn visit_map<V>(&mut self, mut visitor: V) -> Result<ArrayBase<S, Di>, V::Error>
        where V: serde::de::MapVisitor,
    {
        let mut v: Option<u8> = None;
        let mut data: Option<Vec<A>> = None;
        let mut dim: Option<Di> = None;

        loop {
            match try!(visitor.visit_key()) {
                Some(ArrayField::Version) => { v = Some(try!(visitor.visit_value())); },
                Some(ArrayField::Data) => { data = Some(try!(visitor.visit_value())); },
                Some(ArrayField::Dim) => { dim = Some(try!(visitor.visit_value())); },
                None => { break; },
            }
        }

        let v = match v {
            Some(v) => v,
            None => try!(visitor.missing_field("v")),
        };

        if v != ARRAY_FORMAT_VERSION {
            try!(Err(serde::de::Error::custom(format!("unknown array version: {}", v))));
        }

        let data = match data {
            Some(data) => data,
            None => try!(visitor.missing_field("data")),
        };

        let dim = match dim {
            Some(dim) => dim,
            None => try!(visitor.missing_field("dim")),
        };

        if let Ok(array) = ArrayBase::from_shape_vec(dim, data) {
            try!(visitor.end());
            Ok(array)
        } else {
            Err(serde::de::Error::custom("data and dimension must match in size"))
        }
    }
}
<commit_msg>Make the serde deserialization visitor more idiomatic<commit_after>// Copyright 2014-2016 bluss and ndarray developers.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use serde::{self, Serialize, Deserialize};

use std::marker::PhantomData;

use imp_prelude::*;

use super::arraytraits::ARRAY_FORMAT_VERSION;
use super::Iter;
use Dim;
use dimension::DimPrivate;

/// **Requires crate feature `"serde"`**
impl<I> Serialize for Dim<I>
    where I: Serialize,
{
    fn serialize<Se>(&self, serializer: &mut Se) -> Result<(), Se::Error>
        where Se: serde::Serializer
    {
        self.ix().serialize(serializer)
    }
}

/// **Requires crate feature `"serde"`**
impl<I> Deserialize for Dim<I>
    where I: Deserialize,
{
    fn deserialize<D>(deserializer: &mut D) -> Result<Self, D::Error>
        where D: serde::de::Deserializer
    {
        I::deserialize(deserializer).map(Dim::new)
    }
}

/// **Requires crate feature `"serde"`**
impl<A, D, S> Serialize for ArrayBase<S, D>
    where A: Serialize,
          D: Dimension + Serialize,
          S: Data<Elem = A>

{
    fn serialize<Se>(&self, serializer: &mut Se) -> Result<(), Se::Error>
        where Se: serde::Serializer
    {
        let mut struct_state = try!(serializer.serialize_struct("Array", 3));
        try!(serializer.serialize_struct_elt(&mut struct_state, "v", ARRAY_FORMAT_VERSION));
        try!(serializer.serialize_struct_elt(&mut struct_state, "dim", self.raw_dimension()));
        try!(serializer.serialize_struct_elt(&mut struct_state, "data", Sequence(self.iter())));
        serializer.serialize_struct_end(struct_state)
    }
}

// private iterator wrapper
struct Sequence<'a, A: 'a, D>(Iter<'a, A, D>);

impl<'a, A, D> Serialize for Sequence<'a, A, D>
    where A: Serialize,
          D: Dimension + Serialize
{
    fn serialize<S>(&self, serializer: &mut S) -> Result<(), S::Error>
        where S: serde::Serializer
    {
        let iter = &self.0;
        let mut seq_state = try!(serializer.serialize_seq(Some(iter.len())));
        for elt in iter.clone() {
            try!(serializer.serialize_seq_elt(&mut seq_state, elt));
        }
        serializer.serialize_seq_end(seq_state)
    }
}

struct ArrayVisitor<S, Di> {
    _marker_a: PhantomData<S>,
    _marker_b: PhantomData<Di>,
}

enum ArrayField {
    Version,
    Dim,
    Data,
}

impl<S, Di> ArrayVisitor<S, Di> {
    pub fn new() -> Self {
        ArrayVisitor { _marker_a: PhantomData, _marker_b: PhantomData, }
    }
}

/// **Requires crate feature `"serde"`**
impl<A, Di, S> Deserialize for ArrayBase<S, Di>
    where A: Deserialize,
          Di: Deserialize + Dimension,
          S: DataOwned<Elem = A>
{
    fn deserialize<D>(deserializer: &mut D) -> Result<ArrayBase<S, Di>, D::Error>
        where D: serde::de::Deserializer
    {
        static FIELDS: &'static [&'static str] = &["v", "dim", "data"];

        deserializer.deserialize_struct("Array", FIELDS, ArrayVisitor::new())
    }
}

impl serde::de::Deserialize for ArrayField {
    fn deserialize<D>(deserializer: &mut D) -> Result<ArrayField, D::Error>
        where D: serde::de::Deserializer
    {
        struct ArrayFieldVisitor;

        impl serde::de::Visitor for ArrayFieldVisitor {
            type Value = ArrayField;

            fn visit_str<E>(&mut self, value: &str) -> Result<ArrayField, E>
                where E: serde::de::Error
            {
                match value {
                    "v" => Ok(ArrayField::Version),
                    "data" => Ok(ArrayField::Data),
                    "dim" => Ok(ArrayField::Dim),
                    _ => Err(serde::de::Error::custom("expected v, data, or dim")),
                }
            }
        }

        deserializer.deserialize(ArrayFieldVisitor)
    }
}

impl<A, Di, S> serde::de::Visitor for ArrayVisitor<S,Di>
    where A: Deserialize,
          Di: Deserialize + Dimension,
          S: DataOwned<Elem = A>
{
    type Value = ArrayBase<S, Di>;

    fn visit_map<V>(&mut self, mut visitor: V) -> Result<ArrayBase<S, Di>, V::Error>
        where V: serde::de::MapVisitor,
    {
        let mut v: Option<u8> = None;
        let mut data: Option<Vec<A>> = None;
        let mut dim: Option<Di> = None;

        while let Some(key) = try!(visitor.visit_key::<ArrayField>()) {
            match key {
                ArrayField::Version => { v = Some(try!(visitor.visit_value())); },
                ArrayField::Data => { data = Some(try!(visitor.visit_value())); },
                ArrayField::Dim => { dim = Some(try!(visitor.visit_value())); },
            }
        }
        try!(visitor.end());

        let v = match v {
            Some(v) => v,
            None => try!(visitor.missing_field("v")),
        };

        if v != ARRAY_FORMAT_VERSION {
            try!(Err(serde::de::Error::custom(format!("unknown array version: {}", v))));
        }

        let data = match data {
            Some(data) => data,
            None => try!(visitor.missing_field("data")),
        };

        let dim = match dim {
            Some(dim) => dim,
            None => try!(visitor.missing_field("dim")),
        };

        if let Ok(array) = ArrayBase::from_shape_vec(dim, data) {
            Ok(array)
        } else {
            Err(serde::de::Error::custom("data and dimension must match in size"))
        }
    }
}
<|endoftext|>


<commit_before>#[cfg(feature = "getopts")] extern crate getopts;
#[cfg(feature = "getopts")] extern crate markov;

#[cfg(feature = "getopts")] use std::env::args;
#[cfg(feature = "getopts")] use std::path::Path;
#[cfg(feature = "getopts")] use getopts::Options;
#[cfg(feature = "getopts")] use markov::Chain;

#[cfg(all(feature = "getopts", not(test)))]
fn main() {
    markov_gen(args().collect()).iter().map(|s| println!("{}", s)).count();
}

#[cfg(all(not(feature = "getopts"), not(test)))]
fn main() {
    println!("markgen must be compiled with getopts enabled.")
}

/// Generates a number of strings using a markov chain on specified inputs. This is designed
/// primarily for command-line usage. The arguments are expected to be paths to files to be fed
/// into the chain. Additionally, the argument `-n #` is supported to specify the number of phrases
/// to be generated. This number must be a positive, non-zero integer. `-o #` is also supported to
/// specify the order of Markov chain to be used. Note `-o` must be specified before any file
/// names and must also be a positive, non-zero integer.
///
/// Some valid usages of this function:
/// `markov_gen(vec!["test".to_owned()])`
/// `markov_gen(vec!["test".to_owned(), "-n".to_owned(), "3".to_owned()])`
/// `markov_gen(vec!["-n".to_owned(), "3".to_owned(), "test".to_owned()])`
/// `markov_gen(vec!["-o".to_owned(), "2".to_owned(), "test".to_owned()])`
///
/// Some invalid usages of this function:
/// `markov_gen(vec!["-n".to_owned(), "3".to_owned()])`
/// `markov_gen(vec!["test".to_owned(), "-n".to_owned(), "0".to_owned()])`
/// `markov_gen(vec!["test".to_owned(), "-n".to_owned(), "test".to_owned()])`
/// `markov_gen(vec!["test".to_owned(), "-o".to_owned(), "3".to_owned()])`
/// `markov_gen(vec!["-o".to_owned(), "0".to_owned()], "test".to_owned())`
#[cfg(feature = "getopts")]
fn markov_gen(args: Vec<String>) -> Vec<String>{
    let mut opts = Options::new();
    opts.optopt("n", "count", "set the number of phrases to generate", "COUNT");
    opts.optopt("o", "order", "set the order of the Markov chain", "ORDER");
    opts.optflag("h", "help", "print this help menu");
    let matches = match opts.parse(&args[1..]) {
        Ok(m) => m,
        Err(e) => panic!(e.to_string()),
    };
    if matches.opt_present("h") {
        let brief = format!("Usage: {} FILE1 [FILE2 FILE3 ...] [options]", args[0]);
        print!("{}", opts.usage(&brief));
        Vec::new()
    } else {
        let mut chain: Chain<String> = Chain::new();
        let count = match matches.opt_str("n") {
            Some(arg) => match arg.parse() {
                Ok(n) if n > 0 => n,
                _ => panic!("Expected positive integer argument to -n, found {}.", &arg),
            },
            None => 1
        };
        if let Some(arg) = matches.opt_str("o") {
            match arg.parse() {
                Ok(n) if n > 0 => { chain.order(n); },
                _ => panic!("Expected positive integer argument to -n, found {}.", &arg),
            }
        }
        for path in matches.free.iter() {
            chain.feed_file(Path::new(&path));
        }
        if chain.is_empty() { panic!("No files were fed into the chain.") }
        chain.str_iter_for(count).collect()
    }
}

#[cfg(all(feature = "getopts", test))]
mod test {
    use super::markov_gen;
    use std::borrow::ToOwned;

    #[test]
    fn gen_default() {
        assert_eq!(markov_gen(vec!["prog".to_owned(), "test".to_owned()]).len(), 1)
    }

    #[test]
    fn gen_number_after() {
        assert_eq!(markov_gen(
            vec!["markgen".to_owned(), "test".to_owned(), "-n".to_owned(), "3".to_owned()]
        ).len(), 3)
    }

    #[test]
    fn gen_before_number() {
        assert_eq!(markov_gen(
            vec!["markgen".to_owned(), "-n".to_owned(), "3".to_owned(), "test".to_owned()]
        ).len(), 3)
    }

    #[test]
    #[should_panic(message = "No files were fed into the chain.")]
    fn gen_invalid_no_files() {
        markov_gen(vec!["prog".to_owned(), "-n".to_owned(), "3".to_owned()]);
    }

    #[test]
    #[should_panic(message = "Expected positive integer argument to -n, found 0.")]
    fn gen_invalid_n_arg_zero() {
        markov_gen(vec!["prog".to_owned(), "test".to_owned(), "-n".to_owned(), "0".to_owned()]);
    }

    #[test]
    #[should_panic(message = "Expected positive integer argument to -n, found test.")]
    fn gen_invalid_n_arg_string() {
        markov_gen(vec!["prog".to_owned(), "test".to_owned(), "-n".to_owned(), "test".to_owned()]);
    }
}
<commit_msg>update should_panic attribute<commit_after>#[cfg(feature = "getopts")] extern crate getopts;
#[cfg(feature = "getopts")] extern crate markov;

#[cfg(feature = "getopts")] use std::env::args;
#[cfg(feature = "getopts")] use std::path::Path;
#[cfg(feature = "getopts")] use getopts::Options;
#[cfg(feature = "getopts")] use markov::Chain;

#[cfg(all(feature = "getopts", not(test)))]
fn main() {
    markov_gen(args().collect()).iter().map(|s| println!("{}", s)).count();
}

#[cfg(all(not(feature = "getopts"), not(test)))]
fn main() {
    println!("markgen must be compiled with getopts enabled.")
}

/// Generates a number of strings using a markov chain on specified inputs. This is designed
/// primarily for command-line usage. The arguments are expected to be paths to files to be fed
/// into the chain. Additionally, the argument `-n #` is supported to specify the number of phrases
/// to be generated. This number must be a positive, non-zero integer. `-o #` is also supported to
/// specify the order of Markov chain to be used. Note `-o` must be specified before any file
/// names and must also be a positive, non-zero integer.
///
/// Some valid usages of this function:
/// `markov_gen(vec!["test".to_owned()])`
/// `markov_gen(vec!["test".to_owned(), "-n".to_owned(), "3".to_owned()])`
/// `markov_gen(vec!["-n".to_owned(), "3".to_owned(), "test".to_owned()])`
/// `markov_gen(vec!["-o".to_owned(), "2".to_owned(), "test".to_owned()])`
///
/// Some invalid usages of this function:
/// `markov_gen(vec!["-n".to_owned(), "3".to_owned()])`
/// `markov_gen(vec!["test".to_owned(), "-n".to_owned(), "0".to_owned()])`
/// `markov_gen(vec!["test".to_owned(), "-n".to_owned(), "test".to_owned()])`
/// `markov_gen(vec!["test".to_owned(), "-o".to_owned(), "3".to_owned()])`
/// `markov_gen(vec!["-o".to_owned(), "0".to_owned()], "test".to_owned())`
#[cfg(feature = "getopts")]
fn markov_gen(args: Vec<String>) -> Vec<String>{
    let mut opts = Options::new();
    opts.optopt("n", "count", "set the number of phrases to generate", "COUNT");
    opts.optopt("o", "order", "set the order of the Markov chain", "ORDER");
    opts.optflag("h", "help", "print this help menu");
    let matches = match opts.parse(&args[1..]) {
        Ok(m) => m,
        Err(e) => panic!(e.to_string()),
    };
    if matches.opt_present("h") {
        let brief = format!("Usage: {} FILE1 [FILE2 FILE3 ...] [options]", args[0]);
        print!("{}", opts.usage(&brief));
        Vec::new()
    } else {
        let mut chain: Chain<String> = Chain::new();
        let count = match matches.opt_str("n") {
            Some(arg) => match arg.parse() {
                Ok(n) if n > 0 => n,
                _ => panic!("Expected positive integer argument to -n, found {}.", &arg),
            },
            None => 1
        };
        if let Some(arg) = matches.opt_str("o") {
            match arg.parse() {
                Ok(n) if n > 0 => { chain.order(n); },
                _ => panic!("Expected positive integer argument to -n, found {}.", &arg),
            }
        }
        for path in matches.free.iter() {
            chain.feed_file(Path::new(&path));
        }
        if chain.is_empty() { panic!("No files were fed into the chain.") }
        chain.str_iter_for(count).collect()
    }
}

#[cfg(all(feature = "getopts", test))]
mod test {
    use super::markov_gen;
    use std::borrow::ToOwned;

    #[test]
    fn gen_default() {
        assert_eq!(markov_gen(vec!["prog".to_owned(), "test".to_owned()]).len(), 1)
    }

    #[test]
    fn gen_number_after() {
        assert_eq!(markov_gen(
            vec!["markgen".to_owned(), "test".to_owned(), "-n".to_owned(), "3".to_owned()]
        ).len(), 3)
    }

    #[test]
    fn gen_before_number() {
        assert_eq!(markov_gen(
            vec!["markgen".to_owned(), "-n".to_owned(), "3".to_owned(), "test".to_owned()]
        ).len(), 3)
    }

    #[test]
    #[should_panic(expected = "No files were fed into the chain.")]
    fn gen_invalid_no_files() {
        markov_gen(vec!["prog".to_owned(), "-n".to_owned(), "3".to_owned()]);
    }

    #[test]
    #[should_panic(expected = "Expected positive integer argument to -n, found 0.")]
    fn gen_invalid_n_arg_zero() {
        markov_gen(vec!["prog".to_owned(), "test".to_owned(), "-n".to_owned(), "0".to_owned()]);
    }

    #[test]
    #[should_panic(expected = "Expected positive integer argument to -n, found test.")]
    fn gen_invalid_n_arg_string() {
        markov_gen(vec!["prog".to_owned(), "test".to_owned(), "-n".to_owned(), "test".to_owned()]);
    }
}
<|endoftext|>


<commit_before>//! Checks for any invariants we expect to be true, and pages whoever is on call
//! if they are not.
//!
//! Usage:
//!     cargo run --bin monitor

#![warn(clippy::all, rust_2018_idioms)]

mod on_call;

use cargo_registry::{db, schema::*, util::Error};
use diesel::prelude::*;

fn main() -> Result<(), Error> {
    let conn = db::connect_now()?;

    check_stalled_background_jobs(&conn)?;
    check_spam_attack(&conn)?;
    Ok(())
}

fn check_stalled_background_jobs(conn: &PgConnection) -> Result<(), Error> {
    use cargo_registry::schema::background_jobs::dsl::*;
    use diesel::dsl::*;

    const EVENT_KEY: &str = "background_jobs";

    println!("Checking for stalled background jobs");

    let max_job_time = dotenv::var("MAX_JOB_TIME")
        .map(|s| s.parse::<i32>().unwrap())
        .unwrap_or(15);

    let stalled_job_count = background_jobs
        .filter(created_at.lt(now - max_job_time.minutes()))
        .count()
        .get_result::<i64>(conn)?;

    let event = if stalled_job_count > 0 {
        on_call::Event::Trigger {
            incident_key: Some(EVENT_KEY.into()),
            description: format!(
                "{} jobs have been in the queue for more than {} minutes",
                stalled_job_count, max_job_time
            ),
        }
    } else {
        on_call::Event::Resolve {
            incident_key: EVENT_KEY.into(),
            description: Some("No stalled background jobs".into()),
        }
    };

    log_and_trigger_event(event)?;
    Ok(())
}

fn check_spam_attack(conn: &PgConnection) -> Result<(), Error> {
    use cargo_registry::models::krate::canon_crate_name;
    use diesel::dsl::*;
    use diesel::sql_types::Bool;

    const EVENT_KEY: &str = "spam_attack";

    println!("Checking for crates indicating someone is spamming us");

    let bad_crate_names = dotenv::var("SPAM_CRATE_NAMES");
    let bad_crate_names: Vec<_> = bad_crate_names
        .as_ref()
        .map(|s| s.split(',').collect())
        .unwrap_or_default();
    let bad_author_patterns = dotenv::var("SPAM_AUTHOR_PATTERNS");
    let bad_author_patterns: Vec<_> = bad_author_patterns
        .as_ref()
        .map(|s| s.split(',').collect())
        .unwrap_or_default();

    let mut event_description = None;

    let bad_crate = crates::table
        .filter(canon_crate_name(crates::name).eq(any(bad_crate_names)))
        .select(crates::name)
        .first::<String>(conn)
        .optional()?;

    if let Some(bad_crate) = bad_crate {
        event_description = Some(format!("Crate named {} published", bad_crate));
    }

    let mut query = version_authors::table
        .select(version_authors::name)
        .filter(false.into_sql::<Bool>()) // Never return anything if we have no patterns
        .into_boxed();
    for author_pattern in bad_author_patterns {
        query = query.or_filter(version_authors::name.like(author_pattern));
    }
    let bad_author = query.first::<String>(conn).optional()?;

    if let Some(bad_author) = bad_author {
        event_description = Some(format!("Crate with author {} published", bad_author));
    }

    let event = if let Some(event_description) = event_description {
        on_call::Event::Trigger {
            incident_key: Some(EVENT_KEY.into()),
            description: format!("{}, possible spam attack underway", event_description,),
        }
    } else {
        on_call::Event::Resolve {
            incident_key: EVENT_KEY.into(),
            description: Some("No spam crates detected".into()),
        }
    };

    log_and_trigger_event(event)?;
    Ok(())
}

fn log_and_trigger_event(event: on_call::Event) -> Result<(), Error> {
    match event {
        on_call::Event::Trigger {
            ref description, ..
        } => println!("Paging on-call: {}", description),
        on_call::Event::Resolve {
            description: Some(ref description),
            ..
        } => println!("{}", description),
        _ => {} // noop
    }
    event.send()
}
<commit_msg>Don't page for long running jobs<commit_after>//! Checks for any invariants we expect to be true, and pages whoever is on call
//! if they are not.
//!
//! Usage:
//!     cargo run --bin monitor

#![warn(clippy::all, rust_2018_idioms)]

mod on_call;

use cargo_registry::{db, schema::*, util::Error};
use diesel::prelude::*;

fn main() -> Result<(), Error> {
    let conn = db::connect_now()?;

    check_stalled_background_jobs(&conn)?;
    check_spam_attack(&conn)?;
    Ok(())
}

fn check_stalled_background_jobs(conn: &PgConnection) -> Result<(), Error> {
    use cargo_registry::schema::background_jobs::dsl::*;
    use diesel::dsl::*;
    use diesel::sql_types::Integer;

    const EVENT_KEY: &str = "background_jobs";

    println!("Checking for stalled background jobs");

    let max_job_time = dotenv::var("MAX_JOB_TIME")
        .map(|s| s.parse::<i32>().unwrap())
        .unwrap_or(15);

    let stalled_job_count = background_jobs
        .select(1.into_sql::<Integer>())
        .filter(created_at.lt(now - max_job_time.minutes()))
        .for_update()
        .skip_locked()
        .load::<i32>(conn)?
        .len();

    let event = if stalled_job_count > 0 {
        on_call::Event::Trigger {
            incident_key: Some(EVENT_KEY.into()),
            description: format!(
                "{} jobs have been in the queue for more than {} minutes",
                stalled_job_count, max_job_time
            ),
        }
    } else {
        on_call::Event::Resolve {
            incident_key: EVENT_KEY.into(),
            description: Some("No stalled background jobs".into()),
        }
    };

    log_and_trigger_event(event)?;
    Ok(())
}

fn check_spam_attack(conn: &PgConnection) -> Result<(), Error> {
    use cargo_registry::models::krate::canon_crate_name;
    use diesel::dsl::*;
    use diesel::sql_types::Bool;

    const EVENT_KEY: &str = "spam_attack";

    println!("Checking for crates indicating someone is spamming us");

    let bad_crate_names = dotenv::var("SPAM_CRATE_NAMES");
    let bad_crate_names: Vec<_> = bad_crate_names
        .as_ref()
        .map(|s| s.split(',').collect())
        .unwrap_or_default();
    let bad_author_patterns = dotenv::var("SPAM_AUTHOR_PATTERNS");
    let bad_author_patterns: Vec<_> = bad_author_patterns
        .as_ref()
        .map(|s| s.split(',').collect())
        .unwrap_or_default();

    let mut event_description = None;

    let bad_crate = crates::table
        .filter(canon_crate_name(crates::name).eq(any(bad_crate_names)))
        .select(crates::name)
        .first::<String>(conn)
        .optional()?;

    if let Some(bad_crate) = bad_crate {
        event_description = Some(format!("Crate named {} published", bad_crate));
    }

    let mut query = version_authors::table
        .select(version_authors::name)
        .filter(false.into_sql::<Bool>()) // Never return anything if we have no patterns
        .into_boxed();
    for author_pattern in bad_author_patterns {
        query = query.or_filter(version_authors::name.like(author_pattern));
    }
    let bad_author = query.first::<String>(conn).optional()?;

    if let Some(bad_author) = bad_author {
        event_description = Some(format!("Crate with author {} published", bad_author));
    }

    let event = if let Some(event_description) = event_description {
        on_call::Event::Trigger {
            incident_key: Some(EVENT_KEY.into()),
            description: format!("{}, possible spam attack underway", event_description,),
        }
    } else {
        on_call::Event::Resolve {
            incident_key: EVENT_KEY.into(),
            description: Some("No spam crates detected".into()),
        }
    };

    log_and_trigger_event(event)?;
    Ok(())
}

fn log_and_trigger_event(event: on_call::Event) -> Result<(), Error> {
    match event {
        on_call::Event::Trigger {
            ref description, ..
        } => println!("Paging on-call: {}", description),
        on_call::Event::Resolve {
            description: Some(ref description),
            ..
        } => println!("{}", description),
        _ => {} // noop
    }
    event.send()
}
<|endoftext|>