<commit_before>use httparse_orig;
use std::str;

pub enum ParseStatus<T> {
    Complete(T),
    InProgress,
    Err(httparse_orig::Error),
}

impl<T> ParseStatus<T> {
    pub fn is_complete(&self) -> bool {
        match *self {
            ParseStatus::Complete(_) => true,
            _ => false,
        }
    }
}

pub trait Parsable {
    fn new() -> Self;
    fn parse(&mut self, buf: &[u8]) -> ParseStatus<usize>;
}

pub trait Sendable {
    fn to_bytes(&self) -> Vec<u8>;
}

pub fn parse_body(headers: &[httparse_orig::Header], buf: &[u8]) -> ParseStatus<Option<Vec<u8>>> {
    match headers.iter().find(|&&h| h.name == "Content-Length") {
        Some(h) => {
            let len_str = str::from_utf8(h.value).unwrap();
            let len: usize = str::FromStr::from_str(len_str).unwrap();

            if len <= buf.len() {
                return ParseStatus::Complete(Some(Vec::from(buf)));
            } else {
                return ParseStatus::InProgress;
            }
        }
        None => return ParseStatus::Complete(None),
    }
}
<commit_msg>add parseing chunked data<commit_after>use httparse_orig;
use std::str;

pub enum ParseStatus<T> {
    Complete(T),
    InProgress,
    Err(httparse_orig::Error),
}

impl<T> ParseStatus<T> {
    pub fn is_complete(&self) -> bool {
        match *self {
            ParseStatus::Complete(_) => true,
            _ => false,
        }
    }
}

pub trait Parsable {
    fn new() -> Self;
    fn parse(&mut self, buf: &[u8]) -> ParseStatus<usize>;
}

pub trait Sendable {
    fn to_bytes(&self) -> Vec<u8>;
}

pub fn parse_body(headers: &[httparse_orig::Header], buf: &[u8]) -> ParseStatus<Option<Vec<u8>>> {
    match headers.iter().find(|&&h| h.name == "Content-Length") {
        Some(h) => {
            let len_str = str::from_utf8(h.value).unwrap();
            let len: usize = str::FromStr::from_str(len_str).unwrap();

            if len <= buf.len() {
                return ParseStatus::Complete(Some(Vec::from(buf)));
            } else {
                return ParseStatus::InProgress;
            }
        }
        None => {
            match headers.iter().find(|&&h| h.name == "Trunsfer Encoding") {
                Some(h) => {
                    if h.value == b"chunked" {
                        return parse_chunked(buf);
                    }
                }
                None => {}
            };
        }
    }
    ParseStatus::Complete(None)
}

fn parse_chunked(buf: &[u8]) -> ParseStatus<Option<Vec<u8>>> {
    let mut vec: Vec<u8> = Vec::new();
    let mut cur_num = Vec::new();
    let mut index = 3;
    loop {
        if buf.len() < index {
            return ParseStatus::InProgress;
        }

        cur_num.push(buf[index - 2]);

        if buf[index - 1] == b'\r' && buf[index] == b'\n' {
            let num = str::FromStr::from_str(String::from_utf8(cur_num).unwrap().as_str()).unwrap();
            cur_num = Vec::new();
            if num == 0 {
                return ParseStatus::Complete(Some(Vec::from(buf)));
            }

            index += num;
        } else {
            index += 1;
        }
    }
}

#[cfg(test)]
mod tests {
    use super::{parse_chunked, ParseStatus};

    #[test]
    fn chunked_test() {
        let src = b"10\r\nabcdefghij\r\n0";
        assert!(match parse_chunked(src as &[u8]) {
            ParseStatus::Complete(_) => true,
            _ => false,
        });
    }
}
<|endoftext|>


<commit_before>#![feature(associated_consts)]
#![feature(type_ascription)]
#![feature(use_extern_macros)]
#![allow(plugin_as_library)]

#[macro_use]
extern crate tapioca;

infer_api!(httpbin, "https://raw.githubusercontent.com/OJFord/tapioca/master/tests/schemata/httpbin.yml");

use httpbin::status;

#[test]
fn ok_err_matching() {
    match status::get(200) {
        Ok(_) => assert!(true),
        Err(_) => assert!(false),
    }
    match status::get(400) {
        Ok(_) => assert!(false),
        Err(_) => assert!(true),
    }
}

#[test]
fn status_body_matching() {
    match status::get(200) {
        Ok(response) => match response.body() {
            status::get::OkBody::Status200(_) => assert!(true),
            _ => assert!(false),
        },
        Err(_) => assert!(false),
    }
}
<commit_msg>Fix module name for path parameter<commit_after>#![feature(associated_consts)]
#![feature(type_ascription)]
#![feature(use_extern_macros)]
#![allow(plugin_as_library)]

#[macro_use]
extern crate tapioca;

infer_api!(httpbin, "https://raw.githubusercontent.com/OJFord/tapioca/master/tests/schemata/httpbin.yml");

use httpbin::status_resource;

#[test]
fn ok_err_matching() {
    match status_resource::get(200) {
        Ok(_) => assert!(true),
        Err(_) => assert!(false),
    }
    match status_resource::get(400) {
        Ok(_) => assert!(false),
        Err(_) => assert!(true),
    }
}

#[test]
fn status_body_matching() {
    match status_resource::get(200) {
        Ok(response) => match response.body() {
            status_resource::get::OkBody::Status200(_) => assert!(true),
            _ => assert!(false),
        },
        Err(_) => assert!(false),
    }
}
<|endoftext|>


<commit_before>use std::collections::{HashSet, HashMap};
use std::io::{BufReader, BufRead};
use std::str::FromStr;
use std::fs::File;
use std::cmp;
use rand::Rng;
use tar::Archive;
use spiral::Spiral;
use dfa::{Dfa, DfaState};
use player::Player;
use cell::Cell;
use field::{Pos, Field};

#[derive(Clone, Debug)]
struct Move {
  x: u32,
  y: u32,
  p: f64 // probability
}

struct Pattern {
  p: f64, // priority
  width: u32,
  height: u32,
  moves: Vec<Move>
}

pub struct Patterns {
  min_size: u32,
  dfa: Dfa,
  patterns: Vec<Pattern>
}

impl Patterns {
  pub fn empty() -> Patterns {
    Patterns {
      min_size: u32::max_value(),
      dfa: Dfa::empty(),
      patterns: Vec::with_capacity(0)
    }
  }

  fn read_header<T: BufRead>(input: &mut T, s: &mut String) -> (u32, u32, u32, f64) {
    s.clear();
    input.read_line(s).ok();
    s.pop();
    let mut split = s.split(' ').fuse();
    let width = u32::from_str(split.next().expect("Invalid pattern format: expected width.")).expect("Invalid pattern format: width must be u32."); //TODO: validate all this.
    let height = u32::from_str(split.next().expect("Invalid pattern format: expected height.")).expect("Invalid pattern format: height must be u32.");
    let moves_count = u32::from_str(split.next().expect("Invalid pattern format: expected moves count.")).expect("Invalid pattern format: moves count must be u32.");
    let priority = f64::from_str(split.next().expect("Invalid pattern format: expected priority.")).expect("Invalid pattern format: priority must be f64.");
    (width, height, moves_count, priority)
  }

  fn read_pattern<T: BufRead>(input: &mut T, s: &mut String, width: u32, height: u32) {
    s.clear();
    for _ in 0 .. height {
      let last_len = s.len();
      input.read_line(s).ok();
      s.pop();
      assert_eq!((s.len() - last_len) as u32, width)
    }
  }

  fn read_moves<T: BufRead>(input: &mut T, s: &mut String, moves_count: u32) -> Vec<Move> {
    let mut moves = Vec::with_capacity(moves_count as usize);
    let mut priorities_sum = 0f64;
    for _ in 0 .. moves_count {
      s.clear();
      input.read_line(s).ok();
      s.pop();
      let mut split = s.split(' ').fuse();
      let x = u32::from_str(split.next().expect("Invalid pattern format: expected x coordinate.")).expect("Invalid pattern format: x coordinate must be u32.");
      let y = u32::from_str(split.next().expect("Invalid pattern format: expected x coordinate.")).expect("Invalid pattern format: x coordinate must be u32.");
      let p = f64::from_str(split.next().expect("Invalid pattern format: expected probability.")).expect("Invalid pattern format: probability must be f64.");
      priorities_sum += p;
      moves.push(Move {
        x: x,
        y: y,
        p: p
      });
    }
    for m in &mut moves {
      m.p /= priorities_sum;
    }
    moves
  }

  fn covering_spiral_length(side_of_square: u32) -> u32 {
    let x = side_of_square / 2 + 1;
    let y = (1 - side_of_square % 2) * side_of_square * 2;
    (8 * x - 13) * x + 6 - y
  }

  fn rotate(width: u32, height: u32, x: i32, y: i32, rotation: u32) -> (i32, i32) {
    match rotation {
      0 => (x, y),
      1 => (width as i32 - x - 1, y),
      2 => (x, height as i32 - y - 1),
      3 => (width as i32 - x - 1, height as i32 - y - 1),
      4 => (y, x),
      5 => (height as i32 - y - 1, x),
      6 => (y, width as i32 - x - 1),
      7 => (height as i32 - y - 1, width as i32 - x - 1),
      r => panic!("Invalid rotation number: {}", r)
    }
  }

  fn build_dfa(name: &str, width: u32, height: u32, pattern: usize, rotation: u32, s: &str) -> Dfa {
    let center_x = (width - 1) / 2;
    let center_y = (height - 1) / 2;
    let spiral_length = Patterns::covering_spiral_length(cmp::max(width, height)) as usize;
    let mut states = Vec::with_capacity(spiral_length + 2);
    let fs = spiral_length; // "Found" state.
    let nfs = spiral_length + 1; // "Not found" state.
    for (i, (shift_x, shift_y)) in Spiral::new().into_iter().take(spiral_length).enumerate() {
      let nxt = i + 1;
      let (x, y) = Patterns::rotate(width, height, center_x as i32 + shift_x, center_y as i32 + shift_y, rotation);
      let state = if x >= 0 && x < width as i32 && y >= 0 && y < height as i32 {
        let pos = y as u32 * width + x as u32;
        match s.char_at(pos as usize) {
          '.' => DfaState::new(nxt, nfs, nfs, nfs, false, HashSet::with_capacity(0)),
          '?' => DfaState::new(nxt, nxt, nxt, nfs, false, HashSet::with_capacity(0)),
          '*' => DfaState::new(nxt, nxt, nxt, nxt, false, HashSet::with_capacity(0)),
          'R' => DfaState::new(nfs, nxt, nfs, nfs, false, HashSet::with_capacity(0)),
          'B' => DfaState::new(nfs, nfs, nxt, nfs, false, HashSet::with_capacity(0)),
          'r' => DfaState::new(nxt, nxt, nfs, nfs, false, HashSet::with_capacity(0)),
          'b' => DfaState::new(nxt, nfs, nxt, nfs, false, HashSet::with_capacity(0)),
          '#' => DfaState::new(nfs, nfs, nfs, nxt, false, HashSet::with_capacity(0)),
          c   => panic!("Invalid character in the pattern '{}': {}", name, c)
        }
      } else {
        DfaState::new(nxt, nxt, nxt, nxt, false, HashSet::with_capacity(0))
      };
      states.push(state);
    }
    let mut patterns = HashSet::with_capacity(1);
    patterns.insert(pattern);
    states.push(DfaState::new(fs, fs, fs, fs, true, patterns));
    states.push(DfaState::new(nfs, nfs, nfs, nfs, true, HashSet::with_capacity(0)));
    Dfa::new(states)
  }

  pub fn load(file: File) -> Patterns {
    let archive = Archive::new(file);
    let mut s = String::new();
    let mut pattern_s = String::new();
    let mut patterns = Vec::new();
    let iter = archive.files().expect("Reading of tar archive is failed.").into_iter().map(|file| file.expect("Reading of file in tar archive is failed."));
    let mut dfa = Dfa::empty();
    let mut min_size = u32::max_value();
    for file in iter {
      let name = file.header().path().ok()
        .and_then(|path| path.to_str().map(|s| s.to_owned()))
        .unwrap_or_else(|| "<unknown>".to_owned());
      let mut input = BufReader::new(file);
      let (width, height, moves_count, priority) = Patterns::read_header(&mut input, &mut s);
      if width < min_size {
        min_size = width;
      }
      if height < min_size {
        min_size = height;
      }
      Patterns::read_pattern(&mut input, &mut pattern_s, width, height);
      let moves = Patterns::read_moves(&mut input, &mut s, moves_count);
      for i in 0 .. 8 {
        let cur_dfa = Patterns::build_dfa(name.as_str(), width, height, patterns.len(), i, &pattern_s);
        dfa = dfa.product(&cur_dfa);
        patterns.push(Pattern {
          p: priority,
          width: width,
          height: height,
          moves: moves.iter().map(|m| {
            let (x, y) = Patterns::rotate(width, height, m.x as i32, m.y as i32, i);
            Move {
              x: x as u32,
              y: y as u32,
              p: m.p
            }
          }).collect()
        });
      }
    }
    Patterns {
      min_size: min_size,
      dfa: dfa,
      patterns: patterns
    }
  }

  pub fn find(&self, field: &Field, player: Player, first_match: bool) -> Vec<(Pos, f64)> {
    if self.dfa.is_empty() {
      return Vec::with_capacity(0);
    }
    let mut priorities_sum = 0f64;
    let mut moves_count = 0usize;
    let mut matched = Vec::new();
    let left_border = (self.min_size - 1) / 2;
    let right_border = self.min_size / 2;
    let inv_color = player == Player::Black;
    for y in left_border .. field.height() - right_border {
      for x in left_border .. field.width() - right_border {
        let patterns = self.dfa.run(&mut Spiral::new().into_iter().map(|(shift_x, shift_y)| {
          let cur_x = x as i32 + shift_x;
          let cur_y = y as i32 + shift_y;
          if cur_x >= 0 && cur_x < field.width() as i32 && cur_y >= 0 && cur_y < field.height() as i32 {
            let pos = field.to_pos(cur_x as u32, cur_y as u32);
            field.cell(pos)
          } else {
            Cell::new(true)
          }
        }), inv_color, first_match);
        for &pattern_number in patterns {
          let pattern = &self.patterns[pattern_number];
          priorities_sum += pattern.p;
          moves_count += pattern.moves.len();
          matched.push((pattern_number, x, y));
        }
      }
    }
    let mut result = Vec::with_capacity(moves_count);
    for (pattern_number, center_x, center_y) in matched {
      let pattern = &self.patterns[pattern_number];
      for &Move { x, y, p: probability } in &pattern.moves {
        let move_x = center_x - (pattern.width - 1) / 2 + x;
        let move_y = center_y - (pattern.height - 1) / 2 + y;
        result.push((field.to_pos(move_x, move_y), probability * pattern.p / priorities_sum));
      }
    }
    result
  }

  pub fn find_sorted(&self, field: &Field, player: Player, first_match: bool) -> Vec<(Pos, f64)> {
    let moves = self.find(field, player, first_match);
    let mut map = HashMap::with_capacity(moves.len());
    for (pos, p) in moves {
      let sum_p = map.get(&pos).cloned().unwrap_or(0f64) + p;
      map.insert(pos, sum_p);
    }
    let mut result = map.into_iter().collect::<Vec<(Pos, f64)>>();
    result.sort_by(|&(_, a), &(_, b)| b.partial_cmp(&a).expect("Cann't compare f64 types."));
    result
  }

  pub fn find_foreground(&self, field: &Field, player: Player, first_match: bool) -> Option<Pos> {
    self.find_sorted(field, player, first_match).first().map(|&(pos, _)| pos)
  }

  pub fn find_rand<T: Rng>(&self, field: &Field, player: Player, first_match: bool, rng: &mut T) -> Option<Pos> {
    let moves = self.find_sorted(field, player, first_match);
    if moves.is_empty() {
      return None;
    }
    let rand = rng.gen();
    let mut sum = 0f64;
    let mut idx = 0;
    while sum < rand && idx < moves.len() {
      sum += moves[idx].1;
      idx += 1;
    }
    Some(moves[idx - 1].0)
  }
}
<commit_msg>Remove todo.<commit_after>use std::collections::{HashSet, HashMap};
use std::io::{BufReader, BufRead};
use std::str::FromStr;
use std::fs::File;
use std::cmp;
use rand::Rng;
use tar::Archive;
use spiral::Spiral;
use dfa::{Dfa, DfaState};
use player::Player;
use cell::Cell;
use field::{Pos, Field};

#[derive(Clone, Debug)]
struct Move {
  x: u32,
  y: u32,
  p: f64 // probability
}

struct Pattern {
  p: f64, // priority
  width: u32,
  height: u32,
  moves: Vec<Move>
}

pub struct Patterns {
  min_size: u32,
  dfa: Dfa,
  patterns: Vec<Pattern>
}

impl Patterns {
  pub fn empty() -> Patterns {
    Patterns {
      min_size: u32::max_value(),
      dfa: Dfa::empty(),
      patterns: Vec::with_capacity(0)
    }
  }

  fn read_header<T: BufRead>(input: &mut T, s: &mut String) -> (u32, u32, u32, f64) {
    s.clear();
    input.read_line(s).ok();
    s.pop();
    let mut split = s.split(' ').fuse();
    let width = u32::from_str(split.next().expect("Invalid pattern format: expected width.")).expect("Invalid pattern format: width must be u32.");
    let height = u32::from_str(split.next().expect("Invalid pattern format: expected height.")).expect("Invalid pattern format: height must be u32.");
    let moves_count = u32::from_str(split.next().expect("Invalid pattern format: expected moves count.")).expect("Invalid pattern format: moves count must be u32.");
    let priority = f64::from_str(split.next().expect("Invalid pattern format: expected priority.")).expect("Invalid pattern format: priority must be f64.");
    (width, height, moves_count, priority)
  }

  fn read_pattern<T: BufRead>(input: &mut T, s: &mut String, width: u32, height: u32) {
    s.clear();
    for _ in 0 .. height {
      let last_len = s.len();
      input.read_line(s).ok();
      s.pop();
      assert_eq!((s.len() - last_len) as u32, width)
    }
  }

  fn read_moves<T: BufRead>(input: &mut T, s: &mut String, moves_count: u32) -> Vec<Move> {
    let mut moves = Vec::with_capacity(moves_count as usize);
    let mut priorities_sum = 0f64;
    for _ in 0 .. moves_count {
      s.clear();
      input.read_line(s).ok();
      s.pop();
      let mut split = s.split(' ').fuse();
      let x = u32::from_str(split.next().expect("Invalid pattern format: expected x coordinate.")).expect("Invalid pattern format: x coordinate must be u32.");
      let y = u32::from_str(split.next().expect("Invalid pattern format: expected x coordinate.")).expect("Invalid pattern format: x coordinate must be u32.");
      let p = f64::from_str(split.next().expect("Invalid pattern format: expected probability.")).expect("Invalid pattern format: probability must be f64.");
      priorities_sum += p;
      moves.push(Move {
        x: x,
        y: y,
        p: p
      });
    }
    for m in &mut moves {
      m.p /= priorities_sum;
    }
    moves
  }

  fn covering_spiral_length(side_of_square: u32) -> u32 {
    let x = side_of_square / 2 + 1;
    let y = (1 - side_of_square % 2) * side_of_square * 2;
    (8 * x - 13) * x + 6 - y
  }

  fn rotate(width: u32, height: u32, x: i32, y: i32, rotation: u32) -> (i32, i32) {
    match rotation {
      0 => (x, y),
      1 => (width as i32 - x - 1, y),
      2 => (x, height as i32 - y - 1),
      3 => (width as i32 - x - 1, height as i32 - y - 1),
      4 => (y, x),
      5 => (height as i32 - y - 1, x),
      6 => (y, width as i32 - x - 1),
      7 => (height as i32 - y - 1, width as i32 - x - 1),
      r => panic!("Invalid rotation number: {}", r)
    }
  }

  fn build_dfa(name: &str, width: u32, height: u32, pattern: usize, rotation: u32, s: &str) -> Dfa {
    let center_x = (width - 1) / 2;
    let center_y = (height - 1) / 2;
    let spiral_length = Patterns::covering_spiral_length(cmp::max(width, height)) as usize;
    let mut states = Vec::with_capacity(spiral_length + 2);
    let fs = spiral_length; // "Found" state.
    let nfs = spiral_length + 1; // "Not found" state.
    for (i, (shift_x, shift_y)) in Spiral::new().into_iter().take(spiral_length).enumerate() {
      let nxt = i + 1;
      let (x, y) = Patterns::rotate(width, height, center_x as i32 + shift_x, center_y as i32 + shift_y, rotation);
      let state = if x >= 0 && x < width as i32 && y >= 0 && y < height as i32 {
        let pos = y as u32 * width + x as u32;
        match s.char_at(pos as usize) {
          '.' => DfaState::new(nxt, nfs, nfs, nfs, false, HashSet::with_capacity(0)),
          '?' => DfaState::new(nxt, nxt, nxt, nfs, false, HashSet::with_capacity(0)),
          '*' => DfaState::new(nxt, nxt, nxt, nxt, false, HashSet::with_capacity(0)),
          'R' => DfaState::new(nfs, nxt, nfs, nfs, false, HashSet::with_capacity(0)),
          'B' => DfaState::new(nfs, nfs, nxt, nfs, false, HashSet::with_capacity(0)),
          'r' => DfaState::new(nxt, nxt, nfs, nfs, false, HashSet::with_capacity(0)),
          'b' => DfaState::new(nxt, nfs, nxt, nfs, false, HashSet::with_capacity(0)),
          '#' => DfaState::new(nfs, nfs, nfs, nxt, false, HashSet::with_capacity(0)),
          c   => panic!("Invalid character in the pattern '{}': {}", name, c)
        }
      } else {
        DfaState::new(nxt, nxt, nxt, nxt, false, HashSet::with_capacity(0))
      };
      states.push(state);
    }
    let mut patterns = HashSet::with_capacity(1);
    patterns.insert(pattern);
    states.push(DfaState::new(fs, fs, fs, fs, true, patterns));
    states.push(DfaState::new(nfs, nfs, nfs, nfs, true, HashSet::with_capacity(0)));
    Dfa::new(states)
  }

  pub fn load(file: File) -> Patterns {
    let archive = Archive::new(file);
    let mut s = String::new();
    let mut pattern_s = String::new();
    let mut patterns = Vec::new();
    let iter = archive.files().expect("Reading of tar archive is failed.").into_iter().map(|file| file.expect("Reading of file in tar archive is failed."));
    let mut dfa = Dfa::empty();
    let mut min_size = u32::max_value();
    for file in iter {
      let name = file.header().path().ok()
        .and_then(|path| path.to_str().map(|s| s.to_owned()))
        .unwrap_or_else(|| "<unknown>".to_owned());
      let mut input = BufReader::new(file);
      let (width, height, moves_count, priority) = Patterns::read_header(&mut input, &mut s);
      if width < min_size {
        min_size = width;
      }
      if height < min_size {
        min_size = height;
      }
      Patterns::read_pattern(&mut input, &mut pattern_s, width, height);
      let moves = Patterns::read_moves(&mut input, &mut s, moves_count);
      for i in 0 .. 8 {
        let cur_dfa = Patterns::build_dfa(name.as_str(), width, height, patterns.len(), i, &pattern_s);
        dfa = dfa.product(&cur_dfa);
        patterns.push(Pattern {
          p: priority,
          width: width,
          height: height,
          moves: moves.iter().map(|m| {
            let (x, y) = Patterns::rotate(width, height, m.x as i32, m.y as i32, i);
            Move {
              x: x as u32,
              y: y as u32,
              p: m.p
            }
          }).collect()
        });
      }
    }
    Patterns {
      min_size: min_size,
      dfa: dfa,
      patterns: patterns
    }
  }

  pub fn find(&self, field: &Field, player: Player, first_match: bool) -> Vec<(Pos, f64)> {
    if self.dfa.is_empty() {
      return Vec::with_capacity(0);
    }
    let mut priorities_sum = 0f64;
    let mut moves_count = 0usize;
    let mut matched = Vec::new();
    let left_border = (self.min_size - 1) / 2;
    let right_border = self.min_size / 2;
    let inv_color = player == Player::Black;
    for y in left_border .. field.height() - right_border {
      for x in left_border .. field.width() - right_border {
        let patterns = self.dfa.run(&mut Spiral::new().into_iter().map(|(shift_x, shift_y)| {
          let cur_x = x as i32 + shift_x;
          let cur_y = y as i32 + shift_y;
          if cur_x >= 0 && cur_x < field.width() as i32 && cur_y >= 0 && cur_y < field.height() as i32 {
            let pos = field.to_pos(cur_x as u32, cur_y as u32);
            field.cell(pos)
          } else {
            Cell::new(true)
          }
        }), inv_color, first_match);
        for &pattern_number in patterns {
          let pattern = &self.patterns[pattern_number];
          priorities_sum += pattern.p;
          moves_count += pattern.moves.len();
          matched.push((pattern_number, x, y));
        }
      }
    }
    let mut result = Vec::with_capacity(moves_count);
    for (pattern_number, center_x, center_y) in matched {
      let pattern = &self.patterns[pattern_number];
      for &Move { x, y, p: probability } in &pattern.moves {
        let move_x = center_x - (pattern.width - 1) / 2 + x;
        let move_y = center_y - (pattern.height - 1) / 2 + y;
        result.push((field.to_pos(move_x, move_y), probability * pattern.p / priorities_sum));
      }
    }
    result
  }

  pub fn find_sorted(&self, field: &Field, player: Player, first_match: bool) -> Vec<(Pos, f64)> {
    let moves = self.find(field, player, first_match);
    let mut map = HashMap::with_capacity(moves.len());
    for (pos, p) in moves {
      let sum_p = map.get(&pos).cloned().unwrap_or(0f64) + p;
      map.insert(pos, sum_p);
    }
    let mut result = map.into_iter().collect::<Vec<(Pos, f64)>>();
    result.sort_by(|&(_, a), &(_, b)| b.partial_cmp(&a).expect("Cann't compare f64 types."));
    result
  }

  pub fn find_foreground(&self, field: &Field, player: Player, first_match: bool) -> Option<Pos> {
    self.find_sorted(field, player, first_match).first().map(|&(pos, _)| pos)
  }

  pub fn find_rand<T: Rng>(&self, field: &Field, player: Player, first_match: bool, rng: &mut T) -> Option<Pos> {
    let moves = self.find_sorted(field, player, first_match);
    if moves.is_empty() {
      return None;
    }
    let rand = rng.gen();
    let mut sum = 0f64;
    let mut idx = 0;
    while sum < rand && idx < moves.len() {
      sum += moves[idx].1;
      idx += 1;
    }
    Some(moves[idx - 1].0)
  }
}
<|endoftext|>


<commit_before>#![cfg(target_os = "macos")]

use std::convert::From;
use std::os::raw::c_void;
use cocoa::appkit::NSApplicationActivationPolicy;
use {Window, WindowBuilder};

/// Additional methods on `Window` that are specific to MacOS.
pub trait WindowExt {
    /// Returns a pointer to the cocoa `NSWindow` that is used by this window.
    ///
    /// The pointer will become invalid when the `Window` is destroyed.
    fn get_nswindow(&self) -> *mut c_void;

    /// Returns a pointer to the cocoa `NSView` that is used by this window.
    ///
    /// The pointer will become invalid when the `Window` is destroyed.
    fn get_nsview(&self) -> *mut c_void;
}

/// Corresponds to `NSApplicationActivationPolicy`.
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum ActivationPolicy {
    /// Corresponds to `NSApplicationActivationPolicyRegular`.
    Regular,
    /// Corresponds to `NSApplicationActivationPolicyAccessory`.
    Accessory,
    /// Corresponds to `NSApplicationActivationPolicyProhibited`.
    Prohibited,
}

impl Default for ActivationPolicy {
    fn default() -> Self {
        ActivationPolicy::Regular
    }
}

impl From<ActivationPolicy> for NSApplicationActivationPolicy {
    fn from(activation_policy: ActivationPolicy) -> Self {
        match activation_policy {
            ActivationPolicy::Regular =>
                NSApplicationActivationPolicy::NSApplicationActivationPolicyRegular,
            ActivationPolicy::Accessory =>
                NSApplicationActivationPolicy::NSApplicationActivationPolicyAccessory,
            ActivationPolicy::Prohibited =>
                NSApplicationActivationPolicy::NSApplicationActivationPolicyProhibited,
        }
    }
}

/// Additional methods on `WindowBuilder` that are specific to MacOS.
pub trait WindowBuilderExt {
    fn with_activation_policy(mut self, activation_policy: ActivationPolicy) -> WindowBuilder;
}

impl WindowBuilderExt for WindowBuilder {
    /// Sets the activation policy for the window being built
    #[inline]
    fn with_activation_policy(mut self, activation_policy: ActivationPolicy) -> WindowBuilder {
        self.platform_specific.activation_policy = activation_policy;
        self
    }
}
<commit_msg>Fix WindowExt implemented on the wrong Window<commit_after>#![cfg(target_os = "macos")]

use std::convert::From;
use std::os::raw::c_void;
use cocoa::appkit::NSApplicationActivationPolicy;
use {Window, WindowBuilder};

/// Additional methods on `Window` that are specific to MacOS.
pub trait WindowExt {
    /// Returns a pointer to the cocoa `NSWindow` that is used by this window.
    ///
    /// The pointer will become invalid when the `Window` is destroyed.
    fn get_nswindow(&self) -> *mut c_void;

    /// Returns a pointer to the cocoa `NSView` that is used by this window.
    ///
    /// The pointer will become invalid when the `Window` is destroyed.
    fn get_nsview(&self) -> *mut c_void;
}

impl WindowExt for Window {
    #[inline]
    fn get_nswindow(&self) -> *mut c_void {
        self.window.get_nswindow()
    }

    #[inline]
    fn get_nsview(&self) -> *mut c_void {
        self.window.get_nsview()
    }
}

/// Corresponds to `NSApplicationActivationPolicy`.
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum ActivationPolicy {
    /// Corresponds to `NSApplicationActivationPolicyRegular`.
    Regular,
    /// Corresponds to `NSApplicationActivationPolicyAccessory`.
    Accessory,
    /// Corresponds to `NSApplicationActivationPolicyProhibited`.
    Prohibited,
}

impl Default for ActivationPolicy {
    fn default() -> Self {
        ActivationPolicy::Regular
    }
}

impl From<ActivationPolicy> for NSApplicationActivationPolicy {
    fn from(activation_policy: ActivationPolicy) -> Self {
        match activation_policy {
            ActivationPolicy::Regular =>
                NSApplicationActivationPolicy::NSApplicationActivationPolicyRegular,
            ActivationPolicy::Accessory =>
                NSApplicationActivationPolicy::NSApplicationActivationPolicyAccessory,
            ActivationPolicy::Prohibited =>
                NSApplicationActivationPolicy::NSApplicationActivationPolicyProhibited,
        }
    }
}

/// Additional methods on `WindowBuilder` that are specific to MacOS.
pub trait WindowBuilderExt {
    fn with_activation_policy(mut self, activation_policy: ActivationPolicy) -> WindowBuilder;
}

impl WindowBuilderExt for WindowBuilder {
    /// Sets the activation policy for the window being built
    #[inline]
    fn with_activation_policy(mut self, activation_policy: ActivationPolicy) -> WindowBuilder {
        self.platform_specific.activation_policy = activation_policy;
        self
    }
}
<|endoftext|>


<commit_before>mod figure;

use stamp;

use summary::Summary;

macro_rules! plot {
    ($p: expr) => {
        match $p {
            Ok(t) => Ok(t),
            Err(_) => Err("Unable to plot sample data"),
        }
    }
}

struct Boxplot {
    box_lo: f64,
    box_mid: f64,
    box_hi: f64,
    marker: f64,
    wh_lo: f64,
    wh_hi: f64,
}

impl Boxplot {
    fn from_summary(summary: &Summary) -> Self {
        let range = summary.range();
        let n = |x| (x - summary.min()) / range;

        Boxplot {
            box_lo: n(summary.lower_quartile()),
            box_mid: n(summary.median()),
            box_hi: n(summary.upper_quartile()),
            marker: n(summary.mean()),
            wh_lo: n(summary.min()),
            wh_hi: n(summary.max()),
        }
    }

    fn from_summary_no_outliers(summary: &Summary) -> Self {
        let range = summary.max_non_outlier() - summary.min_non_outlier();
        let n = |x| (x - summary.min_non_outlier()) / range;

        Boxplot {
            box_lo: n(summary.lower_quartile()),
            box_mid: n(summary.median()),
            box_hi: n(summary.upper_quartile()),
            marker: n(summary.mean()),
            wh_lo: n(summary.min_non_outlier()),
            wh_hi: n(summary.max_non_outlier()),
        }
    }
}

struct BoxplotCols {
    box_lo: usize,
    box_mid: usize,
    box_hi: usize,
    marker: usize,
    wh_lo: usize,
    wh_hi: usize,
}

impl BoxplotCols {
    fn new(data: &Boxplot, width: usize) -> Self {
        let max_col = (width - 1) as f64;
        let to_col = |x: f64| (x * max_col).floor() as usize;

        BoxplotCols {
            box_lo: to_col(data.box_lo),
            box_mid: to_col(data.box_mid),
            box_hi: to_col(data.box_hi),
            marker: to_col(data.marker),
            wh_lo: to_col(data.wh_lo),
            wh_hi: to_col(data.wh_hi),
        }
    }
}

struct RowChars {
    wh_lo: &'static str,
    wh_lo_box_lo_fill: &'static str,
    box_lo: &'static str,
    box_lo_box_mid_fill: &'static str,
    box_mid: &'static str,
    box_mid_box_hi_fill: &'static str,
    box_hi: &'static str,
    box_hi_wh_hi_fill: &'static str,
    wh_hi: &'static str,
}

impl RowChars {
    pub fn render(&self, row: &mut Vec<String>, cols: &BoxplotCols) {
        // Lower whisker extent.
        for i in (cols.wh_lo + 1)..cols.box_lo {
            row[i] = self.wh_lo_box_lo_fill.to_string();
        }

        // Upper whisker extent.
        for i in (cols.box_hi + 1)..cols.wh_hi {
            row[i] = self.box_hi_wh_hi_fill.to_string();
        }

        // Lower box extent.
        for i in (cols.box_lo + 1)..cols.box_mid {
            row[i] = self.box_lo_box_mid_fill.to_string();
        }

        // Upper box extent.
        for i in (cols.box_mid + 1)..cols.box_hi {
            row[i] = self.box_mid_box_hi_fill.to_string();
        }

        // Lower box end.
        row[cols.box_lo] = self.box_lo.to_string();

        // Upper box end.
        row[cols.box_hi] = self.box_hi.to_string();

        // Lower whisker end.
        row[cols.wh_lo] = self.wh_lo.to_string();

        // Upper whisker end.
        row[cols.wh_hi] = self.wh_hi.to_string();

        // Middle line.
        row[cols.box_mid] = self.box_mid.to_string();
    }
}

struct BoxplotChars {
    marker: &'static str,
    rows: [RowChars; 3],
}

impl BoxplotChars {
    pub fn render(&self, summary: &Summary, width: usize, outliers: bool)
                  -> Result<String, &'static str> {
        let data = if outliers {
            Boxplot::from_summary(summary)
        } else {
            Boxplot::from_summary_no_outliers(summary)
        };
        let cols = BoxplotCols::new(&data, width);

        let mut plot = Plot::new(width);

        self.rows[0].render(&mut plot.0, &cols);
        self.rows[1].render(&mut plot.1, &cols);
        self.rows[2].render(&mut plot.2, &cols);

        let no_marker = plot.render();

        let base = plot!(stamp::Stamp::new(&no_marker))?;
        let marker = plot!(stamp::Stamp::new(self.marker))?;
        let layered = plot!(base.layer(&marker, cols.marker, 1))?;

        Ok(layered.render())
    }
}

static ASCII_CHARS: BoxplotChars = BoxplotChars {
    marker: "x",
    rows: [
        RowChars {
            wh_lo: " ",
            wh_lo_box_lo_fill: " ",
            box_lo: "+",
            box_lo_box_mid_fill: "-",
            box_mid: "+",
            box_mid_box_hi_fill: "-",
            box_hi: "+",
            box_hi_wh_hi_fill: " ",
            wh_hi: " ",
        },
        RowChars {
            wh_lo: "|",
            wh_lo_box_lo_fill: "-",
            box_lo: "|",
            box_lo_box_mid_fill: " ",
            box_mid: "|",
            box_mid_box_hi_fill: " ",
            box_hi: "|",
            box_hi_wh_hi_fill: "-",
            wh_hi: "|",
        },
        RowChars {
            wh_lo: " ",
            wh_lo_box_lo_fill: " ",
            box_lo: "+",
            box_lo_box_mid_fill: "-",
            box_mid: "+",
            box_mid_box_hi_fill: "-",
            box_hi: "+",
            box_hi_wh_hi_fill: " ",
            wh_hi: " ",
        },
    ],
};

static UNICODE_CHARS: BoxplotChars = BoxplotChars {
    marker: "✕",
    rows: [
        RowChars {
            wh_lo: "┬",
            wh_lo_box_lo_fill: " ",
            box_lo: "┌",
            box_lo_box_mid_fill: "─",
            box_mid: "┬",
            box_mid_box_hi_fill: "─",
            box_hi: "┐",
            box_hi_wh_hi_fill: " ",
            wh_hi: "┬",
        },
        RowChars {
            wh_lo: "├",
            wh_lo_box_lo_fill: "─",
            box_lo: "┤",
            box_lo_box_mid_fill: " ",
            box_mid: "│",
            box_mid_box_hi_fill: " ",
            box_hi: "├",
            box_hi_wh_hi_fill: "─",
            wh_hi: "┤",
        },
        RowChars {
            wh_lo: "┴",
            wh_lo_box_lo_fill: " ",
            box_lo: "└",
            box_lo_box_mid_fill: "─",
            box_mid: "┴",
            box_mid_box_hi_fill: "─",
            box_hi: "┘",
            box_hi_wh_hi_fill: " ",
            wh_hi: "┴",
        },
    ],
};

fn make_row(width: usize) -> Vec<String> {
    use std::iter::repeat;

    let mut row = vec![];
    row.extend(repeat(String::from("")).take(width));

    row
}

struct Plot(Vec<String>, Vec<String>, Vec<String>);

impl Plot {
    fn new(width: usize) -> Self {
        Plot(make_row(width), make_row(width), make_row(width))
    }

    fn render(&self) -> String {
        let rows = vec![
            self.0.join(""),
            self.1.join(""),
            self.2.join(""),
        ];

        rows.join("\n")
    }
}

pub fn summary_plot(summary: &Summary, width: usize, ascii: bool, outliers: bool)
                    -> Result<String, &'static str> {
    let plot_style = if ascii { &ASCII_CHARS } else { &UNICODE_CHARS };

    plot_style.render(summary, width, outliers)
}

pub fn comparison_plot(
    summaries: &[&Summary],
    width: usize,
    ascii: bool,
    border: bool,
    outliers: bool,
) -> Result<String, &'static str> {
    if summaries.is_empty() {
        return Err("Cannot plot empty list of summaries");
    }

    let padding = if border { 2 } else { 0 };
    let content_width = (width - 2 * padding) as f64;
    let border_style = if ascii {
        figure::ASCII_BORDER
    } else {
        figure::UNICODE_BORDER
    };

    use std::f64;

    let plot_min = if outliers { Summary::min } else { Summary::min_non_outlier };
    let min = summaries
        .iter()
        .map(|s| plot_min(s))
        .fold(f64::MAX, |x, y| x.min(y));

    let plot_max = if outliers { Summary::max } else { Summary::max_non_outlier };
    let max = summaries
        .iter()
        .map(|s| plot_max(s))
        .fold(f64::MIN, |x, y| x.max(y));

    // Used to compute relative widths of boxplots from their own ranges.
    let range = max - min;

    let mut plots = vec![];

    for s in summaries {
        let s_min = if outliers { s.min() } else { s.min_non_outlier() };
        let s_max = if outliers { s.max() } else { s.max_non_outlier() };

        // Proportion of total content width spanned by this plot.
        let p = (s_max - s_min) / range;

        // Boxplot content width in cols.
        let w = (content_width * p).floor().max(1.0);
        assert!(1.0 <= w);
        assert!(w <= content_width);

        let plot = plot!(stamp::Stamp::new(&summary_plot(s, w as usize, ascii, outliers)?))?;

        assert!(min <= s_min);
        let offset_p = (s_min - min) / range;

        let offset = (offset_p * content_width).min(content_width - w);
        assert!(offset + w <= content_width);

        plots.push((plot, padding + (offset as usize)));
    }

    let height = &plots
        .iter()
        .map(|&(ref p, _)| p.height())
        .sum() + (padding * 2);

    let base = if border {
        figure::Border::new(border_style, width, height).render()
    } else {
        figure::Filled::blank(width, height).render()
    };

    let mut all_plots = plot!(stamp::Stamp::new(&base))?;

    for (i, &(ref plot, left_offset)) in plots.iter().enumerate() {
        all_plots = plot!(all_plots.layer(&plot, left_offset, padding + i * plot.height()))?;
    }

    Ok(all_plots.render())
}
<commit_msg>Account for skewed means when plotting without outliers<commit_after>mod figure;

use stamp;

use summary::Summary;

macro_rules! plot {
    ($p: expr) => {
        match $p {
            Ok(t) => Ok(t),
            Err(_) => Err("Unable to plot sample data"),
        }
    }
}

struct Boxplot {
    box_lo: f64,
    box_mid: f64,
    box_hi: f64,
    marker: f64,
    wh_lo: f64,
    wh_hi: f64,
}

impl Boxplot {
    fn from_summary(summary: &Summary) -> Self {
        let range = summary.range();
        let n = |x| (x - summary.min()) / range;

        Boxplot {
            box_lo: n(summary.lower_quartile()),
            box_mid: n(summary.median()),
            box_hi: n(summary.upper_quartile()),
            marker: n(summary.mean()),
            wh_lo: n(summary.min()),
            wh_hi: n(summary.max()),
        }
    }

    fn from_summary_no_outliers(summary: &Summary) -> Self {
        let min = summary.min_non_outlier().min(summary.mean());
        let max = summary.max_non_outlier().max(summary.mean());
        let range = max - min;
        let n = |x| (x - min) / range;

        Boxplot {
            box_lo: n(summary.lower_quartile()),
            box_mid: n(summary.median()),
            box_hi: n(summary.upper_quartile()),
            marker: n(summary.mean()),
            wh_lo: n(summary.min_non_outlier()),
            wh_hi: n(summary.max_non_outlier()),
        }
    }
}

struct BoxplotCols {
    box_lo: usize,
    box_mid: usize,
    box_hi: usize,
    marker: usize,
    wh_lo: usize,
    wh_hi: usize,
}

impl BoxplotCols {
    fn new(data: &Boxplot, width: usize) -> Self {
        let max_col = (width - 1) as f64;
        let to_col = |x: f64| (x * max_col).floor() as usize;

        BoxplotCols {
            box_lo: to_col(data.box_lo),
            box_mid: to_col(data.box_mid),
            box_hi: to_col(data.box_hi),
            marker: to_col(data.marker),
            wh_lo: to_col(data.wh_lo),
            wh_hi: to_col(data.wh_hi),
        }
    }
}

struct RowChars {
    wh_lo: &'static str,
    wh_lo_box_lo_fill: &'static str,
    box_lo: &'static str,
    box_lo_box_mid_fill: &'static str,
    box_mid: &'static str,
    box_mid_box_hi_fill: &'static str,
    box_hi: &'static str,
    box_hi_wh_hi_fill: &'static str,
    wh_hi: &'static str,
}

impl RowChars {
    pub fn render(&self, row: &mut Vec<String>, cols: &BoxplotCols) {
        // Lower whisker extent.
        for i in (cols.wh_lo + 1)..cols.box_lo {
            row[i] = self.wh_lo_box_lo_fill.to_string();
        }

        // Upper whisker extent.
        for i in (cols.box_hi + 1)..cols.wh_hi {
            row[i] = self.box_hi_wh_hi_fill.to_string();
        }

        // Lower box extent.
        for i in (cols.box_lo + 1)..cols.box_mid {
            row[i] = self.box_lo_box_mid_fill.to_string();
        }

        // Upper box extent.
        for i in (cols.box_mid + 1)..cols.box_hi {
            row[i] = self.box_mid_box_hi_fill.to_string();
        }

        // Lower box end.
        row[cols.box_lo] = self.box_lo.to_string();

        // Upper box end.
        row[cols.box_hi] = self.box_hi.to_string();

        // Lower whisker end.
        row[cols.wh_lo] = self.wh_lo.to_string();

        // Upper whisker end.
        row[cols.wh_hi] = self.wh_hi.to_string();

        // Middle line.
        row[cols.box_mid] = self.box_mid.to_string();
    }
}

struct BoxplotChars {
    marker: &'static str,
    rows: [RowChars; 3],
}

impl BoxplotChars {
    pub fn render(&self, summary: &Summary, width: usize, outliers: bool)
                  -> Result<String, &'static str> {
        let data = if outliers {
            Boxplot::from_summary(summary)
        } else {
            Boxplot::from_summary_no_outliers(summary)
        };
        let cols = BoxplotCols::new(&data, width);
        let mut plot = Plot::new(width);

        self.rows[0].render(&mut plot.0, &cols);
        self.rows[1].render(&mut plot.1, &cols);
        self.rows[2].render(&mut plot.2, &cols);

        let no_marker = plot.render();

        let base = plot!(stamp::Stamp::new(&no_marker))?;
        let marker = plot!(stamp::Stamp::new(self.marker))?;
        let layered = plot!(base.layer(&marker, cols.marker, 1))?;

        Ok(layered.render())
    }
}

static ASCII_CHARS: BoxplotChars = BoxplotChars {
    marker: "x",
    rows: [
        RowChars {
            wh_lo: " ",
            wh_lo_box_lo_fill: " ",
            box_lo: "+",
            box_lo_box_mid_fill: "-",
            box_mid: "+",
            box_mid_box_hi_fill: "-",
            box_hi: "+",
            box_hi_wh_hi_fill: " ",
            wh_hi: " ",
        },
        RowChars {
            wh_lo: "|",
            wh_lo_box_lo_fill: "-",
            box_lo: "|",
            box_lo_box_mid_fill: " ",
            box_mid: "|",
            box_mid_box_hi_fill: " ",
            box_hi: "|",
            box_hi_wh_hi_fill: "-",
            wh_hi: "|",
        },
        RowChars {
            wh_lo: " ",
            wh_lo_box_lo_fill: " ",
            box_lo: "+",
            box_lo_box_mid_fill: "-",
            box_mid: "+",
            box_mid_box_hi_fill: "-",
            box_hi: "+",
            box_hi_wh_hi_fill: " ",
            wh_hi: " ",
        },
    ],
};

static UNICODE_CHARS: BoxplotChars = BoxplotChars {
    marker: "✕",
    rows: [
        RowChars {
            wh_lo: "┬",
            wh_lo_box_lo_fill: " ",
            box_lo: "┌",
            box_lo_box_mid_fill: "─",
            box_mid: "┬",
            box_mid_box_hi_fill: "─",
            box_hi: "┐",
            box_hi_wh_hi_fill: " ",
            wh_hi: "┬",
        },
        RowChars {
            wh_lo: "├",
            wh_lo_box_lo_fill: "─",
            box_lo: "┤",
            box_lo_box_mid_fill: " ",
            box_mid: "│",
            box_mid_box_hi_fill: " ",
            box_hi: "├",
            box_hi_wh_hi_fill: "─",
            wh_hi: "┤",
        },
        RowChars {
            wh_lo: "┴",
            wh_lo_box_lo_fill: " ",
            box_lo: "└",
            box_lo_box_mid_fill: "─",
            box_mid: "┴",
            box_mid_box_hi_fill: "─",
            box_hi: "┘",
            box_hi_wh_hi_fill: " ",
            wh_hi: "┴",
        },
    ],
};

fn make_row(width: usize) -> Vec<String> {
    use std::iter::repeat;

    let mut row = vec![];
    row.extend(repeat(String::from("")).take(width));

    row
}

struct Plot(Vec<String>, Vec<String>, Vec<String>);

impl Plot {
    fn new(width: usize) -> Self {
        Plot(make_row(width), make_row(width), make_row(width))
    }

    fn render(&self) -> String {
        let rows = vec![
            self.0.join(""),
            self.1.join(""),
            self.2.join(""),
        ];

        rows.join("\n")
    }
}

pub fn summary_plot(summary: &Summary, width: usize, ascii: bool, outliers: bool)
                    -> Result<String, &'static str> {
    let plot_style = if ascii { &ASCII_CHARS } else { &UNICODE_CHARS };

    plot_style.render(summary, width, outliers)
}

pub fn comparison_plot(
    summaries: &[&Summary],
    width: usize,
    ascii: bool,
    border: bool,
    outliers: bool,
) -> Result<String, &'static str> {
    if summaries.is_empty() {
        return Err("Cannot plot empty list of summaries");
    }

    let padding = if border { 2 } else { 0 };
    let content_width = (width - 2 * padding) as f64;
    let border_style = if ascii {
        figure::ASCII_BORDER
    } else {
        figure::UNICODE_BORDER
    };

    use std::f64;

    let plot_min = |s: &Summary| if outliers {
        s.min()
    } else {
        s.min_non_outlier().min(s.mean())
    };
    let min = summaries
        .iter()
        .map(|s| plot_min(s))
        .fold(f64::MAX, |x, y| x.min(y));

    let plot_max = |s: &Summary| if outliers {
        s.max()
    } else {
        s.max_non_outlier().max(s.mean())
    };
    let max = summaries
        .iter()
        .map(|s| plot_max(s))
        .fold(f64::MIN, |x, y| x.max(y));

    // Used to compute relative widths of boxplots from their own ranges.
    let range = max - min;

    let mut plots = vec![];

    for s in summaries {
        let s_min = if outliers {
            s.min()
        } else {
            s.min_non_outlier().min(s.mean())
        };
        let s_max = if outliers {
            s.max()
        } else {
            s.max_non_outlier().max(s.mean())
        };

        // Proportion of total content width spanned by this plot.
        let p = (s_max - s_min) / range;

        // Boxplot content width in cols.
        let w = (content_width * p).floor().max(1.0);
        assert!(1.0 <= w);
        assert!(w <= content_width);

        let plot = plot!(stamp::Stamp::new(&summary_plot(s, w as usize, ascii, outliers)?))?;

        assert!(min <= s_min);
        let offset_p = (s_min - min) / range;

        let offset = (offset_p * content_width).min(content_width - w);
        assert!(offset + w <= content_width);

        plots.push((plot, padding + (offset as usize)));
    }

    let height = &plots
        .iter()
        .map(|&(ref p, _)| p.height())
        .sum() + (padding * 2);

    let base = if border {
        figure::Border::new(border_style, width, height).render()
    } else {
        figure::Filled::blank(width, height).render()
    };

    let mut all_plots = plot!(stamp::Stamp::new(&base))?;

    for (i, &(ref plot, left_offset)) in plots.iter().enumerate() {
        all_plots = plot!(all_plots.layer(&plot, left_offset, padding + i * plot.height()))?;
    }

    Ok(all_plots.render())
}
<|endoftext|>


<commit_before>#[derive(Debug, Clone)]
pub enum TaskPriority {
    NoPrio,
    Low,
    Medium,
    High,
}

impl<'a> From<&'a str> for TaskPriority {

    fn from(s: &str) -> TaskPriority {
        match s {
            "L" => TaskPriority::Low,
            "M" => TaskPriority::Medium,
            "H" => TaskPriority::High,
            _ => TaskPriority::NoPrio,
        }
    }
}

impl Into<String> for TaskPriority {

    fn into(self) -> String {
        String::from(match self {
            TaskPriority::Low    => "L",
            TaskPriority::Medium => "M",
            TaskPriority::High   => "H",
            TaskPriority::NoPrio => "",
        })
    }

}
<commit_msg>TaskPriority must derive PartialEq as well<commit_after>#[derive(Debug, Clone, PartialEq)]
pub enum TaskPriority {
    NoPrio,
    Low,
    Medium,
    High,
}

impl<'a> From<&'a str> for TaskPriority {

    fn from(s: &str) -> TaskPriority {
        match s {
            "L" => TaskPriority::Low,
            "M" => TaskPriority::Medium,
            "H" => TaskPriority::High,
            _ => TaskPriority::NoPrio,
        }
    }
}

impl Into<String> for TaskPriority {

    fn into(self) -> String {
        String::from(match self {
            TaskPriority::Low    => "L",
            TaskPriority::Medium => "M",
            TaskPriority::High   => "H",
            TaskPriority::NoPrio => "",
        })
    }

}
<|endoftext|>


<commit_before>use std::marker::PhantomData;
use std::ops::Index;
use std::ops::IndexMut;
use handle::*;

/// A growable list type, to store data for the different `Handle`.
///
/// `H` is the type of the Handle to acces the data. `D` is the type the data.
pub struct Property<H,D> {
    type_ : PhantomData<H>,
    data_ : Vec<D>,
}

impl<H, D : Clone> Property<H,D> {

    /// Constructs a new `Property<H,D>`.
    ///
    /// # Examples
    ///
    /// ```
    /// use lwmesh::property::Property;
    /// use lwmesh::handle::Vertex;
    ///
    /// let prop = Property::<Vertex,u32>::new();
    /// ```
    pub fn new() -> Property<H,D>{
        Property {
            type_ : PhantomData,
            data_ : Vec::new()
        }
    }

    /// Reserve the minimun capacity to store at least `size` elements in the given `Property<H,D>`.
    ///
    /// # Examples
    ///
    /// ```
    /// use lwmesh::property::Property;
    /// use lwmesh::handle::Vertex;
    ///
    /// let mut prop = Property::<Vertex,u32>::new();
    /// prop.reserve(42);
    /// assert!(prop.capacity() >= 42);
    /// ```
    pub fn reserve(&mut self, size : usize) {
        self.data_.reserve(size);
    }

    /// Returns the number of elements the given `Property<H,D>` can hold without reallocating.
    ///
    /// # Examples
    ///
    /// ```
    /// use lwmesh::property::Property;
    /// use lwmesh::handle::Vertex;
    ///
    /// let mut prop = Property::<Vertex,u32>::new();
    /// assert!(prop.capacity() == 0);
    /// prop.reserve(42);
    /// assert!(prop.capacity() >= 42);
    /// ```
    pub fn capacity(& self) -> usize {
        self.data_.capacity()
    }

    /// Resize the given `Property<H,D>` to have exactly `size` elements.
    ///
    /// If the actual size of the property is less than `size`, elements with value `value` are
    /// added to the property so that the size matches. Otherwise elements are removed from the
    /// vector until the size matches.
    ///
    /// # Examples
    ///
    /// ```
    /// use lwmesh::property::Property;
    /// use lwmesh::handle::Vertex;
    ///
    /// let mut prop = Property::<Vertex,u32>::new();
    /// prop.resize(13,17);
    /// ```
    pub fn resize(&mut self, size : usize, value : D) {
        let n = self.data_.len();
        if n < size {
            self.data_.reserve(size);
            let m = size-n;
            for _ in 0..m {
                self.data_.push(value.clone());
            }
        } else {
            let m = n-size;
            for _ in 0..m {
                self.data_.pop();
            }
        }
    }

    /// Returns the number of elements in the `Property<H,D>`.
    ///
    /// # Examples
    ///
    /// ```
    /// use lwmesh::property::Property;
    /// use lwmesh::handle::Vertex;
    ///
    /// let mut prop = Property::<Vertex,u32>::new();
    /// assert!(prop.len() == 0);
    /// prop.resize(13,17);
    /// assert!(prop.len() == 13)
    /// ```
    pub fn len(&self) -> usize() {
        self.data_.len()
    }
}

impl<T,D> Index<Handle<T> > for Property<Handle<T>,D> {
    type Output = D;

    fn index<'a>(&'a self, _index: Handle<T>) -> &'a D {
        return & self.data_[_index.idx().unwrap()];
    }
}

impl<T,D> IndexMut<Handle<T> > for Property<Handle<T> ,D> {
    fn index_mut<'a>(&'a mut self, _index: Handle<T>) -> &'a mut D {
        return &mut self.data_[_index.idx().unwrap()];
    }
}

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

    #[test]
    fn reserve() {
        let mut property = Property::<Vertex,u32>::new();
        property.reserve(16);
        assert!(16 <= property.capacity());
        property.reserve(33);
        assert!(33 <= property.capacity());
    }

    #[test]
    fn resize() {
        let mut property = Property::<Vertex,u32>::new();
        let size = 5;
        let val = 42;
        property.resize(size,val);
        assert!(property.len() == size);
        property.resize(size-3,val);
        assert!(property.len() == size-3);
    }

    #[test]
    fn index() {
        let mut property = Property::new();
        let size = 8;
        let val = 42;
        property.resize(size,val);
        let v1 = Vertex::new(3);
        assert!(property[v1] == 42);
        property[v1] = 23;
        assert!(property[v1] == 23);

        let ref property_const = & property;
        let v2 = Vertex::new(0);
        assert!(property_const[v2] == 42);
    }

    #[test]
    #[should_panic]
    fn index_out_of_bound() {
        let mut property = Property::new();
        let size = 8;
        let val = 42;
        property.resize(size,val);
        let v1 = Vertex::new(8);
        property[v1] = 0;
    }

    #[test]
    #[should_panic]
    fn index_none() {
        let mut property = Property::new();
        let size = 8;
        let val = 42;
        property.resize(size,val);
        let v1 = Vertex::invalid();
        property[v1] = 0;
    }
}
<commit_msg>property : adds push method to Property<commit_after>use std::marker::PhantomData;
use std::ops::Index;
use std::ops::IndexMut;
use handle::*;

/// A growable list type, to store data for the different `Handle`.
///
/// `H` is the type of the Handle to acces the data. `D` is the type the data.
pub struct Property<H,D> {
    type_ : PhantomData<H>,
    data_ : Vec<D>,
}

impl<T, D : Clone> Property<Handle<T>,D> {

    /// Constructs a new `Property<H,D>`.
    ///
    /// # Examples
    ///
    /// ```
    /// use lwmesh::property::Property;
    /// use lwmesh::handle::Vertex;
    ///
    /// let prop = Property::<Vertex,u32>::new();
    /// ```
    pub fn new() -> Property<Handle<T>,D>{
        Property {
            type_ : PhantomData,
            data_ : Vec::new()
        }
    }

    /// Reserve the minimun capacity to store at least `size` elements in the given `Property<H,D>`.
    ///
    /// # Examples
    ///
    /// ```
    /// use lwmesh::property::Property;
    /// use lwmesh::handle::Vertex;
    ///
    /// let mut prop = Property::<Vertex,u32>::new();
    /// prop.reserve(42);
    /// assert!(prop.capacity() >= 42);
    /// ```
    pub fn reserve(&mut self, size : usize) {
        self.data_.reserve(size);
    }

    /// Returns the number of elements the given `Property<H,D>` can hold without reallocating.
    ///
    /// # Examples
    ///
    /// ```
    /// use lwmesh::property::Property;
    /// use lwmesh::handle::Vertex;
    ///
    /// let mut prop = Property::<Vertex,u32>::new();
    /// assert!(prop.capacity() == 0);
    /// prop.reserve(42);
    /// assert!(prop.capacity() >= 42);
    /// ```
    pub fn capacity(& self) -> usize {
        self.data_.capacity()
    }

    /// Resize the given `Property<H,D>` to have exactly `size` elements.
    ///
    /// If the actual size of the property is less than `size`, elements with value `value` are
    /// added to the property so that the size matches. Otherwise elements are removed from the
    /// vector until the size matches.
    ///
    /// # Examples
    ///
    /// ```
    /// use lwmesh::property::Property;
    /// use lwmesh::handle::Vertex;
    ///
    /// let mut prop = Property::<Vertex,u32>::new();
    /// prop.resize(13,17);
    /// ```
    pub fn resize(&mut self, size : usize, value : D) {
        let n = self.data_.len();
        if n < size {
            self.data_.reserve(size);
            let m = size-n;
            for _ in 0..m {
                self.data_.push(value.clone());
            }
        } else {
            let m = n-size;
            for _ in 0..m {
                self.data_.pop();
            }
        }
    }

    /// Returns the number of elements in the `Property<H,D>`.
    ///
    /// # Examples
    ///
    /// ```
    /// use lwmesh::property::Property;
    /// use lwmesh::handle::Vertex;
    ///
    /// let mut prop = Property::<Vertex,u32>::new();
    /// assert!(prop.len() == 0);
    /// prop.resize(13,17);
    /// assert!(prop.len() == 13)
    /// ```
    pub fn len(&self) -> usize() {
        self.data_.len()
    }

    pub fn push(&mut self, value : D) -> Handle<T> {
        self.data_.push(value);
        Handle::new(self.data_.len())
    }
}

impl<T,D> Index<Handle<T> > for Property<Handle<T>,D> {
    type Output = D;

    fn index<'a>(&'a self, _index: Handle<T>) -> &'a D {
        return & self.data_[_index.idx().unwrap()];
    }
}

impl<T,D> IndexMut<Handle<T> > for Property<Handle<T> ,D> {
    fn index_mut<'a>(&'a mut self, _index: Handle<T>) -> &'a mut D {
        return &mut self.data_[_index.idx().unwrap()];
    }
}

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

    #[test]
    fn reserve() {
        let mut property = Property::<Vertex,u32>::new();
        property.reserve(16);
        assert!(16 <= property.capacity());
        property.reserve(33);
        assert!(33 <= property.capacity());
    }

    #[test]
    fn resize() {
        let mut property = Property::<Vertex,u32>::new();
        let size = 5;
        let val = 42;
        property.resize(size,val);
        assert!(property.len() == size);
        property.resize(size-3,val);
        assert!(property.len() == size-3);
    }

    #[test]
    fn index() {
        let mut property = Property::new();
        let size = 8;
        let val = 42;
        property.resize(size,val);
        let v1 = Vertex::new(3);
        assert!(property[v1] == 42);
        property[v1] = 23;
        assert!(property[v1] == 23);

        let ref property_const = & property;
        let v2 = Vertex::new(0);
        assert!(property_const[v2] == 42);
    }

    #[test]
    #[should_panic]
    fn index_out_of_bound() {
        let mut property = Property::new();
        let size = 8;
        let val = 42;
        property.resize(size,val);
        let v1 = Vertex::new(8);
        property[v1] = 0;
    }

    #[test]
    #[should_panic]
    fn index_none() {
        let mut property = Property::new();
        let size = 8;
        let val = 42;
        property.resize(size,val);
        let v1 = Vertex::invalid();
        property[v1] = 0;
    }
}
<|endoftext|>


<commit_before>extern crate procrs;
extern crate argparse;
#[macro_use]
extern crate prettytable;
use prettytable::Table;
use prettytable::format::FormatBuilder;
use std::collections::HashMap;
use std::iter::repeat;
use procrs::pid::*;
use procrs::TaskId;
use argparse::{ArgumentParser, StoreTrue, Store};

fn main() {
    let opts = parse_args();
    let (query, long, perf, verbose, tree) =
        (opts.query, opts.long, opts.perf, opts.verbose, opts.tree);

    let mut pids = PidIter::new_query(query).unwrap()
        .collect::<Result<Vec<_>, _>>().unwrap();

    let mut name_indent = HashMap::new();

    if verbose {
        for pid in pids {
            println!("{:?}", pid);
        }
        return
    }

    if opts.tree {
        pids = treeify_names(pids, &mut name_indent);
    } else {
        pids.sort_by(|p1, p2| p1.stat.pid.cmp(&p2.stat.pid));
    };

    let mut table = Table::init(
        pids.iter().map(|p| {
            let mut name = match tree {
                false => String::new(),
                true => name_indent.remove(&p.stat.pid).unwrap()
            };

            // TODO: There is a lot of boiler plate for Option<num...>,
            // this can probably be reduced with a custom type (row! calls to_string).
            match (long, perf) {
                (false, false) => {
                    name.push_str(&p.stat.comm);
                    row![p.stat.pid, p.stat.ppid, name]
                },
                (true, false) => {
                    name.push_str(
                        match p.cmdline.join(" ") {
                            ref s if s.len() > 0 => s,
                            _ => &p.stat.comm
                        }
                    );
                    row![p.stat.pid, p.stat.ppid,  name]
                },
                (false, true) => {
                    name.push_str(&p.stat.comm);
                    row![p.stat.pid, p.stat.ppid,
                        p.status.vmrss.map(|m| (m / 1024).to_string()).unwrap_or("".to_owned()),
                        name]
                },
                (true, true) => {
                    name.push_str(
                        match p.cmdline.join(" ") {
                            ref s if s.len() > 0 => s,
                            _ => &p.stat.comm
                        }
                    );
                    row![p.stat.pid, p.stat.ppid,
                        p.status.vmrss.map(|m| (m / 1024).to_string()).unwrap_or("".to_owned()),
                        name]
                }
            }
        }).collect::<Vec<_>>()
    );

    table.set_titles(
        match (long, perf) {
            (false, false) =>
                row!["Pid", "Ppid", "Cmd"],
            (true, false) =>
                row!["Pid", "Ppid", "Cmd"],
            (false, true) =>
                row!["Pid", "Ppid", "RSS", "Cmd"],
            (true, true) =>
                row!["Pid", "Ppid", "RSS", "Cmd"]
        }
    );
    table.set_format(
        FormatBuilder::new()
            .column_separator(' ')
            .build()
    );
    table.printstd();
}

// Given a vector of Pid structs, treeify their names, and return them in the right order.
// This is similar to ps -AH.
fn treeify_names(pids: Vec<Pid>, name_indents: &mut HashMap<TaskId, String>) -> Vec<Pid> {
    let mut child_pids = HashMap::new();
    for pid in pids {
        let ppid = pid.stat.ppid;
        child_pids.entry(ppid)
            .or_insert(Vec::new())
            .push(pid);
    }
    enumerate_children(0, &mut child_pids, name_indents, -1)
}

// Enumerate children pids, and return them.
fn enumerate_children(pid: TaskId, child_pids: &mut HashMap<TaskId, Vec<Pid>>,
    name_indents: &mut HashMap<TaskId, String>, indent: i32) -> Vec<Pid> {
    name_indents.insert(pid,
        match indent {
            i if i >= 0 =>
                repeat("  ").take(i as usize).collect::<String>(),
            _ => "".to_owned()
        }
    );
    let mut pids = Vec::new();
    let ppids = match child_pids.remove(&pid) {
        Some(v) => v,
        None => { return pids; }
    };
    for pid in ppids {
        let pid_num = pid.stat.pid;
        pids.push(pid);
        pids.append(
            &mut enumerate_children(pid_num, child_pids, name_indents, indent + 1)
        );
    }
    pids
}

struct ProgOpts {
    query: PidQuery,
    tree: bool,
    perf: bool,
    long: bool,
    verbose: bool
}

fn parse_args() -> ProgOpts {
    let mut opts = ProgOpts {
        query: PidQuery::NoneQuery,
        tree: false,
        perf: false,
        long: false,
        verbose: false
    };

    {
        let mut ap = ArgumentParser::new();
        ap.set_description("Query linux processes");
        ap.refer(&mut opts.tree)
            .add_option(&["-t", "--tree"], StoreTrue, "Display process tree");
        ap.refer(&mut opts.perf)
            .add_option(&["-p", "--perf"], StoreTrue, "Display performance information");
        ap.refer(&mut opts.long)
            .add_option(&["-l", "--long"], StoreTrue, "Display more information");
        ap.refer(&mut opts.verbose)
            .add_option(&["-v", "--verbose"], StoreTrue, "Verbose output");
        ap.refer(&mut opts.query)
            .add_argument("query", Store, "Optional query to search by, pid or string");
        ap.parse_args_or_exit();
    }

    opts
}
<commit_msg>Add cputime, but needs units.<commit_after>extern crate procrs;
extern crate argparse;
#[macro_use]
extern crate prettytable;
use prettytable::Table;
use prettytable::format::FormatBuilder;
use std::collections::HashMap;
use std::iter::repeat;
use procrs::pid::*;
use procrs::TaskId;
use argparse::{ArgumentParser, StoreTrue, Store};

fn main() {
    let opts = parse_args();
    let (query, long, perf, verbose, tree) =
        (opts.query, opts.long, opts.perf, opts.verbose, opts.tree);

    let mut pids = PidIter::new_query(query).unwrap()
        .collect::<Result<Vec<_>, _>>().unwrap();

    let mut name_indent = HashMap::new();

    if verbose {
        for pid in pids {
            println!("{:?}", pid);
        }
        return
    }

    if opts.tree {
        pids = treeify_names(pids, &mut name_indent);
    } else {
        pids.sort_by(|p1, p2| p1.stat.pid.cmp(&p2.stat.pid));
    };

    let mut table = Table::init(
        pids.iter().map(|p| {
            let mut name = match tree {
                false => String::new(),
                true => name_indent.remove(&p.stat.pid).unwrap()
            };

            // TODO: There is a lot of boiler plate for Option<num...>,
            // this can probably be reduced with a custom type (row! calls to_string).
            match (long, perf) {
                (false, false) => {
                    name.push_str(&p.stat.comm);
                    row![p.stat.pid, p.stat.ppid, name]
                },
                (true, false) => {
                    name.push_str(
                        match p.cmdline.join(" ") {
                            ref s if s.len() > 0 => s,
                            _ => &p.stat.comm
                        }
                    );
                    row![p.stat.pid, p.stat.ppid,  name]
                },
                (false, true) => {
                    name.push_str(&p.stat.comm);
                    row![p.stat.pid, p.stat.ppid,
                        p.status.vmrss.map(|m| (m / 1024).to_string()).unwrap_or("".to_owned()),
                        p.stat.utime,
                        name]
                },
                (true, true) => {
                    name.push_str(
                        match p.cmdline.join(" ") {
                            ref s if s.len() > 0 => s,
                            _ => &p.stat.comm
                        }
                    );
                    row![p.stat.pid, p.stat.ppid,
                        p.status.vmrss.map(|m| (m / 1024).to_string()).unwrap_or("".to_owned()),
                        p.stat.utime,
                        name]
                }
            }
        }).collect::<Vec<_>>()
    );

    table.set_titles(
        match (long, perf) {
            (false, false) =>
                row!["Pid", "Ppid", "Cmd"],
            (true, false) =>
                row!["Pid", "Ppid", "Cmd"],
            (false, true) =>
                row!["Pid", "Ppid", "RSS", "Time", "Cmd"],
            (true, true) =>
                row!["Pid", "Ppid", "RSS", "Time", "Cmd"]
        }
    );
    table.set_format(
        FormatBuilder::new()
            .column_separator(' ')
            .build()
    );
    table.printstd();
}

// Given a vector of Pid structs, treeify their names, and return them in the right order.
// This is similar to ps -AH.
fn treeify_names(pids: Vec<Pid>, name_indents: &mut HashMap<TaskId, String>) -> Vec<Pid> {
    let mut child_pids = HashMap::new();
    for pid in pids {
        let ppid = pid.stat.ppid;
        child_pids.entry(ppid)
            .or_insert(Vec::new())
            .push(pid);
    }
    enumerate_children(0, &mut child_pids, name_indents, -1)
}

// Enumerate children pids, and return them.
fn enumerate_children(pid: TaskId, child_pids: &mut HashMap<TaskId, Vec<Pid>>,
    name_indents: &mut HashMap<TaskId, String>, indent: i32) -> Vec<Pid> {
    name_indents.insert(pid,
        match indent {
            i if i >= 0 =>
                repeat("  ").take(i as usize).collect::<String>(),
            _ => "".to_owned()
        }
    );
    let mut pids = Vec::new();
    let ppids = match child_pids.remove(&pid) {
        Some(v) => v,
        None => { return pids; }
    };
    for pid in ppids {
        let pid_num = pid.stat.pid;
        pids.push(pid);
        pids.append(
            &mut enumerate_children(pid_num, child_pids, name_indents, indent + 1)
        );
    }
    pids
}

struct ProgOpts {
    query: PidQuery,
    tree: bool,
    perf: bool,
    long: bool,
    verbose: bool
}

fn parse_args() -> ProgOpts {
    let mut opts = ProgOpts {
        query: PidQuery::NoneQuery,
        tree: false,
        perf: false,
        long: false,
        verbose: false
    };

    {
        let mut ap = ArgumentParser::new();
        ap.set_description("Query linux processes");
        ap.refer(&mut opts.tree)
            .add_option(&["-t", "--tree"], StoreTrue, "Display process tree");
        ap.refer(&mut opts.perf)
            .add_option(&["-p", "--perf"], StoreTrue, "Display performance information");
        ap.refer(&mut opts.long)
            .add_option(&["-l", "--long"], StoreTrue, "Display more information");
        ap.refer(&mut opts.verbose)
            .add_option(&["-v", "--verbose"], StoreTrue, "Verbose output");
        ap.refer(&mut opts.query)
            .add_argument("query", Store, "Optional query to search by, pid or string");
        ap.parse_args_or_exit();
    }

    opts
}
<|endoftext|>


<commit_before>// FIXME: add the support of transient objects

use notify::{self, RecommendedWatcher, Watcher};
use std::collections::HashMap;
use std::path::{Path, PathBuf};
use std::sync::{Arc, Mutex};
use std::sync::mpsc::{Receiver, Sender, channel};
use std::thread;
use time::precise_time_s;

use id::Id;
use model::Model;
use object::Object;
use shader::Program;

/// Class of types that can be loaded.
pub trait Load<'a>: Sized {
  /// Arguments passed at loading.
  type Args;

  // TODO: see whether we can use something with From/Into instead, so that we can use lambdas.
  fn load<P>(path: P, cache: &mut Cache<'a>, args: Self::Args) -> Result<Self, LoadError> where P: AsRef<Path>;
}

/// Class of types that can be reloaded.
///
/// The idea is to simply recover the arguments used in `Load::load`.
pub trait Reload<'a>: Load<'a> {
  fn reload_args(&self) -> Self::Args;
}

/// Default implementation for types which are loaded without any arguments.
impl<'a, T> Reload<'a> for T where T: Load<'a, Args=()> {
  fn reload_args(&self) -> Self::Args {
    ()
  }
}

#[derive(Clone, Debug, Eq, PartialEq)]
pub enum LoadError {
  FileNotFound(PathBuf, String),
  ParseFailed(String),
  ConversionFailed(String)
}

type Timestamp = f64;

/// Time to await after a resource update to establish that it should be reloaded.
const UPDATE_AWAIT_TIME: Timestamp = 0.1; // 100ms

struct CacheBlock<'a, T> where T: 'a {
  data: Vec<(T, PathBuf, (Receiver<Timestamp>, f64))>,
  ids: HashMap<String, Id<'a, T>>,
}

impl<'a, T> CacheBlock<'a, T> {
  pub fn new() -> Self {
    CacheBlock {
      data: Vec::new(),
      ids: HashMap::new(),
    }
  }
}

macro_rules! cache_struct {
  ($l:tt, $($n:ident : $t:ty),*) => {
    pub struct Cache<$l> {
      senders: Arc<Mutex<HashMap<PathBuf, Sender<Timestamp>>>>,
      $(
        $n: CacheBlock<$l, $t>
      ),*
    }

    impl<$l> Cache<$l> {
      pub fn new<P>(root:P) -> Self where P: AsRef<Path> {
        let senders: Arc<Mutex<HashMap<PathBuf, Sender<Timestamp>>>> = Arc::new(Mutex::new(HashMap::new()));

        // start watcher thread
        {
          let senders = senders.clone();
          let root = root.as_ref().to_path_buf();
          let (wsx, wrx) = channel();
          let mut watcher: RecommendedWatcher = Watcher::new(wsx).unwrap();

          let _ = thread::spawn(move || {
            let _ = watcher.watch(root);

            for event in wrx.iter() {
              match event {
                notify::Event { path: Some(path), op: Ok(notify::op::WRITE) } => {
                  if let Some(sx) = senders.lock().unwrap().get(&path) {
                    sx.send(precise_time_s()).unwrap();
                  }
                },
                _ => {}
              }
            }
          });
        }

        Cache {
          senders: senders,
          $(
            $n: CacheBlock::new()
          ),*
        }
      }
    }
  }
}

pub trait Get<'a, T> where T: 'a + Reload<'a> {
  fn get_id(&mut self, name: &str, args: T::Args) -> Option<Id<'a, T>>;
  fn get_by_id(&mut self, id: &Id<'a, T>) -> Option<&T>;
  fn get(&mut self, name: &str, args: T::Args) -> Option<&T> {
    self.get_id(name, args).and_then(move |i| self.get_by_id(&i))
  }
}

macro_rules! impl_get_id {
  ($n:ident : $t:ty, $this:ident, $name:ident, $args: ident) => {{
    let path_str = format!("data/{}/{}", stringify!($n), $name);
    let path = Path::new(&path_str);

    match $this.$n.ids.get($name).cloned() {
      id@Some(..) => {
        deb!("cache hit for {}", path_str);
        id
      },
      None => {
        deb!("cache miss for {}", path_str);

        // specific loading
        if path.exists() {
          match <$t as Load>::load(&path, $this, $args) {
            Ok(resource) => {
              let path_buf = path.to_owned();

              // create the id if we have loaded the resource
              let id: Id<$t> = ($this.$n.data.len() as u32).into();

              // create a channel to notify any update later and register the sender for the
              // given path
              let (sx, rx) = channel();
              {
                let mut senders = $this.senders.lock().unwrap();
                senders.insert(path_buf.clone(), sx);
              }

              // add the resource to the list of loaded ones
              $this.$n.data.push((resource, path_buf.clone(), (rx, precise_time_s())));
              // cache the resource
              $this.$n.ids.insert($name.to_owned(), id.clone());

              Some(id)
            },
            Err(e) => {
              err!("unable to load resource from {}: {:?}", path_str, e);
              None
            }
          }
        } else { // path doesn’t exist
          err!("ressource at {} cannot be found", path_str);
          None
        }
      }
    }
  }}
}

macro_rules! impl_get_by_id {
  ($n:ident : $t:ty, $this:ident, $id:ident) => {{
    // synchronization
    let mut reload_args = None;

    if let Some(data) = $this.$n.data.get($id.id as usize) {
      match (data.2).0.try_recv() {
        Ok(timestamp) if timestamp - (data.2).1 >= UPDATE_AWAIT_TIME => {
          reload_args = Some((data.1.to_owned(), data.0.reload_args()));
        },
        _ => {}
      }
    } else {
      return None;
    }

    if let Some((path, args)) = reload_args {
      match <$t as Load>::load(&path, $this, args) {
        Ok(new_resource) => {
          // replace the current resource with the freshly loaded one
          deb!("reloaded resource from {:?}", path);
          $this.$n.data[$id.id as usize].0 = new_resource;
        },
        Err(e) => {
          warn!("reloading resource from {:?} has failed: {:?}", path, e);
        }
      }
    }

    $this.$n.data.get($id.id as usize).map(|r| &r.0)
  }}
}

macro_rules! impl_get_no_lifetime {
  ($n:ident : $t:ty) => {
    impl<'a> Get<'a, $t> for Cache<'a> {
      fn get_id(&mut self, name: &str, args: <$t as Load<'a>>::Args) -> Option<Id<'a, $t>> {
        impl_get_id!($n: $t, self, name, args)
      }

      fn get_by_id(&mut self, id: &Id<'a, $t>) -> Option<&$t> {
        impl_get_by_id!($n: $t, self, id)
      }
    }
  }
}

cache_struct!('a,
              models: Model,
              objects: Object<'a>,
              shader_programs: Program);

impl_get_no_lifetime!(models: Model);
impl_get_no_lifetime!(shader_programs: Program);

impl<'a> Get<'a, Object<'a>> for Cache<'a> {
  fn get_id(&mut self, name: &str, args: <Object<'a> as Load<'a>>::Args) -> Option<Id<'a, Object<'a>>> {
    impl_get_id!(objects: Object<'a>, self, name, args)
  }

  fn get_by_id(&mut self, id: &Id<'a, Object<'a>>) -> Option<&Object<'a>> {
    impl_get_by_id!(objects: Object<'a>, self, id)
  }
}
<commit_msg>Cache refactor.<commit_after>// FIXME: add the support of transient objects

use notify::{self, RecommendedWatcher, Watcher};
use std::collections::HashMap;
use std::path::{Path, PathBuf};
use std::sync::{Arc, Mutex};
use std::sync::mpsc::{Receiver, Sender, channel};
use std::thread;
use time::precise_time_s;

use id::Id;
use model::Model;
use object::Object;
use shader::Program;

/// Class of types that can be loaded.
pub trait Load<'a>: Sized {
  /// Arguments passed at loading.
  type Args;

  // TODO: see whether we can use something with From/Into instead, so that we can use lambdas.
  fn load<P>(path: P, cache: &mut Cache<'a>, args: Self::Args) -> Result<Self, LoadError> where P: AsRef<Path>;
}

/// Class of types that can be reloaded.
///
/// The idea is to simply recover the arguments used in `Load::load`.
pub trait Reload<'a>: Load<'a> {
  fn reload_args(&self) -> Self::Args;
}

/// Default implementation for types which are loaded without any arguments.
impl<'a, T> Reload<'a> for T where T: Load<'a, Args=()> {
  fn reload_args(&self) -> Self::Args {
    ()
  }
}

#[derive(Clone, Debug, Eq, PartialEq)]
pub enum LoadError {
  FileNotFound(PathBuf, String),
  ParseFailed(String),
  ConversionFailed(String)
}

type Timestamp = f64;

/// Time to await after a resource update to establish that it should be reloaded.
const UPDATE_AWAIT_TIME: Timestamp = 0.1; // 100ms

struct CacheBlock<'a, T> where T: 'a {
  data: Vec<(T, PathBuf, (Receiver<Timestamp>, f64))>,
  ids: HashMap<String, Id<'a, T>>,
}

impl<'a, T> CacheBlock<'a, T> {
  pub fn new() -> Self {
    CacheBlock {
      data: Vec::new(),
      ids: HashMap::new(),
    }
  }
}

macro_rules! cache_struct {
  ($l:tt, $($n:ident : $t:ty),*) => {
    pub struct Cache<$l> {
      senders: Arc<Mutex<HashMap<PathBuf, Sender<Timestamp>>>>,
      $(
        $n: CacheBlock<$l, $t>
      ),*
    }

    impl<$l> Cache<$l> {
      pub fn new<P>(root:P) -> Self where P: AsRef<Path> {
        let senders: Arc<Mutex<HashMap<PathBuf, Sender<Timestamp>>>> = Arc::new(Mutex::new(HashMap::new()));

        // start watcher thread
        {
          let senders = senders.clone();
          let root = root.as_ref().to_path_buf();
          let (wsx, wrx) = channel();
          let mut watcher: RecommendedWatcher = Watcher::new(wsx).unwrap();

          let _ = thread::spawn(move || {
            let _ = watcher.watch(root);

            for event in wrx.iter() {
              match event {
                notify::Event { path: Some(path), op: Ok(notify::op::WRITE) } => {
                  if let Some(sx) = senders.lock().unwrap().get(&path) {
                    sx.send(precise_time_s()).unwrap();
                  }
                },
                _ => {}
              }
            }
          });
        }

        Cache {
          senders: senders,
          $(
            $n: CacheBlock::new()
          ),*
        }
      }
    }
  }
}

pub trait Get<'a, T> where T: 'a + Reload<'a> {
  fn get_id(&mut self, name: &str, args: T::Args) -> Option<Id<'a, T>>;
  fn get_by_id(&mut self, id: &Id<'a, T>) -> Option<&T>;
  fn get(&mut self, name: &str, args: T::Args) -> Option<&T> {
    self.get_id(name, args).and_then(move |i| self.get_by_id(&i))
  }
}

macro_rules! impl_get_id {
  ($n:ident : $t:ty, $this:ident, $name:ident, $args: ident) => {{
    let path_str = format!("data/{}/{}", stringify!($n), $name);
    let path = Path::new(&path_str);

    match $this.$n.ids.get($name).cloned() {
      id@Some(..) => {
        deb!("cache hit for {}", path_str);
        id
      },
      None => {
        deb!("cache miss for {}", path_str);

        // specific loading
        if path.exists() {
          match <$t as Load>::load(&path, $this, $args) {
            Ok(resource) => {
              let path_buf = path.to_owned();

              // create the id if we have loaded the resource
              let id: Id<$t> = ($this.$n.data.len() as u32).into();

              // create a channel to notify any update later and register the sender for the
              // given path
              let (sx, rx) = channel();
              {
                let mut senders = $this.senders.lock().unwrap();
                senders.insert(path_buf.clone(), sx);
              }

              // add the resource to the list of loaded ones
              $this.$n.data.push((resource, path_buf.clone(), (rx, precise_time_s())));
              // cache the resource
              $this.$n.ids.insert($name.to_owned(), id.clone());

              Some(id)
            },
            Err(e) => {
              err!("unable to load resource from {}: {:?}", path_str, e);
              None
            }
          }
        } else { // path doesn’t exist
          err!("ressource at {} cannot be found", path_str);
          None
        }
      }
    }
  }}
}

macro_rules! impl_get_by_id {
  ($n:ident : $t:ty, $this:ident, $id:ident) => {{
    // synchronization
    let mut reload_args = None;

    if let Some(data) = $this.$n.data.get($id.id as usize) {
      match (data.2).0.try_recv() {
        Ok(timestamp) if timestamp - (data.2).1 >= UPDATE_AWAIT_TIME => {
          reload_args = Some((data.1.to_owned(), data.0.reload_args()));
        },
        _ => {}
      }
    } else {
      return None;
    }

    if let Some((path, args)) = reload_args {
      match <$t as Load>::load(&path, $this, args) {
        Ok(new_resource) => {
          // replace the current resource with the freshly loaded one
          deb!("reloaded resource from {:?}", path);
          $this.$n.data[$id.id as usize].0 = new_resource;
        },
        Err(e) => {
          warn!("reloading resource from {:?} has failed: {:?}", path, e);
        }
      }
    }

    $this.$n.data.get($id.id as usize).map(|r| &r.0)
  }}
}

macro_rules! impl_get_no_lifetime {
  ($n:ident : $t:ty) => {
    impl<'a> Get<'a, $t> for Cache<'a> {
      fn get_id(&mut self, name: &str, args: <$t as Load<'a>>::Args) -> Option<Id<'a, $t>> {
        impl_get_id!($n: $t, self, name, args)
      }

      fn get_by_id(&mut self, id: &Id<'a, $t>) -> Option<&$t> {
        impl_get_by_id!($n: $t, self, id)
      }
    }
  }
}

cache_struct!('a,
              models: Model,
              shader_programs: Program,
              objects: Object<'a>);

impl_get_no_lifetime!(models: Model);
impl_get_no_lifetime!(shader_programs: Program);

impl<'a> Get<'a, Object<'a>> for Cache<'a> {
  fn get_id(&mut self, name: &str, args: <Object<'a> as Load<'a>>::Args) -> Option<Id<'a, Object<'a>>> {
    impl_get_id!(objects: Object<'a>, self, name, args)
  }

  fn get_by_id(&mut self, id: &Id<'a, Object<'a>>) -> Option<&Object<'a>> {
    impl_get_by_id!(objects: Object<'a>, self, id)
  }
}
<|endoftext|>