<commit_before>#![allow(dead_code)]
#![feature(unsafe_no_drop_flag)]
#![feature(convert)]
#![feature(str_char)]
#![feature(plugin)]

#![plugin(clippy)]

extern crate rand;

#[macro_use]
extern crate log;

extern crate log4rs;

extern crate num_cpus;

extern crate rustc_serialize;

extern crate toml;

extern crate crossbeam;

extern crate tar;

#[cfg(test)]
extern crate quickcheck;

mod config;
mod player;
mod zobrist;
mod cell;
mod field;
mod wave_pruning;
mod trajectories_pruning;
mod common;
mod uct;
mod heuristic;
mod minimax;
mod bot;
mod dfa;
mod spiral;
mod patterns;

#[cfg(test)]
mod field_test;

use std::io;
use std::io::{Write, BufReader, BufRead};
use std::str::FromStr;
use std::path::Path;
use std::fs::File;
use std::sync::Arc;
use log4rs::toml::Creator;
use player::Player;
use bot::Bot;
use patterns::Patterns;

const CONFIG_PATH: &'static str = "config/config.toml";

const LOG_CONFIG_PATH: &'static str = "config/log.toml";

const PATTERNS_PATH: &'static str = "patterns.tar";

fn write_author<T: Write>(output: &mut T, id: u32) {
  writeln!(output, "= {0} author kurnevsky_evgeny", id).ok();
}

fn write_author_error<T: Write>(output: &mut T, id: u32) {
  writeln!(output, "? {0} author", id).ok();
}

fn write_init<T: Write>(output: &mut T, id: u32) {
  writeln!(output, "= {0} init", id).ok();
}

fn write_init_error<T: Write>(output: &mut T, id: u32) {
  writeln!(output, "? {0} init", id).ok();
}

fn write_gen_move<T: Write>(output: &mut T, id: u32, x: u32, y: u32, player: Player) {
  writeln!(output, "= {0} gen_move {1} {2} {3}", id, x, y, player.to_bool() as u32).ok();
}

fn write_gen_move_error<T: Write>(output: &mut T, id: u32) {
  writeln!(output, "? {0} gen_move", id).ok();
}

fn write_gen_move_with_complexity<T: Write>(output: &mut T, id: u32, x: u32, y: u32, player: Player) {
  writeln!(output, "= {0} gen_move_with_complexity {1} {2} {3}", id, x, y, player.to_bool() as u32).ok();
}

fn write_gen_move_with_complexity_error<T: Write>(output: &mut T, id: u32) {
  writeln!(output, "? {0} gen_move_with_complexity", id).ok();
}

fn write_gen_move_with_time<T: Write>(output: &mut T, id: u32, x: u32, y: u32, player: Player) {
  writeln!(output, "= {0} gen_move_with_time {1} {2} {3}", id, x, y, player.to_bool() as u32).ok();
}

fn write_gen_move_with_time_error<T: Write>(output: &mut T, id: u32) {
  writeln!(output, "? {0} gen_move_with_time", id).ok();
}

fn write_license<T: Write>(output: &mut T, id: u32) {
  writeln!(output, "= {0} license AGPLv3+", id).ok();
}

fn write_license_error<T: Write>(output: &mut T, id: u32) {
  writeln!(output, "? {0} license", id).ok();
}

fn write_list_commands<T: Write>(output: &mut T, id: u32) {
  writeln!(output, "= {0} list_commands gen_move gen_move_with_complexity gen_move_with_time init list_commands name play quit undo version", id).ok();
}

fn write_list_commands_error<T: Write>(output: &mut T, id: u32) {
  writeln!(output, "? {0} list_commands", id).ok();
}

fn write_name<T: Write>(output: &mut T, id: u32) {
  writeln!(output, "= {0} name opai-rust", id).ok();
}

fn write_name_error<T: Write>(output: &mut T, id: u32) {
  writeln!(output, "? {0} name", id).ok();
}

fn write_play<T: Write>(output: &mut T, id: u32, x: u32, y: u32, player: Player) {
  writeln!(output, "= {0} play {1} {2} {3}", id, x, y, player.to_bool() as u32).ok();
}

fn write_play_error<T: Write>(output: &mut T, id: u32) {
  writeln!(output, "? {0} play", id).ok();
}

fn write_quit<T: Write>(output: &mut T, id: u32) {
  writeln!(output, "= {0} quit", id).ok();
}

fn write_quit_error<T: Write>(output: &mut T, id: u32) {
  writeln!(output, "? {0} quit", id).ok();
}

fn write_undo<T: Write>(output: &mut T, id: u32) {
  writeln!(output, "= {0} undo", id).ok();
}

fn write_undo_error<T: Write>(output: &mut T, id: u32) {
  writeln!(output, "? {0} undo", id).ok();
}

fn write_version<T: Write>(output: &mut T, id: u32) {
  writeln!(output, "= {0} version {1}", id, env!("CARGO_PKG_VERSION")).ok();
}

fn write_version_error<T: Write>(output: &mut T, id: u32) {
  writeln!(output, "? {0} version", id).ok();
}

fn write_error<T: Write>(output: &mut T, id: u32) {
  writeln!(output, "? {0} input_error", id).ok();
}

fn main() {
  log4rs::init_file(Path::new(LOG_CONFIG_PATH), Creator::default()).ok();
  config::init();
  if let Some(mut config_file) = File::open(CONFIG_PATH).ok() {
    config::read(&mut config_file);
  } else if let Some(mut config_file) = File::create(CONFIG_PATH).ok() {
    config::write(&mut config_file);
  }
  let patterns = Arc::new(if let Some(patterns_file) = File::open(PATTERNS_PATH).ok() {
    Patterns::load(patterns_file)
  } else {
    Patterns::empty()
  });
  let mut input = BufReader::new(io::stdin());
  let mut output = io::stdout();
  let mut bot_option = None;
  let mut s = String::new();
  loop {
    s.clear();
    input.read_line(&mut s).ok();
    s.pop();
    let mut split = s.split(' ').fuse();
    if let Some(id) = split.next().and_then(|id_str| u32::from_str(id_str).ok()) {
      match split.next() {
        Some("author") => {
          if split.next().is_some() {
            write_author_error(&mut output, id);
          } else {
            write_author(&mut output, id);
          }
        },
        Some("init") => {
          let x_option = split.next().and_then(|x_str| u32::from_str(x_str).ok());
          let y_option = split.next().and_then(|y_str| u32::from_str(y_str).ok());
          let seed_option = split.next().and_then(|seed_str| u64::from_str(seed_str).ok());
          if split.next().is_some() {
            write_init_error(&mut output, id);
          } else if let (Some(x), Some(y), Some(seed)) = (x_option, y_option, seed_option) {
            bot_option = Some(Bot::new(x, y, seed, patterns.clone()));
            write_init(&mut output, id);
          } else {
            write_init_error(&mut output, id);
          }
        },
        Some("gen_move") => {
          let player_option = split.next().and_then(|player_str| u32::from_str(player_str).ok()).and_then(|player_u32| match player_u32 { //TODO: from_number method
            0 => Some(Player::Red),
            1 => Some(Player::Black),
            _ => None
          });
          if split.next().is_some() {
            write_gen_move_error(&mut output, id);
          } else if let (Some(player), Some(bot)) = (player_option, bot_option.as_mut()) {
            if let Some((x, y)) = bot.best_move(player) {
              write_gen_move(&mut output, id, x, y, player);
            } else {
              write_gen_move_error(&mut output, id);
            }
          } else {
            write_gen_move_error(&mut output, id);
          }
        },
        Some("gen_move_with_complexity") => {
          let player_option = split.next().and_then(|player_str| u32::from_str(player_str).ok()).and_then(|player_u32| match player_u32 { //TODO: from_number method
            0 => Some(Player::Red),
            1 => Some(Player::Black),
            _ => None
          });
          let complexity_option = split.next().and_then(|complexity_str| u32::from_str(complexity_str).ok() );
          if split.next().is_some() {
            write_gen_move_with_complexity_error(&mut output, id);
          } else if let (Some(player), Some(complexity), Some(bot)) = (player_option, complexity_option, bot_option.as_mut()) {
            if let Some((x, y)) = bot.best_move_with_complexity(player, complexity) {
              write_gen_move_with_complexity(&mut output, id, x, y, player);
            } else {
              write_gen_move_with_complexity_error(&mut output, id);
            }
          } else {
            write_gen_move_with_complexity_error(&mut output, id);
          }
        },
        Some("gen_move_with_time") => {
          let player_option = split.next().and_then(|player_str| u32::from_str(player_str).ok()).and_then(|player_u32| match player_u32 { //TODO: from_number method
            0 => Some(Player::Red),
            1 => Some(Player::Black),
            _ => None
          });
          let time_option = split.next().and_then(|time_str| u32::from_str(time_str).ok() );
          if split.next().is_some() {
            write_gen_move_with_time_error(&mut output, id);
          } else if let (Some(player), Some(time), Some(bot)) = (player_option, time_option, bot_option.as_mut()) {
            if let Some((x, y)) = bot.best_move_with_time(player, time) {
              write_gen_move_with_time(&mut output, id, x, y, player);
            } else {
              write_gen_move_with_time_error(&mut output, id);
            }
          } else {
            write_gen_move_with_time_error(&mut output, id);
          }
        },
        Some("license") => {
          if split.next().is_some() {
            write_license_error(&mut output, id);
          } else {
            write_license(&mut output, id);
          }
        },
        Some("list_commands") => {
          if split.next().is_some() {
            write_list_commands_error(&mut output, id);
          } else {
            write_list_commands(&mut output, id);
          }
        },
        Some("name") => {
          if split.next().is_some() {
            write_name_error(&mut output, id);
          } else {
            write_name(&mut output, id);
          }
        },
        Some("play") => {
          let x_option = split.next().and_then(|x_str| u32::from_str(x_str).ok());
          let y_option = split.next().and_then(|y_str| u32::from_str(y_str).ok());
          let player_option = split.next().and_then(|player_str| u32::from_str(player_str).ok()).and_then(|player_u32| match player_u32 { //TODO: from_number method
            0 => Some(Player::Red),
            1 => Some(Player::Black),
            _ => None
          });
          if split.next().is_some() {
            write_play_error(&mut output, id);
          } else if let (Some(x), Some(y), Some(player), Some(bot)) = (x_option, y_option, player_option, bot_option.as_mut()) {
            if bot.put_point(x, y, player) {
              write_play(&mut output, id, x, y, player);
            } else {
              write_play_error(&mut output, id);
            }
          } else {
            write_play_error(&mut output, id);
          }
        },
        Some("quit") => {
          if split.next().is_some() {
            write_quit_error(&mut output, id);
          } else {
            write_quit(&mut output, id);
            output.flush().ok();
            break;
          }
        },
        Some("undo") => {
          if split.next().is_some() {
            write_undo_error(&mut output, id);
          } else if let Some(bot) = bot_option.as_mut() {
            if bot.undo() {
              write_undo(&mut output, id);
            } else {
              write_undo_error(&mut output, id);
            }
          } else {
            write_undo_error(&mut output, id);
          }
        },
        Some("version") => {
          if split.next().is_some() {
            write_version_error(&mut output, id);
          } else {
            write_version(&mut output, id);
          }
        },
        _ => {
          write_error(&mut output, id);
        }
      }
    } else {
      write_error(&mut output, 0);
    }
    output.flush().ok();
  }
}
<commit_msg>Disable clippy warning.<commit_after>#![allow(dead_code)]
#![feature(unsafe_no_drop_flag)]
#![feature(convert)]
#![feature(str_char)]
#![feature(plugin)]

#![plugin(clippy)]
#![allow(float_cmp)]

extern crate rand;

#[macro_use]
extern crate log;

extern crate log4rs;

extern crate num_cpus;

extern crate rustc_serialize;

extern crate toml;

extern crate crossbeam;

extern crate tar;

#[cfg(test)]
extern crate quickcheck;

mod config;
mod player;
mod zobrist;
mod cell;
mod field;
mod wave_pruning;
mod trajectories_pruning;
mod common;
mod uct;
mod heuristic;
mod minimax;
mod bot;
mod dfa;
mod spiral;
mod patterns;

#[cfg(test)]
mod field_test;

use std::io;
use std::io::{Write, BufReader, BufRead};
use std::str::FromStr;
use std::path::Path;
use std::fs::File;
use std::sync::Arc;
use log4rs::toml::Creator;
use player::Player;
use bot::Bot;
use patterns::Patterns;

const CONFIG_PATH: &'static str = "config/config.toml";

const LOG_CONFIG_PATH: &'static str = "config/log.toml";

const PATTERNS_PATH: &'static str = "patterns.tar";

fn write_author<T: Write>(output: &mut T, id: u32) {
  writeln!(output, "= {0} author kurnevsky_evgeny", id).ok();
}

fn write_author_error<T: Write>(output: &mut T, id: u32) {
  writeln!(output, "? {0} author", id).ok();
}

fn write_init<T: Write>(output: &mut T, id: u32) {
  writeln!(output, "= {0} init", id).ok();
}

fn write_init_error<T: Write>(output: &mut T, id: u32) {
  writeln!(output, "? {0} init", id).ok();
}

fn write_gen_move<T: Write>(output: &mut T, id: u32, x: u32, y: u32, player: Player) {
  writeln!(output, "= {0} gen_move {1} {2} {3}", id, x, y, player.to_bool() as u32).ok();
}

fn write_gen_move_error<T: Write>(output: &mut T, id: u32) {
  writeln!(output, "? {0} gen_move", id).ok();
}

fn write_gen_move_with_complexity<T: Write>(output: &mut T, id: u32, x: u32, y: u32, player: Player) {
  writeln!(output, "= {0} gen_move_with_complexity {1} {2} {3}", id, x, y, player.to_bool() as u32).ok();
}

fn write_gen_move_with_complexity_error<T: Write>(output: &mut T, id: u32) {
  writeln!(output, "? {0} gen_move_with_complexity", id).ok();
}

fn write_gen_move_with_time<T: Write>(output: &mut T, id: u32, x: u32, y: u32, player: Player) {
  writeln!(output, "= {0} gen_move_with_time {1} {2} {3}", id, x, y, player.to_bool() as u32).ok();
}

fn write_gen_move_with_time_error<T: Write>(output: &mut T, id: u32) {
  writeln!(output, "? {0} gen_move_with_time", id).ok();
}

fn write_license<T: Write>(output: &mut T, id: u32) {
  writeln!(output, "= {0} license AGPLv3+", id).ok();
}

fn write_license_error<T: Write>(output: &mut T, id: u32) {
  writeln!(output, "? {0} license", id).ok();
}

fn write_list_commands<T: Write>(output: &mut T, id: u32) {
  writeln!(output, "= {0} list_commands gen_move gen_move_with_complexity gen_move_with_time init list_commands name play quit undo version", id).ok();
}

fn write_list_commands_error<T: Write>(output: &mut T, id: u32) {
  writeln!(output, "? {0} list_commands", id).ok();
}

fn write_name<T: Write>(output: &mut T, id: u32) {
  writeln!(output, "= {0} name opai-rust", id).ok();
}

fn write_name_error<T: Write>(output: &mut T, id: u32) {
  writeln!(output, "? {0} name", id).ok();
}

fn write_play<T: Write>(output: &mut T, id: u32, x: u32, y: u32, player: Player) {
  writeln!(output, "= {0} play {1} {2} {3}", id, x, y, player.to_bool() as u32).ok();
}

fn write_play_error<T: Write>(output: &mut T, id: u32) {
  writeln!(output, "? {0} play", id).ok();
}

fn write_quit<T: Write>(output: &mut T, id: u32) {
  writeln!(output, "= {0} quit", id).ok();
}

fn write_quit_error<T: Write>(output: &mut T, id: u32) {
  writeln!(output, "? {0} quit", id).ok();
}

fn write_undo<T: Write>(output: &mut T, id: u32) {
  writeln!(output, "= {0} undo", id).ok();
}

fn write_undo_error<T: Write>(output: &mut T, id: u32) {
  writeln!(output, "? {0} undo", id).ok();
}

fn write_version<T: Write>(output: &mut T, id: u32) {
  writeln!(output, "= {0} version {1}", id, env!("CARGO_PKG_VERSION")).ok();
}

fn write_version_error<T: Write>(output: &mut T, id: u32) {
  writeln!(output, "? {0} version", id).ok();
}

fn write_error<T: Write>(output: &mut T, id: u32) {
  writeln!(output, "? {0} input_error", id).ok();
}

fn main() {
  log4rs::init_file(Path::new(LOG_CONFIG_PATH), Creator::default()).ok();
  config::init();
  if let Some(mut config_file) = File::open(CONFIG_PATH).ok() {
    config::read(&mut config_file);
  } else if let Some(mut config_file) = File::create(CONFIG_PATH).ok() {
    config::write(&mut config_file);
  }
  let patterns = Arc::new(if let Some(patterns_file) = File::open(PATTERNS_PATH).ok() {
    Patterns::load(patterns_file)
  } else {
    Patterns::empty()
  });
  let mut input = BufReader::new(io::stdin());
  let mut output = io::stdout();
  let mut bot_option = None;
  let mut s = String::new();
  loop {
    s.clear();
    input.read_line(&mut s).ok();
    s.pop();
    let mut split = s.split(' ').fuse();
    if let Some(id) = split.next().and_then(|id_str| u32::from_str(id_str).ok()) {
      match split.next() {
        Some("author") => {
          if split.next().is_some() {
            write_author_error(&mut output, id);
          } else {
            write_author(&mut output, id);
          }
        },
        Some("init") => {
          let x_option = split.next().and_then(|x_str| u32::from_str(x_str).ok());
          let y_option = split.next().and_then(|y_str| u32::from_str(y_str).ok());
          let seed_option = split.next().and_then(|seed_str| u64::from_str(seed_str).ok());
          if split.next().is_some() {
            write_init_error(&mut output, id);
          } else if let (Some(x), Some(y), Some(seed)) = (x_option, y_option, seed_option) {
            bot_option = Some(Bot::new(x, y, seed, patterns.clone()));
            write_init(&mut output, id);
          } else {
            write_init_error(&mut output, id);
          }
        },
        Some("gen_move") => {
          let player_option = split.next().and_then(|player_str| u32::from_str(player_str).ok()).and_then(|player_u32| match player_u32 { //TODO: from_number method
            0 => Some(Player::Red),
            1 => Some(Player::Black),
            _ => None
          });
          if split.next().is_some() {
            write_gen_move_error(&mut output, id);
          } else if let (Some(player), Some(bot)) = (player_option, bot_option.as_mut()) {
            if let Some((x, y)) = bot.best_move(player) {
              write_gen_move(&mut output, id, x, y, player);
            } else {
              write_gen_move_error(&mut output, id);
            }
          } else {
            write_gen_move_error(&mut output, id);
          }
        },
        Some("gen_move_with_complexity") => {
          let player_option = split.next().and_then(|player_str| u32::from_str(player_str).ok()).and_then(|player_u32| match player_u32 { //TODO: from_number method
            0 => Some(Player::Red),
            1 => Some(Player::Black),
            _ => None
          });
          let complexity_option = split.next().and_then(|complexity_str| u32::from_str(complexity_str).ok() );
          if split.next().is_some() {
            write_gen_move_with_complexity_error(&mut output, id);
          } else if let (Some(player), Some(complexity), Some(bot)) = (player_option, complexity_option, bot_option.as_mut()) {
            if let Some((x, y)) = bot.best_move_with_complexity(player, complexity) {
              write_gen_move_with_complexity(&mut output, id, x, y, player);
            } else {
              write_gen_move_with_complexity_error(&mut output, id);
            }
          } else {
            write_gen_move_with_complexity_error(&mut output, id);
          }
        },
        Some("gen_move_with_time") => {
          let player_option = split.next().and_then(|player_str| u32::from_str(player_str).ok()).and_then(|player_u32| match player_u32 { //TODO: from_number method
            0 => Some(Player::Red),
            1 => Some(Player::Black),
            _ => None
          });
          let time_option = split.next().and_then(|time_str| u32::from_str(time_str).ok() );
          if split.next().is_some() {
            write_gen_move_with_time_error(&mut output, id);
          } else if let (Some(player), Some(time), Some(bot)) = (player_option, time_option, bot_option.as_mut()) {
            if let Some((x, y)) = bot.best_move_with_time(player, time) {
              write_gen_move_with_time(&mut output, id, x, y, player);
            } else {
              write_gen_move_with_time_error(&mut output, id);
            }
          } else {
            write_gen_move_with_time_error(&mut output, id);
          }
        },
        Some("license") => {
          if split.next().is_some() {
            write_license_error(&mut output, id);
          } else {
            write_license(&mut output, id);
          }
        },
        Some("list_commands") => {
          if split.next().is_some() {
            write_list_commands_error(&mut output, id);
          } else {
            write_list_commands(&mut output, id);
          }
        },
        Some("name") => {
          if split.next().is_some() {
            write_name_error(&mut output, id);
          } else {
            write_name(&mut output, id);
          }
        },
        Some("play") => {
          let x_option = split.next().and_then(|x_str| u32::from_str(x_str).ok());
          let y_option = split.next().and_then(|y_str| u32::from_str(y_str).ok());
          let player_option = split.next().and_then(|player_str| u32::from_str(player_str).ok()).and_then(|player_u32| match player_u32 { //TODO: from_number method
            0 => Some(Player::Red),
            1 => Some(Player::Black),
            _ => None
          });
          if split.next().is_some() {
            write_play_error(&mut output, id);
          } else if let (Some(x), Some(y), Some(player), Some(bot)) = (x_option, y_option, player_option, bot_option.as_mut()) {
            if bot.put_point(x, y, player) {
              write_play(&mut output, id, x, y, player);
            } else {
              write_play_error(&mut output, id);
            }
          } else {
            write_play_error(&mut output, id);
          }
        },
        Some("quit") => {
          if split.next().is_some() {
            write_quit_error(&mut output, id);
          } else {
            write_quit(&mut output, id);
            output.flush().ok();
            break;
          }
        },
        Some("undo") => {
          if split.next().is_some() {
            write_undo_error(&mut output, id);
          } else if let Some(bot) = bot_option.as_mut() {
            if bot.undo() {
              write_undo(&mut output, id);
            } else {
              write_undo_error(&mut output, id);
            }
          } else {
            write_undo_error(&mut output, id);
          }
        },
        Some("version") => {
          if split.next().is_some() {
            write_version_error(&mut output, id);
          } else {
            write_version(&mut output, id);
          }
        },
        _ => {
          write_error(&mut output, id);
        }
      }
    } else {
      write_error(&mut output, 0);
    }
    output.flush().ok();
  }
}
<|endoftext|>


<commit_before>use clap::Clap;
use std::cmp::Ordering;
use std::fs::File;

fn normalize_col(col: &str) -> String {
    lazy_static::lazy_static! {
        static ref RE: regex::Regex = regex::Regex::new(r"\(.*?\)$").unwrap();
    }
    let mut col = RE
        .replace_all(col, "")
        .to_lowercase()
        .trim()
        .replace("(", "")
        .replace(")", "")
        .replace(" ", "_")
        .replace(".", "_")
        .replace("-", "_")
        .replace("/", "_")
        .replace("?", "")
        .replace(",", "_")
        .replace("&", "_")
        .replace(":", "")
        .replace("#", "");
    if !col.chars().next().map(char::is_alphabetic).unwrap_or(true) {
        col = format!("c_{}", col)
    }
    col
}

fn _create_table(db: &mut rusqlite::Connection, table_name: &str, cols: &[String]) {
    let create_columns = cols
        .iter()
        .map(|c| format!("{} varchar", c))
        .collect::<Vec<String>>()
        .join(", ");
    db.execute(
        &format!("CREATE TABLE {} ({})", table_name, create_columns),
        &[] as &[&dyn rusqlite::types::ToSql],
    )
    .unwrap();
}

fn _load_table_from_path(
    db: &mut rusqlite::Connection,
    table_name: &str,
    path: &str,
    delimiter: u8,
) -> Result<Vec<String>, Box<dyn std::error::Error>> {
    let mut num_rows = 0;
    let f = File::open(path)?;
    let file_size = f.metadata().unwrap().len();
    let mut reader = csv::ReaderBuilder::new()
        .flexible(true)
        .delimiter(delimiter)
        .from_reader(f);

    let normalized_cols =
        reader
            .headers()?
            .iter()
            .map(normalize_col)
            .fold(vec![], |mut v, orig_col| {
                let mut col = orig_col.clone();
                let mut i = 1;
                while v.contains(&col) {
                    col = format!("{}_{}", orig_col, i);
                    i += 1
                }
                v.push(col);
                v
            });
    _create_table(db, table_name, &normalized_cols);

    let insert_query = format!(
        "INSERT INTO {} VALUES ({})",
        table_name,
        normalized_cols
            .iter()
            .map(|_| "?")
            .collect::<Vec<_>>()
            .join(", ")
    );
    let pb = indicatif::ProgressBar::new(file_size);
    pb.set_style(
        indicatif::ProgressStyle::default_bar()
            .template("[{elapsed_precise}] [{wide_bar:.cyan/blue}] {bytes}/{total_bytes} ({eta})")
            .progress_chars("#>-"),
    );
    let mut records = reader.byte_records();
    let tx = db.transaction().unwrap();
    {
        let mut stmt = tx.prepare(&insert_query).expect("tx.prepare() failed");
        while let Some(row) = records.next() {
            let mut row = row?;
            match row.len().cmp(&normalized_cols.len()) {
                Ordering::Less => {
                    for _ in 0..normalized_cols.len() - row.len() {
                        row.push_field(b"");
                    }
                }
                Ordering::Greater => {
                    panic!("Too many fields on row {}, fields: {:?}", num_rows + 1, row);
                }
                Ordering::Equal => {}
            }
            stmt.execute(rusqlite::params_from_iter(
                row.iter().map(String::from_utf8_lossy),
            ))
            .unwrap();

            num_rows += 1;
            if num_rows % 10000 == 0 {
                pb.set_position(records.reader().position().byte())
            }
        }
    }
    tx.commit().unwrap();
    pb.finish();

    println!(
        "Loaded {} rows into {}({}) from {:?}",
        num_rows,
        table_name,
        normalized_cols.join(", "),
        path,
    );
    Ok(normalized_cols)
}

struct FromAnySqlType {
    value: String,
}

impl rusqlite::types::FromSql for FromAnySqlType {
    fn column_result(
        value: rusqlite::types::ValueRef<'_>,
    ) -> Result<FromAnySqlType, rusqlite::types::FromSqlError> {
        let result = match value {
            rusqlite::types::ValueRef::Null => "null".to_string(),
            rusqlite::types::ValueRef::Integer(v) => v.to_string(),
            rusqlite::types::ValueRef::Real(v) => v.to_string(),
            rusqlite::types::ValueRef::Blob(v) | rusqlite::types::ValueRef::Text(v) => {
                String::from_utf8(v.to_vec()).unwrap()
            }
        };
        Ok(FromAnySqlType { value: result })
    }
}

fn _prepare_query<'a>(
    conn: &'a mut rusqlite::Connection,
    query: &str,
) -> Result<rusqlite::Statement<'a>, String> {
    conn.prepare(query).map_err(|e| e.to_string())
}

fn _handle_query(conn: &mut rusqlite::Connection, line: &str) -> Result<(), String> {
    let mut stmt = _prepare_query(conn, line)?;

    let mut table = comfy_table::Table::new();
    table.load_preset("││──╞═╪╡┆    ┬┴┌┐└┘");
    table.set_content_arrangement(comfy_table::ContentArrangement::Dynamic);
    let mut title_row = comfy_table::Row::new();
    for col in stmt.column_names() {
        title_row.add_cell(comfy_table::Cell::new(col));
    }
    table.set_header(title_row);

    let mut results = stmt.query(&[] as &[&dyn rusqlite::types::ToSql]).unwrap();
    while let Ok(Some(r)) = results.next() {
        let mut row = comfy_table::Row::new();
        for i in 0..r.column_count() {
            let cell: FromAnySqlType = r.get(i).unwrap();
            row.add_cell(comfy_table::Cell::new(&cell.value));
        }
        table.add_row(row);
    }
    println!("{}", table);
    Ok(())
}

fn _handle_export(conn: &mut rusqlite::Connection, line: &str) -> Result<(), String> {
    lazy_static::lazy_static! {
        static ref RE: regex::Regex = regex::Regex::new(r"^\.export\(([\w_\-\./]+)\) (.*)").unwrap();
    }
    let caps = RE
        .captures(line)
        .ok_or_else(|| "Must match `.export(file-name) SQL`".to_owned())?;
    let destination_path = &caps[1];
    let query = &caps[2];

    let mut stmt = _prepare_query(conn, query)?;

    let mut writer = csv::Writer::from_path(destination_path).map_err(|e| format!("{:?}", e))?;
    writer.write_record(stmt.column_names()).unwrap();

    let mut results = stmt.query(&[] as &[&dyn rusqlite::types::ToSql]).unwrap();
    while let Ok(Some(r)) = results.next() {
        writer
            .write_record((0..r.column_count()).map(|i| {
                let cell: FromAnySqlType = r.get(i).unwrap();
                cell.value
            }))
            .unwrap();
    }

    Ok(())
}

fn _process_query(conn: &mut rusqlite::Connection, line: &str) {
    let result = if line.starts_with(".export") {
        _handle_export(conn, line)
    } else if line.starts_with(".schema") {
        _handle_query(
            conn,
            "SELECT sql AS schema FROM sqlite_master WHERE name like 't%'",
        )
    } else {
        _handle_query(conn, line)
    };
    if let Err(e) = result {
        println!("{}", e);
    }
}

fn install_udfs(c: &mut rusqlite::Connection) -> Result<(), Box<dyn std::error::Error>> {
    c.create_scalar_function(
        "regexp_extract",
        3,
        rusqlite::functions::FunctionFlags::SQLITE_UTF8
            | rusqlite::functions::FunctionFlags::SQLITE_DETERMINISTIC,
        |ctx| {
            let re = ctx.get_or_create_aux(
                0,
                |vr| -> Result<_, Box<dyn std::error::Error + Send + Sync + 'static>> {
                    Ok(regex::Regex::new(vr.as_str()?)?)
                },
            )?;
            let value = ctx.get::<Box<str>>(1)?;
            let replacement = ctx.get::<Box<str>>(2)?;

            let caps = match re.captures(&value) {
                Some(caps) => caps,
                None => return Ok("".to_string()),
            };
            let mut dest = String::new();
            caps.expand(&replacement, &mut dest);
            Ok(dest)
        },
    )?;
    c.create_scalar_function(
        "regexp",
        2,
        rusqlite::functions::FunctionFlags::SQLITE_UTF8
            | rusqlite::functions::FunctionFlags::SQLITE_DETERMINISTIC,
        |ctx| {
            let re = ctx.get_or_create_aux(
                0,
                |vr| -> Result<_, Box<dyn std::error::Error + Send + Sync + 'static>> {
                    Ok(regex::Regex::new(vr.as_str()?)?)
                },
            )?;
            let value = ctx.get::<Box<str>>(1)?;
            Ok(re.is_match(&value))
        },
    )?;

    Ok(())
}

struct SimpleWordCompleter {
    words: Vec<String>,
}

static BREAK_CHARS: [u8; 5] = [b' ', b'(', b')', b',', b'.'];
impl SimpleWordCompleter {
    fn new(words: Vec<String>) -> SimpleWordCompleter {
        SimpleWordCompleter { words }
    }
}

impl rustyline::Helper for SimpleWordCompleter {}

impl rustyline::hint::Hinter for SimpleWordCompleter {
    type Hint = String;

    fn hint(&self, _line: &str, _pos: usize, _ctx: &rustyline::Context<'_>) -> Option<String> {
        None
    }
}

impl rustyline::highlight::Highlighter for SimpleWordCompleter {}

impl rustyline::validate::Validator for SimpleWordCompleter {}

impl rustyline::completion::Completer for SimpleWordCompleter {
    type Candidate = String;

    fn complete(
        &self,
        line: &str,
        pos: usize,
        _ctx: &rustyline::Context<'_>,
    ) -> rustyline::Result<(usize, Vec<String>)> {
        let (start, word) = rustyline::completion::extract_word(line, pos, None, &BREAK_CHARS);

        let matches = self
            .words
            .iter()
            .filter(|w| w.starts_with(word))
            .cloned()
            .collect();
        Ok((start, matches))
    }
}

#[derive(Clap)]
struct Opts {
    #[clap(long, about = "Use ',' as the delimiter for the CSV")]
    comma: bool,
    #[clap(long, about = "Use '|' as the delimiter for the CSV")]
    pipe: bool,
    #[clap(long, about = "Use '\\t' as the delimiter for the CSV")]
    tab: bool,
    #[clap(long, about = "Use ';' as the delimiter for the CSV")]
    semicolon: bool,

    #[clap()]
    paths: Vec<String>,
}

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let opts = Opts::parse();

    let delim = match (opts.comma, opts.pipe, opts.tab, opts.semicolon) {
        (true, false, false, false) | (false, false, false, false) => b',',
        (false, true, false, false) => b'|',
        (false, false, true, false) => b'\t',
        (false, false, false, true) => b';',
        _ => {
            eprintln!("Can't pass more than one of --comma, --pipe, and --tab");
            std::process::exit(1);
        }
    };

    let mut conn = rusqlite::Connection::open_in_memory().unwrap();

    let mut base_words = [
        // keywords
        "distinct",
        "select",
        "from",
        "group",
        "by",
        "order",
        "where",
        "count",
        "limit",
        "offset",
        // functions
        "length",
        "coalesce",
        "regexp_extract",
        // csv-sql commands
        "export",
        "schema",
    ]
    .iter()
    .map(|&s| s.to_string())
    .collect::<Vec<String>>();

    if opts.paths.len() == 1 {
        let mut col_names = _load_table_from_path(&mut conn, "t", &opts.paths[0], delim)?;
        base_words.append(&mut col_names);
    } else {
        for (idx, path) in opts.paths.iter().enumerate() {
            let mut col_names =
                _load_table_from_path(&mut conn, &format!("t{}", idx + 1), path, delim)?;
            base_words.append(&mut col_names);
        }
    }

    install_udfs(&mut conn)?;

    let completer = SimpleWordCompleter::new(base_words);
    let mut rl = rustyline::Editor::new();
    rl.set_helper(Some(completer));
    let history_path = dirs::home_dir().unwrap().join(".csv-sql-history");
    let _ = rl.load_history(&history_path);
    loop {
        match rl.readline("> ") {
            Ok(line) => {
                if line.trim().is_empty() {
                    continue;
                }
                _process_query(&mut conn, &line);
                rl.add_history_entry(line);
            }
            Err(rustyline::error::ReadlineError::Interrupted) => {
                println!("Interrupted");
                continue;
            }
            Err(rustyline::error::ReadlineError::Eof) => {
                break;
            }
            Err(err) => {
                println!("Error: {}", err);
                break;
            }
        }
    }
    rl.save_history(&history_path).unwrap();

    Ok(())
}

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

    #[test]
    fn test_normalize_col() {
        for (value, expected) in &[
            ("", ""),
            ("abc", "abc"),
            ("(S)AO", "sao"),
            ("abc (123)", "abc"),
            ("2/6/2000", "c_2_6_2000"),
            ("COMBO#", "combo"),
        ] {
            assert_eq!(&&normalize_col(value), expected);
        }
    }
}
<commit_msg>Use column count from stmt, in prep for rusqlite update<commit_after>use clap::Clap;
use std::cmp::Ordering;
use std::fs::File;

fn normalize_col(col: &str) -> String {
    lazy_static::lazy_static! {
        static ref RE: regex::Regex = regex::Regex::new(r"\(.*?\)$").unwrap();
    }
    let mut col = RE
        .replace_all(col, "")
        .to_lowercase()
        .trim()
        .replace("(", "")
        .replace(")", "")
        .replace(" ", "_")
        .replace(".", "_")
        .replace("-", "_")
        .replace("/", "_")
        .replace("?", "")
        .replace(",", "_")
        .replace("&", "_")
        .replace(":", "")
        .replace("#", "");
    if !col.chars().next().map(char::is_alphabetic).unwrap_or(true) {
        col = format!("c_{}", col)
    }
    col
}

fn _create_table(db: &mut rusqlite::Connection, table_name: &str, cols: &[String]) {
    let create_columns = cols
        .iter()
        .map(|c| format!("{} varchar", c))
        .collect::<Vec<String>>()
        .join(", ");
    db.execute(
        &format!("CREATE TABLE {} ({})", table_name, create_columns),
        &[] as &[&dyn rusqlite::types::ToSql],
    )
    .unwrap();
}

fn _load_table_from_path(
    db: &mut rusqlite::Connection,
    table_name: &str,
    path: &str,
    delimiter: u8,
) -> Result<Vec<String>, Box<dyn std::error::Error>> {
    let mut num_rows = 0;
    let f = File::open(path)?;
    let file_size = f.metadata().unwrap().len();
    let mut reader = csv::ReaderBuilder::new()
        .flexible(true)
        .delimiter(delimiter)
        .from_reader(f);

    let normalized_cols =
        reader
            .headers()?
            .iter()
            .map(normalize_col)
            .fold(vec![], |mut v, orig_col| {
                let mut col = orig_col.clone();
                let mut i = 1;
                while v.contains(&col) {
                    col = format!("{}_{}", orig_col, i);
                    i += 1
                }
                v.push(col);
                v
            });
    _create_table(db, table_name, &normalized_cols);

    let insert_query = format!(
        "INSERT INTO {} VALUES ({})",
        table_name,
        normalized_cols
            .iter()
            .map(|_| "?")
            .collect::<Vec<_>>()
            .join(", ")
    );
    let pb = indicatif::ProgressBar::new(file_size);
    pb.set_style(
        indicatif::ProgressStyle::default_bar()
            .template("[{elapsed_precise}] [{wide_bar:.cyan/blue}] {bytes}/{total_bytes} ({eta})")
            .progress_chars("#>-"),
    );
    let mut records = reader.byte_records();
    let tx = db.transaction().unwrap();
    {
        let mut stmt = tx.prepare(&insert_query).expect("tx.prepare() failed");
        while let Some(row) = records.next() {
            let mut row = row?;
            match row.len().cmp(&normalized_cols.len()) {
                Ordering::Less => {
                    for _ in 0..normalized_cols.len() - row.len() {
                        row.push_field(b"");
                    }
                }
                Ordering::Greater => {
                    panic!("Too many fields on row {}, fields: {:?}", num_rows + 1, row);
                }
                Ordering::Equal => {}
            }
            stmt.execute(rusqlite::params_from_iter(
                row.iter().map(String::from_utf8_lossy),
            ))
            .unwrap();

            num_rows += 1;
            if num_rows % 10000 == 0 {
                pb.set_position(records.reader().position().byte())
            }
        }
    }
    tx.commit().unwrap();
    pb.finish();

    println!(
        "Loaded {} rows into {}({}) from {:?}",
        num_rows,
        table_name,
        normalized_cols.join(", "),
        path,
    );
    Ok(normalized_cols)
}

struct FromAnySqlType {
    value: String,
}

impl rusqlite::types::FromSql for FromAnySqlType {
    fn column_result(
        value: rusqlite::types::ValueRef<'_>,
    ) -> Result<FromAnySqlType, rusqlite::types::FromSqlError> {
        let result = match value {
            rusqlite::types::ValueRef::Null => "null".to_string(),
            rusqlite::types::ValueRef::Integer(v) => v.to_string(),
            rusqlite::types::ValueRef::Real(v) => v.to_string(),
            rusqlite::types::ValueRef::Blob(v) | rusqlite::types::ValueRef::Text(v) => {
                String::from_utf8(v.to_vec()).unwrap()
            }
        };
        Ok(FromAnySqlType { value: result })
    }
}

fn _prepare_query<'a>(
    conn: &'a mut rusqlite::Connection,
    query: &str,
) -> Result<rusqlite::Statement<'a>, String> {
    conn.prepare(query).map_err(|e| e.to_string())
}

fn _handle_query(conn: &mut rusqlite::Connection, line: &str) -> Result<(), String> {
    let mut stmt = _prepare_query(conn, line)?;
    let col_count = stmt.column_count();

    let mut table = comfy_table::Table::new();
    table.load_preset("││──╞═╪╡┆    ┬┴┌┐└┘");
    table.set_content_arrangement(comfy_table::ContentArrangement::Dynamic);
    let mut title_row = comfy_table::Row::new();
    for col in stmt.column_names() {
        title_row.add_cell(comfy_table::Cell::new(col));
    }
    table.set_header(title_row);

    let mut results = stmt.query(&[] as &[&dyn rusqlite::types::ToSql]).unwrap();
    while let Ok(Some(r)) = results.next() {
        let mut row = comfy_table::Row::new();
        for i in 0..col_count {
            let cell: FromAnySqlType = r.get(i).unwrap();
            row.add_cell(comfy_table::Cell::new(&cell.value));
        }
        table.add_row(row);
    }
    println!("{}", table);
    Ok(())
}

fn _handle_export(conn: &mut rusqlite::Connection, line: &str) -> Result<(), String> {
    lazy_static::lazy_static! {
        static ref RE: regex::Regex = regex::Regex::new(r"^\.export\(([\w_\-\./]+)\) (.*)").unwrap();
    }
    let caps = RE
        .captures(line)
        .ok_or_else(|| "Must match `.export(file-name) SQL`".to_owned())?;
    let destination_path = &caps[1];
    let query = &caps[2];

    let mut stmt = _prepare_query(conn, query)?;
    let col_count = stmt.column_count();

    let mut writer = csv::Writer::from_path(destination_path).map_err(|e| format!("{:?}", e))?;
    writer.write_record(stmt.column_names()).unwrap();

    let mut results = stmt.query(&[] as &[&dyn rusqlite::types::ToSql]).unwrap();
    while let Ok(Some(r)) = results.next() {
        writer
            .write_record((0..col_count).map(|i| {
                let cell: FromAnySqlType = r.get(i).unwrap();
                cell.value
            }))
            .unwrap();
    }

    Ok(())
}

fn _process_query(conn: &mut rusqlite::Connection, line: &str) {
    let result = if line.starts_with(".export") {
        _handle_export(conn, line)
    } else if line.starts_with(".schema") {
        _handle_query(
            conn,
            "SELECT sql AS schema FROM sqlite_master WHERE name like 't%'",
        )
    } else {
        _handle_query(conn, line)
    };
    if let Err(e) = result {
        println!("{}", e);
    }
}

fn install_udfs(c: &mut rusqlite::Connection) -> Result<(), Box<dyn std::error::Error>> {
    c.create_scalar_function(
        "regexp_extract",
        3,
        rusqlite::functions::FunctionFlags::SQLITE_UTF8
            | rusqlite::functions::FunctionFlags::SQLITE_DETERMINISTIC,
        |ctx| {
            let re = ctx.get_or_create_aux(
                0,
                |vr| -> Result<_, Box<dyn std::error::Error + Send + Sync + 'static>> {
                    Ok(regex::Regex::new(vr.as_str()?)?)
                },
            )?;
            let value = ctx.get::<Box<str>>(1)?;
            let replacement = ctx.get::<Box<str>>(2)?;

            let caps = match re.captures(&value) {
                Some(caps) => caps,
                None => return Ok("".to_string()),
            };
            let mut dest = String::new();
            caps.expand(&replacement, &mut dest);
            Ok(dest)
        },
    )?;
    c.create_scalar_function(
        "regexp",
        2,
        rusqlite::functions::FunctionFlags::SQLITE_UTF8
            | rusqlite::functions::FunctionFlags::SQLITE_DETERMINISTIC,
        |ctx| {
            let re = ctx.get_or_create_aux(
                0,
                |vr| -> Result<_, Box<dyn std::error::Error + Send + Sync + 'static>> {
                    Ok(regex::Regex::new(vr.as_str()?)?)
                },
            )?;
            let value = ctx.get::<Box<str>>(1)?;
            Ok(re.is_match(&value))
        },
    )?;

    Ok(())
}

struct SimpleWordCompleter {
    words: Vec<String>,
}

static BREAK_CHARS: [u8; 5] = [b' ', b'(', b')', b',', b'.'];
impl SimpleWordCompleter {
    fn new(words: Vec<String>) -> SimpleWordCompleter {
        SimpleWordCompleter { words }
    }
}

impl rustyline::Helper for SimpleWordCompleter {}

impl rustyline::hint::Hinter for SimpleWordCompleter {
    type Hint = String;

    fn hint(&self, _line: &str, _pos: usize, _ctx: &rustyline::Context<'_>) -> Option<String> {
        None
    }
}

impl rustyline::highlight::Highlighter for SimpleWordCompleter {}

impl rustyline::validate::Validator for SimpleWordCompleter {}

impl rustyline::completion::Completer for SimpleWordCompleter {
    type Candidate = String;

    fn complete(
        &self,
        line: &str,
        pos: usize,
        _ctx: &rustyline::Context<'_>,
    ) -> rustyline::Result<(usize, Vec<String>)> {
        let (start, word) = rustyline::completion::extract_word(line, pos, None, &BREAK_CHARS);

        let matches = self
            .words
            .iter()
            .filter(|w| w.starts_with(word))
            .cloned()
            .collect();
        Ok((start, matches))
    }
}

#[derive(Clap)]
struct Opts {
    #[clap(long, about = "Use ',' as the delimiter for the CSV")]
    comma: bool,
    #[clap(long, about = "Use '|' as the delimiter for the CSV")]
    pipe: bool,
    #[clap(long, about = "Use '\\t' as the delimiter for the CSV")]
    tab: bool,
    #[clap(long, about = "Use ';' as the delimiter for the CSV")]
    semicolon: bool,

    #[clap()]
    paths: Vec<String>,
}

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let opts = Opts::parse();

    let delim = match (opts.comma, opts.pipe, opts.tab, opts.semicolon) {
        (true, false, false, false) | (false, false, false, false) => b',',
        (false, true, false, false) => b'|',
        (false, false, true, false) => b'\t',
        (false, false, false, true) => b';',
        _ => {
            eprintln!("Can't pass more than one of --comma, --pipe, and --tab");
            std::process::exit(1);
        }
    };

    let mut conn = rusqlite::Connection::open_in_memory().unwrap();

    let mut base_words = [
        // keywords
        "distinct",
        "select",
        "from",
        "group",
        "by",
        "order",
        "where",
        "count",
        "limit",
        "offset",
        // functions
        "length",
        "coalesce",
        "regexp_extract",
        // csv-sql commands
        "export",
        "schema",
    ]
    .iter()
    .map(|&s| s.to_string())
    .collect::<Vec<String>>();

    if opts.paths.len() == 1 {
        let mut col_names = _load_table_from_path(&mut conn, "t", &opts.paths[0], delim)?;
        base_words.append(&mut col_names);
    } else {
        for (idx, path) in opts.paths.iter().enumerate() {
            let mut col_names =
                _load_table_from_path(&mut conn, &format!("t{}", idx + 1), path, delim)?;
            base_words.append(&mut col_names);
        }
    }

    install_udfs(&mut conn)?;

    let completer = SimpleWordCompleter::new(base_words);
    let mut rl = rustyline::Editor::new();
    rl.set_helper(Some(completer));
    let history_path = dirs::home_dir().unwrap().join(".csv-sql-history");
    let _ = rl.load_history(&history_path);
    loop {
        match rl.readline("> ") {
            Ok(line) => {
                if line.trim().is_empty() {
                    continue;
                }
                _process_query(&mut conn, &line);
                rl.add_history_entry(line);
            }
            Err(rustyline::error::ReadlineError::Interrupted) => {
                println!("Interrupted");
                continue;
            }
            Err(rustyline::error::ReadlineError::Eof) => {
                break;
            }
            Err(err) => {
                println!("Error: {}", err);
                break;
            }
        }
    }
    rl.save_history(&history_path).unwrap();

    Ok(())
}

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

    #[test]
    fn test_normalize_col() {
        for (value, expected) in &[
            ("", ""),
            ("abc", "abc"),
            ("(S)AO", "sao"),
            ("abc (123)", "abc"),
            ("2/6/2000", "c_2_6_2000"),
            ("COMBO#", "combo"),
        ] {
            assert_eq!(&&normalize_col(value), expected);
        }
    }
}
<|endoftext|>


<commit_before>extern crate rand;
extern crate docopt;
extern crate rustc_serialize;

use rand::Rng;
use docopt::Docopt;

// Write the Docopt usage string.
const USAGE: &'static str = "
Usage:
    illuminated-inmates [--prisoner-count=<prisoner-count>] [--repetitions=<repetitions>] [--log-period=<log-period>]

Options:
    -n, --prisoner-count=<prisoner-count>   Number of inmates to simulate [default: 100]
    -r, --repetitions=<repetitions>         Number of times to repeat the simulation (for statistical analysis) [default: 1]
    -l, --log-period=<log-period>           How frequently should the simulation log (in days) [default: 1000]
";

#[derive(RustcDecodable, Debug)]
struct Args {
    flag_prisoner_count: usize,
    flag_repetitions: u32,
    flag_log_period: u32,
}

#[derive(Clone)]
struct Prisoner {
    known_visited_prisoners: Vec<bool>,
}
impl Prisoner {
    fn new(prisoner_count: usize) -> Prisoner {
        Prisoner {
            known_visited_prisoners: vec![false; prisoner_count],
        }
    }

    fn select_light_position(&mut self, day: u32, light_is_on: bool, self_index: usize) -> bool {
        let prisoner_count = self.known_visited_prisoners.len();

        // I now know that I've been interrogated
        self.known_visited_prisoners[self_index] = true;

        // If the previous prisoner knew that this prisoner has been interrogated
        if day > 0 && light_is_on {
            // TODO: multiple strategies
            self.known_visited_prisoners[((day - 1) as usize) % prisoner_count] = true;
        }

        // Return whether we know if today's prisoner has been interrogated
        self.known_visited_prisoners[(day as usize) % prisoner_count]
    }

    fn count_known(&self) -> u32 {
        self.known_visited_prisoners.iter().fold(
            0u32,
            |sum, &has_been_interrogated|
                sum + if has_been_interrogated { 1 } else { 0 }
        )
    }
}

struct WorldState {
    prisoners: Vec<Prisoner>,
    day: u32,
    light_is_on: bool,
}
impl WorldState {
    fn new(prisoner_count: usize) -> WorldState {
        WorldState {
            prisoners: vec![Prisoner::new(prisoner_count); prisoner_count],
            day: 0,
            light_is_on: false,
        }
    }

    fn iterate(&mut self) -> bool {
        let prisoner_count = self.prisoners.len();
        let chosen_prisoner_index = rand::thread_rng().gen_range(0usize, prisoner_count);
        let mut chosen_prisoner = &mut self.prisoners[chosen_prisoner_index];

        self.light_is_on = chosen_prisoner.select_light_position(
            self.day,
            self.light_is_on,
            chosen_prisoner_index,
        );

        // If this prisoner now knows that all prisoners have been interrogated
        if chosen_prisoner.count_known() == (prisoner_count as u32) {
            return true; // Done
        }

        self.day += 1;

        false
    }

    fn best_known(&self) -> u32 {
        self.prisoners.iter()
            .map(|ref prisoner| prisoner.count_known())
            .max()
            .unwrap() // There should always be non-zero prisoners
    }
}

// Returns the duration in days
fn run_simulation(prisoner_count: usize, log_period: u32) -> u32 {
    println!("Beginning simulation with {} prisoners", prisoner_count);

    let mut state = WorldState::new(prisoner_count);

    while !state.iterate() {
        if state.day % log_period == 0 {
            println!("Day: {}, max-known: {}", state.day, state.best_known());
        }
    }

    println!("Done! Day: {}\n", state.day);

    state.day
}

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

    let simulation_results = (0..args.flag_repetitions)
        .map(|_| run_simulation(args.flag_prisoner_count, args.flag_log_period))
        .collect::<Vec<u32>>();

    let average_runtime: u32 = simulation_results.iter().fold(0, |sum, x| sum + x) / args.flag_repetitions;
    println!("Average run-time (over {} runs): {} days", args.flag_repetitions, average_runtime);
}<commit_msg>Allow each day to represent 0 or more prisoners<commit_after>extern crate rand;
extern crate docopt;
extern crate rustc_serialize;

use rand::Rng;
use docopt::Docopt;

// Write the Docopt usage string.
const USAGE: &'static str = "
Usage:
    illuminated-inmates [--prisoner-count=<prisoner-count>] [--repetitions=<repetitions>] [--log-period=<log-period>]

Options:
    -n, --prisoner-count=<prisoner-count>   Number of inmates to simulate [default: 100]
    -r, --repetitions=<repetitions>         Number of times to repeat the simulation (for statistical analysis) [default: 1]
    -l, --log-period=<log-period>           How frequently should the simulation log (in days) [default: 1000]
";

#[derive(RustcDecodable, Debug)]
struct Args {
    flag_prisoner_count: usize,
    flag_repetitions: u32,
    flag_log_period: u32,
}

#[derive(Clone)]
struct Prisoner {
    known_visited_prisoners: Vec<bool>,
}
impl Prisoner {
    fn new(prisoner_count: usize) -> Prisoner {
        Prisoner {
            known_visited_prisoners: vec![false; prisoner_count],
        }
    }

    fn get_todays_prisoner_indexes(&self, day: u32) -> Vec<usize> {
        let prisoner_count = self.known_visited_prisoners.len();
        let period = prisoner_count;
        let day_index = (day as usize) % period;

        vec![day_index]
    }

    fn select_light_position(&mut self, day: u32, light_is_on: bool, self_index: usize) -> bool {
        // I now know that I've been interrogated
        self.known_visited_prisoners[self_index] = true;

        // If the previous prisoner knew that this prisoner has been interrogated
        if day > 0 && light_is_on {
            for i in self.get_todays_prisoner_indexes(day - 1) {
                 self.known_visited_prisoners[i] = true;
            }
        }

        // Return whether we know if today's prisoner has been interrogated
        self.get_todays_prisoner_indexes(day).iter().all(|&i| self.known_visited_prisoners[i])
    }

    fn count_known(&self) -> u32 {
        self.known_visited_prisoners.iter().fold(
            0u32,
            |sum, &has_been_interrogated|
                sum + if has_been_interrogated { 1 } else { 0 }
        )
    }
}

struct WorldState {
    prisoners: Vec<Prisoner>,
    day: u32,
    light_is_on: bool,
}
impl WorldState {
    fn new(prisoner_count: usize) -> WorldState {
        WorldState {
            prisoners: vec![Prisoner::new(prisoner_count); prisoner_count],
            day: 0,
            light_is_on: false,
        }
    }

    fn iterate(&mut self) -> bool {
        let prisoner_count = self.prisoners.len();
        let chosen_prisoner_index = rand::thread_rng().gen_range(0usize, prisoner_count);
        let mut chosen_prisoner = &mut self.prisoners[chosen_prisoner_index];

        self.light_is_on = chosen_prisoner.select_light_position(
            self.day,
            self.light_is_on,
            chosen_prisoner_index,
        );

        // If this prisoner now knows that all prisoners have been interrogated
        if chosen_prisoner.count_known() == (prisoner_count as u32) {
            return true; // Done
        }

        self.day += 1;

        false
    }

    fn best_known(&self) -> u32 {
        self.prisoners.iter()
            .map(|ref prisoner| prisoner.count_known())
            .max()
            .unwrap() // There should always be non-zero prisoners
    }
}

// Returns the duration in days
fn run_simulation(prisoner_count: usize, log_period: u32) -> u32 {
    println!("Beginning simulation with {} prisoners", prisoner_count);

    let mut state = WorldState::new(prisoner_count);

    while !state.iterate() {
        if state.day % log_period == 0 {
            println!("Day: {}, max-known: {}", state.day, state.best_known());
        }
    }

    println!("Done! Day: {}\n", state.day);

    state.day
}

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

    let simulation_results = (0..args.flag_repetitions)
        .map(|_| run_simulation(args.flag_prisoner_count, args.flag_log_period))
        .collect::<Vec<u32>>();

    let average_runtime: u32 = simulation_results.iter().fold(0, |sum, x| sum + x) / args.flag_repetitions;
    println!("Average run-time (over {} runs): {} days", args.flag_repetitions, average_runtime);
}<|endoftext|>


<commit_before>extern crate "rustc-serialize" as rustc_serialize;

use rustc_serialize::{Encodable, Encoder};

// ================================================================================================

const CRTP_VERSION : &'static str = env!("CARGO_PKG_VERSION");

struct SSRC(u32);
struct RtpTimestamp(u32);
struct NtpTimestamp(u64);

struct SenderInfo {
  ntp_ts     : u32, // FIXME: should be NtpTimestamp,
  rtp_ts     : u32, // FIXME: should be RtpTimestamp,
  pckt_count : u32,
  byte_count : u32
}

impl Encodable for SenderInfo {
  fn encode<S : Encoder>(&self, encoder : &mut S) -> Result<(), S::Error> {
    encoder.emit_u32(self.ntp_ts)
  }
}

struct ReportBlock {
  ssrc       : SSRC,
  fract_lost : u8,
  cumul_lost : u32,
  ext_seq    : u32,
  jitter     : u32,
  lsr        : u32,
  dlsr       : u32
}

struct SdesChunk {
  ssrc  : SSRC,
  cname : Option<String>,
  name  : Option<String>,
  email : Option<String>,
  phone : Option<String>,
  loc   : Option<String>,
  tool  : Option<String>,
  note  : Option<String>
}

enum PacketRTCP {
  SR(SSRC, Vec<ReportBlock>, SenderInfo),
  RR(SSRC, Vec<ReportBlock>),
  SDES(Vec<SdesChunk>),
  BYE(Vec<SSRC>, String),
}

enum Packet {
  PacketRTP,
  PacketCompoundRTCP(Vec<PacketRTCP>)
}

// ================================================================================================

fn main() {
  println!("CRTP v{}", CRTP_VERSION)
}

// ================================================================================================
// vim: set ts=2 sw=2 tw=0 et ai:
<commit_msg>Remove Encodable impl Fix type of ntp_ts<commit_after>extern crate "rustc-serialize" as rustc_serialize;

use rustc_serialize::{Encodable, Encoder};

// ================================================================================================

const CRTP_VERSION : &'static str = env!("CARGO_PKG_VERSION");

struct SSRC(u32);
struct RtpTimestamp(u32);
struct NtpTimestamp(u64);

struct SenderInfo {
  ntp_ts     : u64, // FIXME: should be NtpTimestamp,
  rtp_ts     : u32, // FIXME: should be RtpTimestamp,
  pckt_count : u32,
  byte_count : u32
}

struct ReportBlock {
  ssrc       : SSRC,
  fract_lost : u8,
  cumul_lost : u32,
  ext_seq    : u32,
  jitter     : u32,
  lsr        : u32,
  dlsr       : u32
}

struct SdesChunk {
  ssrc  : SSRC,
  cname : Option<String>,
  name  : Option<String>,
  email : Option<String>,
  phone : Option<String>,
  loc   : Option<String>,
  tool  : Option<String>,
  note  : Option<String>
}

enum PacketRTCP {
  SR(SSRC, Vec<ReportBlock>, SenderInfo),
  RR(SSRC, Vec<ReportBlock>),
  SDES(Vec<SdesChunk>),
  BYE(Vec<SSRC>, String),
}

enum Packet {
  PacketRTP,
  PacketCompoundRTCP(Vec<PacketRTCP>)
}

// ================================================================================================

fn main() {
  println!("CRTP v{}", CRTP_VERSION)
}

// ================================================================================================
// vim: set ts=2 sw=2 tw=0 et ai:
<|endoftext|>


<commit_before>extern crate hyper;
extern crate rustc_serialize;
extern crate chrono;
extern crate regex;
extern crate env_logger;
#[macro_use] extern crate log;
extern crate docopt;

use std::ascii::AsciiExt;
use std::io::Read;
use std::env;
use std::collections::HashMap;
use std::fs::File;
use std::io::Write;
use std::path::PathBuf;
use std::fmt;
use std::error::Error;
use std::result;

use chrono::naive::datetime::NaiveDateTime;
use chrono::offset::local::Local;
use chrono::offset::fixed::FixedOffset;
use chrono::datetime::DateTime;
use chrono::Datelike;

use rustc_serialize::json;
use rustc_serialize::Decodable;
use hyper::client::Client;

use regex::Regex;

use docopt::Docopt;

static USAGE: &'static str = "
Washing State Ferry Schedules

Usage:
  wsf [options] <from> <to>
  wsf -h

  <from> and <to> are a prefix of the departing terminal and arriving
  terminal, respectively. For example 'wsf sea ba' is equivalent to
  'wsf Seattle \"Bainbridge Island\"'.

Options:
  -h --help     Show this screen.
";

#[derive(Debug, RustcDecodable)]
struct Args {
    arg_from: String,
    arg_to: String,
}

fn main() {
    env_logger::init().unwrap();

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

    let from_in: &str =  &args.arg_from.to_ascii_lowercase();
    let to_in: &str   = &args.arg_to.to_ascii_lowercase();

    // pull in api key at *build* time from environment
    let mut s = Session::new(env!("WSDOT_API_KEY"));

    let now = Local::now();
    let mut from: Option<i32> = None;
    let mut to: Option<i32> = None;
    for terminal in s.terminals().unwrap().iter() {
        if terminal.Description.to_ascii_lowercase().starts_with(&from_in) {
            from = Some(terminal.TerminalID);
        }
        if terminal.Description.to_ascii_lowercase().starts_with(&to_in) {
            to = Some(terminal.TerminalID);
        }
    }

    let tc = s.schedule(from.unwrap(), to.unwrap()).unwrap();
    for time in tc.Times.iter() {
        if time.depart_time() > now {
            println!("{}\t{}\t{}\t{}",
                     time.depart_time().time(),
                     tc.DepartingTerminalName,
                     tc.ArrivingTerminalName,
                     time.VesselName );
        }

    }
    s.save_cache();
}

struct Session {
    api_key: String,
    client: Client,
    cache: Cache,
    cacheflushdate: String,
    cache_path: String,
    offline: bool,
}

impl Session {

    fn new(api_key: &str) -> Session {
        let mut cache_path: PathBuf = env::home_dir().unwrap();
        cache_path.push(".wsf.cache");
        let cache_path = format!("{}", cache_path.display());

        let mut s = Session {
            api_key: api_key.to_string(),
            client: Client::new(),
            cache: Cache::load(&cache_path),
            cacheflushdate: String::new(),
            cache_path: cache_path,
            offline: false,
        };

        s.offline = match s.get::<String>(format!("/cacheflushdate")) {
            Ok(cfd) => {
                    s.cacheflushdate = cfd;
                    false
                },
            Err(_) => true,
        };
        s
    }

    fn save_cache(&mut self) {
        self.cache.cache_flush_date = self.cacheflushdate.clone();
        let mut f = File::create(&self.cache_path).unwrap();
        let encoded = json::encode(&self.cache).unwrap();
        f.write_all(encoded.as_bytes()).unwrap();
    }

    fn get<T: Decodable>(&self, path: String) -> Result<T> {
        let url = &format!("http://www.wsdot.wa.gov/ferries/api/schedule/rest{}?apiaccesscode={}",
                            path,
                            self.api_key);
        let mut res = try!(self.client.get(url).send());
        assert_eq!(res.status, hyper::Ok);

        let mut buf = String::new();
        try!(res.read_to_string(&mut buf));
        Ok(try!(json::decode::<T>(&buf)))
    }

    fn terminals(&mut self) -> Result<Vec<Terminal>> {
        if self.offline || (self.cache.cache_flush_date == self.cacheflushdate) {
            return Ok(self.cache.terminals.clone())
        }
        else {
            let now = Local::today();
            let path = format!("/terminals/{}-{}-{}", now.year(), now.month(), now.day());
            let routes: Vec<Terminal> = try!(self.get(path));
            self.cache.terminals = routes.clone();
            return Ok(routes);
        }
    }

    fn schedule(&mut self, from: i32, to: i32) -> Result<TerminalCombo> {
        let mut cache_is_stale = true;
        let cache_key = format!("{} {}", from, to);

        if self.offline || (self.cache.cache_flush_date == self.cacheflushdate) {
            if self.cache.sailings.contains_key(&cache_key) {
                // cache is up to date and has route!
                // unwrap is correct as we checked for enry first
                return Ok(self.cache.sailings.get(&cache_key).unwrap().clone());
            }
            else {
                // cache is up to date, but we don't have this route in it
                cache_is_stale = false;
            }
        }

        if cache_is_stale {
            self.cache.sailings.clear();
        }

        let now = Local::now();
        let path = format!("/schedule/{}-{}-{}/{}/{}",
                            now.year(), now.month(), now.day(), from, to);

        let schedule: Schedule = try!(self.get(path));

        self.cache.sailings.insert(cache_key, schedule.TerminalCombos[0].clone());
        Ok(schedule.TerminalCombos[0].clone())
    }
}

#[derive(RustcDecodable, RustcEncodable, Debug)]
struct Cache {
    terminals: Vec<Terminal>,
    sailings: HashMap<String, TerminalCombo>,
    cache_flush_date: String,
}

impl Cache {
    fn load(path: &String) -> Cache {
        let r = File::open(path);
        match r {
            Ok(mut f) => {
                let mut s = String::new();
                f.read_to_string(&mut s).unwrap();
                let cache = json::decode(&s).unwrap();
                cache
            },
            Err(_) => {
                Cache {
                    terminals: vec![],
                    sailings: HashMap::new(),
                    cache_flush_date: String::new(),
                }
            }
        }
    }
}

#[allow(non_snake_case)]
#[derive(RustcDecodable, RustcEncodable, Debug, Clone)]
struct Terminal {
    TerminalID: i32,
    Description: String,
}

#[allow(non_snake_case)]
#[derive(RustcDecodable, RustcEncodable, Clone, Debug)]
struct SailingTime {
    DepartingTime: String,
    ArrivingTime: Option<String>,
    VesselName: String,
}

impl SailingTime {
    // parse date strings of form "/Date(1436318400000-0700)/"
    fn depart_time(&self) -> DateTime<Local> {
        let re = Regex::new(r"^/Date\((\d{10})000-(\d{2})(\d{2})\)/$").unwrap();
        let caps = re.captures(&self.DepartingTime).unwrap();

        let epoch: i64 = caps.at(1).unwrap().parse().unwrap();
        let tz_hours: i32 = caps.at(2).unwrap().parse().unwrap();
        let tz_minutes: i32 = caps.at(3).unwrap().parse().unwrap();

        let nd = NaiveDateTime::from_timestamp(epoch, 0);
        let tz = FixedOffset::west((tz_hours * 3600) + (tz_minutes * 60));
        let fotz: DateTime<FixedOffset> = DateTime::from_utc(nd, tz);
        fotz.with_timezone(&Local)
    }
}

#[allow(non_snake_case)]
#[derive(RustcDecodable, RustcEncodable, Clone, Debug)]
struct TerminalCombo {
    Times: Vec<SailingTime>,
    DepartingTerminalName: String,
    ArrivingTerminalName: String,
}

#[allow(non_snake_case)]
#[derive(RustcDecodable, RustcEncodable, Debug)]
struct Schedule {
    TerminalCombos: Vec<TerminalCombo>,
}

type Result<T> = result::Result<T, CliError>;

#[derive(Debug)]
enum CliError {
    Parse(rustc_serialize::json::DecoderError),
    Http(hyper::error::Error),
    Input(std::io::Error),
}

impl From<hyper::error::Error> for CliError {
    fn from(err: hyper::error::Error) -> CliError {
        CliError::Http(err)
    }
}

impl From<std::io::Error> for CliError {
    fn from(err: std::io::Error) -> CliError {
        CliError::Input(err)
    }
}

impl From<rustc_serialize::json::DecoderError> for CliError {
    fn from(err: rustc_serialize::json::DecoderError) -> CliError {
        CliError::Parse(err)
    }
}

impl fmt::Display for CliError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        /*
        match *self {
            CliError::Io(ref err) => err.fmt(f),
            CliError::Csv(ref err) => err.fmt(f),
            CliError::NotFound => write!(f, "No matching cities with a \
                                             population were found."),
        }
        */
        write!(f, "oops!")
    }
}

impl Error for CliError {
    fn description(&self) -> &str {
        /*
        match *self {
            CliError::Io(ref err) => err.description(),
            CliError::Csv(ref err) => err.description(),
            CliError::NotFound => "not found",
        }
        */
        "broke and went boom"
    }
}
<commit_msg>normalizing error handling further<commit_after>extern crate hyper;
extern crate rustc_serialize;
extern crate chrono;
extern crate regex;
extern crate env_logger;
#[macro_use] extern crate log;
extern crate docopt;

use std::ascii::AsciiExt;
use std::io::Read;
use std::env;
use std::collections::HashMap;
use std::fs::File;
use std::io::Write;
use std::path::PathBuf;
use std::fmt;
use std::error::Error;
use std::result;

use chrono::naive::datetime::NaiveDateTime;
use chrono::offset::local::Local;
use chrono::offset::fixed::FixedOffset;
use chrono::datetime::DateTime;
use chrono::Datelike;

use rustc_serialize::json;
use rustc_serialize::Decodable;
use hyper::client::Client;

use regex::Regex;

use docopt::Docopt;

static USAGE: &'static str = "
Washing State Ferry Schedules

Usage:
  wsf [options] <from> <to>
  wsf -h

  <from> and <to> are a prefix of the departing terminal and arriving
  terminal, respectively. For example 'wsf sea ba' is equivalent to
  'wsf Seattle \"Bainbridge Island\"'.

Options:
  -h --help     Show this screen.
";

#[derive(Debug, RustcDecodable)]
struct Args {
    arg_from: String,
    arg_to: String,
}

fn run() -> Result<()> {
    try!(env_logger::init());

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

    let from_in: &str =  &args.arg_from.to_ascii_lowercase();
    let to_in: &str   = &args.arg_to.to_ascii_lowercase();

    // pull in api key at *build* time from environment
    let mut s = Session::new(env!("WSDOT_API_KEY"));

    let now = Local::now();
    let mut from: Option<i32> = None;
    let mut to: Option<i32> = None;
    for terminal in s.terminals().unwrap().iter() {
        if terminal.Description.to_ascii_lowercase().starts_with(&from_in) {
            from = Some(terminal.TerminalID);
        }
        if terminal.Description.to_ascii_lowercase().starts_with(&to_in) {
            to = Some(terminal.TerminalID);
        }
    }

    let tc = s.schedule(from.unwrap(), to.unwrap()).unwrap();
    for time in tc.Times.iter() {
        if time.depart_time() > now {
            println!("{}\t{}\t{}\t{}",
                     time.depart_time().time(),
                     tc.DepartingTerminalName,
                     tc.ArrivingTerminalName,
                     time.VesselName );
        }

    }
    s.save_cache()
}

fn main() {
    match run() {
        Ok(_) => {},
        Err(e) => {
            println!("");
            std::process::exit(1);
        }
    }
}

struct Session {
    api_key: String,
    client: Client,
    cache: Cache,
    cacheflushdate: String,
    cache_path: String,
    offline: bool,
}

impl Session {

    fn new(api_key: &str) -> Session {
        let mut cache_path: PathBuf = env::home_dir().unwrap();
        cache_path.push(".wsf.cache");
        let cache_path = format!("{}", cache_path.display());

        let mut s = Session {
            api_key: api_key.to_string(),
            client: Client::new(),
            cache: Cache::load(&cache_path),
            cacheflushdate: String::new(),
            cache_path: cache_path,
            offline: false,
        };

        s.offline = match s.get::<String>(format!("/cacheflushdate")) {
            Ok(cfd) => {
                    s.cacheflushdate = cfd;
                    false
                },
            Err(_) => true,
        };
        s
    }

    fn save_cache(&mut self) -> Result<()> {
        self.cache.cache_flush_date = self.cacheflushdate.clone();

        let mut f = try!(File::create(&self.cache_path));
        let encoded = try!(json::encode(&self.cache));
        Ok(try!(f.write_all(encoded.as_bytes())))
    }

    fn get<T: Decodable>(&self, path: String) -> Result<T> {
        let url = &format!("http://www.wsdot.wa.gov/ferries/api/schedule/rest{}?apiaccesscode={}",
                            path,
                            self.api_key);
        let mut res = try!(self.client.get(url).send());
        assert_eq!(res.status, hyper::Ok);

        let mut buf = String::new();
        try!(res.read_to_string(&mut buf));
        Ok(try!(json::decode::<T>(&buf)))
    }

    fn terminals(&mut self) -> Result<Vec<Terminal>> {
        if self.offline || (self.cache.cache_flush_date == self.cacheflushdate) {
            return Ok(self.cache.terminals.clone())
        }
        else {
            let now = Local::today();
            let path = format!("/terminals/{}-{}-{}", now.year(), now.month(), now.day());
            let routes: Vec<Terminal> = try!(self.get(path));
            self.cache.terminals = routes.clone();
            return Ok(routes);
        }
    }

    fn schedule(&mut self, from: i32, to: i32) -> Result<TerminalCombo> {
        let mut cache_is_stale = true;
        let cache_key = format!("{} {}", from, to);

        if self.offline || (self.cache.cache_flush_date == self.cacheflushdate) {
            if self.cache.sailings.contains_key(&cache_key) {
                // cache is up to date and has route!
                // unwrap is correct as we checked for enry first
                return Ok(self.cache.sailings.get(&cache_key).unwrap().clone());
            }
            else {
                // cache is up to date, but we don't have this route in it
                cache_is_stale = false;
            }
        }

        if cache_is_stale {
            self.cache.sailings.clear();
        }

        let now = Local::now();
        let path = format!("/schedule/{}-{}-{}/{}/{}",
                            now.year(), now.month(), now.day(), from, to);

        let schedule: Schedule = try!(self.get(path));

        self.cache.sailings.insert(cache_key, schedule.TerminalCombos[0].clone());
        Ok(schedule.TerminalCombos[0].clone())
    }
}

#[derive(RustcDecodable, RustcEncodable, Debug)]
struct Cache {
    terminals: Vec<Terminal>,
    sailings: HashMap<String, TerminalCombo>,
    cache_flush_date: String,
}

impl Cache {
    fn load(path: &String) -> Cache {
        let r = File::open(path);
        match r {
            Ok(mut f) => {
                let mut s = String::new();
                f.read_to_string(&mut s).unwrap();
                let cache = json::decode(&s).unwrap();
                cache
            },
            Err(_) => {
                Cache {
                    terminals: vec![],
                    sailings: HashMap::new(),
                    cache_flush_date: String::new(),
                }
            }
        }
    }
}

#[allow(non_snake_case)]
#[derive(RustcDecodable, RustcEncodable, Debug, Clone)]
struct Terminal {
    TerminalID: i32,
    Description: String,
}

#[allow(non_snake_case)]
#[derive(RustcDecodable, RustcEncodable, Clone, Debug)]
struct SailingTime {
    DepartingTime: String,
    ArrivingTime: Option<String>,
    VesselName: String,
}

impl SailingTime {
    // parse date strings of form "/Date(1436318400000-0700)/"
    fn depart_time(&self) -> DateTime<Local> {
        let re = Regex::new(r"^/Date\((\d{10})000-(\d{2})(\d{2})\)/$").unwrap();
        let caps = re.captures(&self.DepartingTime).unwrap();

        let epoch: i64 = caps.at(1).unwrap().parse().unwrap();
        let tz_hours: i32 = caps.at(2).unwrap().parse().unwrap();
        let tz_minutes: i32 = caps.at(3).unwrap().parse().unwrap();

        let nd = NaiveDateTime::from_timestamp(epoch, 0);
        let tz = FixedOffset::west((tz_hours * 3600) + (tz_minutes * 60));
        let fotz: DateTime<FixedOffset> = DateTime::from_utc(nd, tz);
        fotz.with_timezone(&Local)
    }
}

#[allow(non_snake_case)]
#[derive(RustcDecodable, RustcEncodable, Clone, Debug)]
struct TerminalCombo {
    Times: Vec<SailingTime>,
    DepartingTerminalName: String,
    ArrivingTerminalName: String,
}

#[allow(non_snake_case)]
#[derive(RustcDecodable, RustcEncodable, Debug)]
struct Schedule {
    TerminalCombos: Vec<TerminalCombo>,
}

type Result<T> = result::Result<T, CliError>;

#[derive(Debug)]
enum CliError {
    Log(log::SetLoggerError),
    Parse(rustc_serialize::json::DecoderError),
    SaveCache(rustc_serialize::json::EncoderError),
    Http(hyper::error::Error),
    Io(std::io::Error),
    BrokenReality(String),
}

impl From<rustc_serialize::json::EncoderError> for CliError {
    fn from(err: rustc_serialize::json::EncoderError) -> CliError {
        CliError::SaveCache(err)
    }
}

impl From<log::SetLoggerError> for CliError {
    fn from(err: log::SetLoggerError) -> CliError {
        CliError::Log(err)
    }
}
impl From<hyper::error::Error> for CliError {
    fn from(err: hyper::error::Error) -> CliError {
        CliError::Http(err)
    }
}

impl From<std::io::Error> for CliError {
    fn from(err: std::io::Error) -> CliError {
        CliError::Io(err)
    }
}

impl From<rustc_serialize::json::DecoderError> for CliError {
    fn from(err: rustc_serialize::json::DecoderError) -> CliError {
        CliError::Parse(err)
    }
}

impl fmt::Display for CliError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        unimplemented!();
        /*
        TODO implement
        match *self {
            CliError::Io(ref err) => err.fmt(f),
            CliError::Csv(ref err) => err.fmt(f),
            CliError::NotFound => write!(f, "No matching cities with a \
                                             population were found."),
        }
        */
        write!(f, "oops!")
    }
}

impl Error for CliError {
    fn description(&self) -> &str {
        unimplemented!();
        /*
        TODO implement
        match *self {
            CliError::Io(ref err) => err.description(),
            CliError::Csv(ref err) => err.description(),
            CliError::NotFound => "not found",
        }
        */
        "broke and went boom"
    }
}
<|endoftext|>


<commit_before>use std::env;

extern crate git2;
extern crate tiny_http;

static INDEX_GIT_URL: &'static str = "https://github.com/rust-lang/crates.io-index";

fn main() {
    git2::Repository::clone(INDEX_GIT_URL, "crates.io-index").unwrap();

    let port = match env::var("PORT") {
        Ok(p) => p.parse::<u16>().unwrap(),
        Err(..) => 8000,
    };

    let server = tiny_http::ServerBuilder::new().with_port(port).build().unwrap();

    for request in server.incoming_requests() {
        println!("received request! method: {:?}, url: {:?}, headers: {:?}",
            request.get_method(),
            request.get_url(),
            request.get_headers()
        );

        let response = tiny_http::Response::from_string("hello world".to_string());
        request.respond(response);
    }
}
<commit_msg>Add a couple print messages<commit_after>use std::env;

extern crate git2;
extern crate tiny_http;

static INDEX_GIT_URL: &'static str = "https://github.com/rust-lang/crates.io-index";

fn main() {
    println!("Cloning crates.io-index");
    git2::Repository::clone(INDEX_GIT_URL, "crates.io-index").unwrap();

    let port = match env::var("PORT") {
        Ok(p) => p.parse::<u16>().unwrap(),
        Err(..) => 8000,
    };

    let server = tiny_http::ServerBuilder::new().with_port(port).build().unwrap();

    println!("Server listening on port {}", port);
    for request in server.incoming_requests() {
        println!("received request! method: {:?}, url: {:?}, headers: {:?}",
            request.get_method(),
            request.get_url(),
            request.get_headers()
        );

        let response = tiny_http::Response::from_string("hello world".to_string());
        request.respond(response);
    }
}
<|endoftext|>