<commit_before>#[macro_use]
extern crate serde_derive;
extern crate toml;
extern crate clap;
extern crate hyper;
extern crate hyper_native_tls;

mod gitignore;
mod cli;

use std::fs::{File, OpenOptions, create_dir, read_dir};
use std::io::{BufReader, Result, Error, ErrorKind};
use std::io::prelude::*;
use std::path::Path;
use std::process::{exit, Command};
use std::env;

#[derive(Deserialize)]
struct Config {
    mkdir: Option<Vec<String>>,
    touch: Option<Vec<String>>,
    exec: Option<Vec<String>>,
    gitignore: Option<Vec<String>>,
    include: Option<Vec<String>>,
}

fn main() {
    let matches = cli::build_cli().get_matches();
    let lang = matches.value_of("lang").unwrap();

    if let Some(matches) = matches.subcommand_matches("new") {
        println!("doing new {}", matches.value_of("NAME").unwrap());
    }

    let config_path = get_config_path(&lang.to_string());

    let config: Config = match parse_config(config_path) {
        Ok(c) => c,
        Err(err) => {
            println!("Error while parsing config! {:?}", err);
            exit(1);
        }
    };

    if let Some(subcmd) = matches.subcommand_matches("new") {
        let name = subcmd.value_of("NAME").unwrap();
        if file_exists(&name.to_string()) {
            println!("A file or directory with that name already exists");
            exit(1);
        } else {
            match create_dir(name.clone()) {
                Ok(_) => println!("Created directory '{}'", name),
                Err(e) => {
                    println!("Could not create direcotry '{}'. {:?}", name, e);
                    exit(1);
                }
            }
            if let Err(_) = env::set_current_dir(Path::new(&*name)) {
                println!("Could not change directory!");
                exit(1);
            }
        }
    }

    if !dir_is_empty(&".".to_string()) {
        println!("Direcotry is not empty!");
        exit(1);
    }

    if let Some(ref includes) = config.include {
        for incl in includes {
            let path = get_config_path(&incl);

            let conf: Config = match parse_config(path) {
                Ok(c) => c,
                Err(e) => {
                    println!("Could not read include '{}'. {}", incl, e);
                    exit(1);
                }
            };
            process_config(&conf);
        }
    }

    process_config(&config);

}

fn process_config(config: &Config) {
    if let Some(ref dirs) = config.mkdir {
        for dir in dirs {
            if !file_exists(&dir) {
                match create_dir(dir.clone()) {
                    Ok(_) => println!("Created directory '{}'", dir),
                    Err(e) => println!("Could not create directory '{}'. {:?}", dir, e),
                }
            } else {
                println!("Directory '{}' already exists", dir);
            }
        }
    }

    if let Some(ref gi) = config.gitignore {
        let ign = gitignore::get_gitignore(gi).unwrap();
        let file = OpenOptions::new()
            .create_new(true)
            .append(true)
            .open(".gitignore");
        if let Ok(mut file) = file {
            match writeln!(file, "{}", ign) {
                Ok(_) => {}
                Err(e) => println!("Could not write .gitignore. {:?}", e),
            }
        } else {
            println!("Could not write .gitignore");
        }
    }

    if let Some(ref fnames) = config.touch {
        for file in fnames {
            if !create_file(&file) {
                println!("Could not create file '{}'", file);
            }
        }
    }

    if let Some(ref cmds) = config.exec {
        for cmd in cmds {
            println!("Executing '{}'", cmd);
            match exec(&cmd) {
                Ok((out, err)) => {
                    println!("stdout: {}", out);
                    println!("stderr: {}", err);
                }
                Err(e) => println!("Failed to execute: {:?}", e),
            }
        }
    }
}

/// Returns the config path for a `lang` parameter.
///
/// This method concatenates $HOME with "/.skeleton/{lang}.toml"
///
/// # Examples
///
/// ```
/// let path = get_config_path(&"rust".to_string())
/// ```
fn get_config_path(lang: &String) -> String {
    let mut config_path = env!("HOME").to_string();

    config_path.push_str("/.skeleton/");
    config_path.push_str(lang);
    config_path.push_str(".toml");
    config_path
}

/// Checks if a directory is empty
///
/// # Examples
///
/// ```
/// let dir_is_empty(&".".to_string());
/// ```
fn dir_is_empty(dir: &String) -> bool {
    let paths = match read_dir(dir) {
        Ok(paths) => paths,
        Err(_) => return false,
    };

    for _ in paths {
        return false;
    }
    true
}

/// Checks if a file exists
fn file_exists(name: &String) -> bool {
    Path::new(&name).exists()
}

/// Executes a command using `sh` or `cmd.exe` depending on the used operating system.
///
/// Returns either a `Error` or a tuple `(stdout, stderr)`
fn exec(cmd: &String) -> Result<(String, String)> {
    #[cfg(unix)]
    let shell = "sh";
    #[cfg(unix)]
    let arg1 = "-c";
    #[cfg(windows)]
    let shell = "cmd.exe";
    #[cfg(windows)]
    let arg1 = "/C";

    let out = Command::new(shell).arg(arg1).arg(cmd).output()?;

    Ok((String::from_utf8(out.stdout).unwrap_or("Could not parse stdout".to_string()),
        String::from_utf8(out.stderr).unwrap_or("Could not parse stderr".to_string())))

}

/// Creates a file if it doesn't exist.
fn create_file(file_name: &String) -> bool {
    !file_exists(&file_name) &&
    OpenOptions::new()
        .append(true)
        .create(true)
        .open(file_name)
        .is_ok()
}

/// Parses a configuration file and returns the `Config` struct.
fn parse_config(file_name: String) -> Result<Config> {
    let file = File::open(file_name)?;
    let mut reader = BufReader::new(file);
    let mut contents = String::new();

    reader.read_to_string(&mut contents)?;

    toml::from_str(&contents).map_err(|err| Error::new(ErrorKind::InvalidInput, err))
}

#[test]
fn test_file_exists() {
    assert!(file_exists(&"test/foo.toml".to_string()));
    assert!(!file_exists(&"test/none".to_string()));
}

#[test]
fn test_parse_config() {
    let conf = parse_config("test/foo.toml".to_string()).unwrap();
    let gi = conf.gitignore.unwrap();
    assert_eq!(gi, vec!["rust".to_string(), "vim".to_string()]);

    let mkd = conf.mkdir.unwrap();
    assert_eq!(mkd, vec!["b".to_string()]);

    let touch = conf.touch.unwrap();
    assert_eq!(touch, vec!["a".to_string()]);

    let exec = conf.exec.unwrap();
    assert_eq!(exec, vec!["touch asdf".to_string()]);
}

#[test]
fn test_exec() {
    let (stdout, stderr) = exec(&"echo -n test".to_string()).unwrap();
    assert_eq!(stdout, "test".to_string());
    assert_eq!(stderr, "".to_string());
    let (stdout, stderr) = exec(&"echo -n test 1>&2".to_string()).unwrap();
    assert_eq!(stdout, "".to_string());
    assert_eq!(stderr, "test".to_string());
}

#[test]
fn test_dir_is_empty() {
    let is_empty = dir_is_empty(&"./test".to_string());
    assert!(!is_empty);
    let non_existent = dir_is_empty(&"./non_existent_dir".to_string());
    assert!(!non_existent);
}

#[test]
fn test_get_config_path() {
    let expected = get_config_path(&"foo".to_string());
    let mut actual = env::home_dir().unwrap().to_str().unwrap().to_string();
    actual.push_str("/.skeleton/foo.toml");
    assert_eq!(actual, expected);
}
<commit_msg>Introduce feature to customize the execution order<commit_after>#[macro_use]
extern crate serde_derive;
extern crate toml;
extern crate clap;
extern crate hyper;
extern crate hyper_native_tls;

mod gitignore;
mod cli;

use std::fs::{File, OpenOptions, create_dir, read_dir};
use std::io::{BufReader, Result, Error, ErrorKind};
use std::io::prelude::*;
use std::path::Path;
use std::process::{exit, Command};
use std::env;

#[derive(Deserialize)]
struct Config {
    mkdir: Option<Vec<String>>,
    touch: Option<Vec<String>>,
    exec: Option<Vec<String>>,
    gitignore: Option<Vec<String>>,
    include: Option<Vec<String>>,
    order: Option<Vec<String>>,
}

fn main() {
    let matches = cli::build_cli().get_matches();
    let lang = matches.value_of("lang").unwrap();

    if let Some(matches) = matches.subcommand_matches("new") {
        println!("doing new {}", matches.value_of("NAME").unwrap());
    }

    let config_path = get_config_path(&lang.to_string());

    let config: Config = match parse_config(config_path) {
        Ok(c) => c,
        Err(err) => {
            println!("Error while parsing config! {:?}", err);
            exit(1);
        }
    };

    if let Some(subcmd) = matches.subcommand_matches("new") {
        let name = subcmd.value_of("NAME").unwrap();
        if file_exists(&name.to_string()) {
            println!("A file or directory with that name already exists");
            exit(1);
        } else {
            match create_dir(name.clone()) {
                Ok(_) => println!("Created directory '{}'", name),
                Err(e) => {
                    println!("Could not create direcotry '{}'. {:?}", name, e);
                    exit(1);
                }
            }
            if let Err(_) = env::set_current_dir(Path::new(&*name)) {
                println!("Could not change directory!");
                exit(1);
            }
        }
    }

    if !dir_is_empty(&".".to_string()) {
        println!("Direcotry is not empty!");
        exit(1);
    }

    if let Some(ref includes) = config.include {
        for incl in includes {
            let path = get_config_path(&incl);

            let conf: Config = match parse_config(path) {
                Ok(c) => c,
                Err(e) => {
                    println!("Could not read include '{}'. {}", incl, e);
                    exit(1);
                }
            };
            process_config(conf);
        }
    }

    process_config(config);

}

fn mkdir(dirs: &Option<Vec<String>>) {
    if let Some(ref dirs) = *dirs {
        for dir in dirs {
            if !file_exists(&dir) {
                match create_dir(dir.clone()) {
                    Ok(_) => println!("Created directory '{}'", dir),
                    Err(e) => println!("Could not create directory '{}'. {:?}", dir, e),
                }
            } else {
                println!("Directory '{}' already exists", dir);
            }
        }
    }
}

fn gitignore(gi: &Option<Vec<String>>) {
    if let Some(ref gi) = *gi {
        let ign = gitignore::get_gitignore(&gi).unwrap();
        let file = OpenOptions::new()
            .create_new(true)
            .append(true)
            .open(".gitignore");
        if let Ok(mut file) = file {
            match writeln!(file, "{}", ign) {
                Ok(_) => {}
                Err(e) => println!("Could not write .gitignore. {:?}", e),
            }
        } else {
            println!("Could not write .gitignore");
        }
    }
}

fn touch(files: &Option<Vec<String>>) {
    if let Some(ref fnames) = *files {
        for file in fnames {
            if !create_file(&file) {
                println!("Could not create file '{}'", file);
            }
        }
    }
}

fn exec_list(cmd_list: &Option<Vec<String>>) {
    if let Some(ref cmds) = *cmd_list {
        for cmd in cmds {
            println!("Executing '{}'", cmd);
            match exec(&cmd) {
                Ok((out, err)) => {
                    println!("stdout: {}", out);
                    println!("stderr: {}", err);
                }
                Err(e) => println!("Failed to execute: {:?}", e),
            }
        }
    }
}

fn process_config(config: Config) {
    let order: Vec<String> = match config.order {
        Some(order) => order,
        None => {
            vec!["mkdir".to_string(),
                 "gitignore".to_string(),
                 "touch".to_string(),
                 "exec".to_string()]
        }
    };

    for ord in order {
        match &*ord {
            "mkdir" => mkdir(&config.mkdir),
            "gitignore" => gitignore(&config.gitignore),
            "touch" => touch(&config.touch),
            "exec" => exec_list(&config.exec),
            x => println!("Unknown operation \"{}\"", x),
        }
    }
}

/// Returns the config path for a `lang` parameter.
///
/// This method concatenates $HOME with "/.skeleton/{lang}.toml"
///
/// # Examples
///
/// ```
/// let path = get_config_path(&"rust".to_string())
/// ```
fn get_config_path(lang: &String) -> String {
    let mut config_path = env!("HOME").to_string();

    config_path.push_str("/.skeleton/");
    config_path.push_str(lang);
    config_path.push_str(".toml");
    config_path
}

/// Checks if a directory is empty
///
/// # Examples
///
/// ```
/// let dir_is_empty(&".".to_string());
/// ```
fn dir_is_empty(dir: &String) -> bool {
    let paths = match read_dir(dir) {
        Ok(paths) => paths,
        Err(_) => return false,
    };

    for _ in paths {
        return false;
    }
    true
}

/// Checks if a file exists
fn file_exists(name: &String) -> bool {
    Path::new(&name).exists()
}

/// Executes a command using `sh` or `cmd.exe` depending on the used operating system.
///
/// Returns either a `Error` or a tuple `(stdout, stderr)`
fn exec(cmd: &String) -> Result<(String, String)> {
    #[cfg(unix)]
    let shell = "sh";
    #[cfg(unix)]
    let arg1 = "-c";
    #[cfg(windows)]
    let shell = "cmd.exe";
    #[cfg(windows)]
    let arg1 = "/C";

    let out = Command::new(shell).arg(arg1).arg(cmd).output()?;

    Ok((String::from_utf8(out.stdout).unwrap_or("Could not parse stdout".to_string()),
        String::from_utf8(out.stderr).unwrap_or("Could not parse stderr".to_string())))

}

/// Creates a file if it doesn't exist.
fn create_file(file_name: &String) -> bool {
    !file_exists(&file_name) &&
    OpenOptions::new()
        .append(true)
        .create(true)
        .open(file_name)
        .is_ok()
}

/// Parses a configuration file and returns the `Config` struct.
fn parse_config(file_name: String) -> Result<Config> {
    let file = File::open(file_name)?;
    let mut reader = BufReader::new(file);
    let mut contents = String::new();

    reader.read_to_string(&mut contents)?;

    toml::from_str(&contents).map_err(|err| Error::new(ErrorKind::InvalidInput, err))
}

#[test]
fn test_file_exists() {
    assert!(file_exists(&"test/foo.toml".to_string()));
    assert!(!file_exists(&"test/none".to_string()));
}

#[test]
fn test_parse_config() {
    let conf = parse_config("test/foo.toml".to_string()).unwrap();
    let gi = conf.gitignore.unwrap();
    assert_eq!(gi, vec!["rust".to_string(), "vim".to_string()]);

    let mkd = conf.mkdir.unwrap();
    assert_eq!(mkd, vec!["b".to_string()]);

    let touch = conf.touch.unwrap();
    assert_eq!(touch, vec!["a".to_string()]);

    let exec = conf.exec.unwrap();
    assert_eq!(exec, vec!["touch asdf".to_string()]);
}

#[test]
fn test_exec() {
    let (stdout, stderr) = exec(&"echo -n test".to_string()).unwrap();
    assert_eq!(stdout, "test".to_string());
    assert_eq!(stderr, "".to_string());
    let (stdout, stderr) = exec(&"echo -n test 1>&2".to_string()).unwrap();
    assert_eq!(stdout, "".to_string());
    assert_eq!(stderr, "test".to_string());
}

#[test]
fn test_dir_is_empty() {
    let is_empty = dir_is_empty(&"./test".to_string());
    assert!(!is_empty);
    let non_existent = dir_is_empty(&"./non_existent_dir".to_string());
    assert!(!non_existent);
}

#[test]
fn test_get_config_path() {
    let expected = get_config_path(&"foo".to_string());
    let mut actual = env::home_dir().unwrap().to_str().unwrap().to_string();
    actual.push_str("/.skeleton/foo.toml");
    assert_eq!(actual, expected);
}
<|endoftext|>


<commit_before>// TODO
// impl ToGeojson for ....
// generic number instead of f64 for position?
// rename Position to Pos or Positions

extern crate serialize;

use std::collections::TreeMap;
use serialize::json::ToJson;
use serialize::json;

/// Position
///
/// [GeoJSON Format Specification § 2.1.1](http://geojson.org/geojson-spec.html#positions)
pub struct Position(pub Vec<f64>);

impl ToJson for Position {
    fn to_json(&self) -> json::Json {
        let &Position(ref nums) = self;
        nums.to_json()
    }
}

impl Clone for Position {
    fn clone(&self) -> Position {
        let &Position(ref nums) = self;
        Position(nums.clone())
    }
}

/// Point
///
/// [GeoJSON Format Specification § 2.1.2](http://geojson.org/geojson-spec.html#point)
pub struct Point {
    pub coordinates: Position,
}

impl ToJson for Point {
    fn to_json(&self) -> json::Json {
        let mut d = TreeMap::new();
        d.insert("type".to_string(), "Point".to_string().to_json());
        d.insert("coordinates".to_string(), self.coordinates.to_json());
        d.to_json()
    }
}

/// MultiPoint
///
/// [GeoJSON Format Specification § 2.1.3](http://geojson.org/geojson-spec.html#multipoint)
pub struct MultiPoint {
    pub points: Vec<Point>,
}

impl ToJson for MultiPoint {
    fn to_json(&self) -> json::Json {
        let coordinates: Vec<Position> =
            self.points.iter().map(|p| p.coordinates.clone()).collect();
        let mut d = TreeMap::new();
        d.insert("type".to_string(), "MultiPoint".to_string().to_json());
        d.insert("coordinates".to_string(), coordinates.to_json());
        d.to_json()
    }
}

/// LineString
///
/// [GeoJSON Format Specification § 2.1.4](http://geojson.org/geojson-spec.html#linestring)
pub struct LineString {
    pub coordinates: Vec<Position>,
}

impl ToJson for LineString {
    fn to_json(&self) -> json::Json {
        let mut d = TreeMap::new();
        d.insert("type".to_string(), "LineString".to_string().to_json());
        d.insert("coordinates".to_string(), self.coordinates.to_json());
        d.to_json()
    }
}

/// MultiLineString
///
/// [GeoJSON Format Specification § 2.1.5](http://geojson.org/geojson-spec.html#multilinestring)
pub struct MultiLineString {
    pub line_strings: Vec<LineString>,
}

impl ToJson for MultiLineString {
    fn to_json(&self) -> json::Json {
        let coordinates: Vec<Vec<Position>> =
            self.line_strings.iter().map(|l| l.coordinates.clone()).collect();
        let mut d = TreeMap::new();
        d.insert("type".to_string(), "MultiLineString".to_string().to_json());
        d.insert("coordinates".to_string(), coordinates.to_json());
        d.to_json()
    }
}

/// Polygon
///
/// [GeoJSON Format Specification § 2.1.6](http://geojson.org/geojson-spec.html#polygon)
pub struct Polygon {
    pub exterior: Vec<Position>,
    pub holes: Vec<Vec<Position>>,  // TODO: this should be optional
}

impl Polygon {
    fn coordinates(&self) -> Vec<Vec<Position>> {
        let mut coordinates = self.holes.clone();
        coordinates.insert(0, self.exterior.clone());
        coordinates
    }
}

impl ToJson for Polygon {
    fn to_json(&self) -> json::Json {
        let mut d = TreeMap::new();
        d.insert("type".to_string(), "Polygon".to_string().to_json());
        d.insert("coordinates".to_string(), self.coordinates().to_json());
        d.to_json()
    }
}

/// MultiPolygon
///
/// [GeoJSON Format Specification § 2.1.7](http://geojson.org/geojson-spec.html#multipolygon)
pub struct MultiPolygon {
    pub polygons: Vec<Polygon>,
}

impl ToJson for MultiPolygon {
    fn to_json(&self) -> json::Json {
        let coordinates: Vec<Vec<Vec<Position>>> =
            self.polygons.iter().map(|p| p.coordinates()).collect();
        let mut d = TreeMap::new();
        d.insert("type".to_string(), "MultiPolygon".to_string().to_json());
        d.insert("coordinates".to_string(), coordinates.to_json());
        d.to_json()
    }
}

/// Geometry
pub enum Geometry {
    Point(Point),
    MultiPoint(MultiPoint),
    LineString(LineString),
    MultiLineString(MultiLineString),
    Polygon(Polygon),
    MultiPolygon(MultiPolygon),
    GeometryCollection(GeometryCollection),
}

impl ToJson for Geometry {
    fn to_json(&self) -> json::Json {
        match *self {
            // TODO: is there a better way of doing this?
            Point(ref geom) => geom.to_json(),
            MultiPoint(ref geom) => geom.to_json(),
            LineString(ref geom) => geom.to_json(),
            MultiLineString(ref geom) => geom.to_json(),
            Polygon(ref geom) => geom.to_json(),
            MultiPolygon(ref geom) => geom.to_json(),
            GeometryCollection(ref geom) => geom.to_json(),
        }
    }
}

/// GeometryCollection
///
/// [GeoJSON Format Specification § 2.1.8](http://geojson.org/geojson-spec.html#geometry-collection)
pub struct GeometryCollection {
    geometries: Vec<Geometry>,
}

impl ToJson for GeometryCollection {
    fn to_json(&self) -> json::Json {
        let mut d = TreeMap::new();
        d.insert("type".to_string(), "GeometryCollection".to_string().to_json());
        d.insert("geometries".to_string(), self.geometries.to_json());
        d.to_json()
    }
}

/// Feature
///
/// [GeoJSON Format Specification § 2.2](http://geojson.org/geojson-spec.html#feature-objects)
pub struct Feature {
    geometry: Geometry,
    properties: json::Json,
}

impl ToJson for Feature {
    fn to_json(&self) -> json::Json {
        let mut d = TreeMap::new();
        d.insert("type".to_string(), "Feature".to_string().to_json());
        d.insert("geometry".to_string(), self.geometry.to_json());
        d.insert("properties".to_string(), self.properties.to_json());
        d.to_json()
    }
}

/// FeatureCollection
///
/// [GeoJSON Format Specification § 2.3](http://geojson.org/geojson-spec.html#feature-collection-objects)
pub struct FeatureCollection {
    features: Vec<Feature>,
}

impl ToJson for FeatureCollection {
    fn to_json(&self) -> json::Json {
        let mut d = TreeMap::new();
        d.insert("type".to_string(), "FeatureCollection".to_string().to_json());
        d.insert("features".to_string(), self.features.to_json());
        d.to_json()
    }
}

fn main() {
    let point = Point {
        coordinates: Position(vec![1., 2., 3.]),
    };

    let j: json::Json = point.to_json();
    let s: String = j.to_pretty_str();

    println!("{}", s);
}
<commit_msg>Remove unfixable(?) TODO<commit_after>// TODO
// impl ToGeojson for ....
// generic number instead of f64 for position?
// rename Position to Pos or Positions

extern crate serialize;

use std::collections::TreeMap;
use serialize::json::ToJson;
use serialize::json;

/// Position
///
/// [GeoJSON Format Specification § 2.1.1](http://geojson.org/geojson-spec.html#positions)
pub struct Position(pub Vec<f64>);

impl ToJson for Position {
    fn to_json(&self) -> json::Json {
        let &Position(ref nums) = self;
        nums.to_json()
    }
}

impl Clone for Position {
    fn clone(&self) -> Position {
        let &Position(ref nums) = self;
        Position(nums.clone())
    }
}

/// Point
///
/// [GeoJSON Format Specification § 2.1.2](http://geojson.org/geojson-spec.html#point)
pub struct Point {
    pub coordinates: Position,
}

impl ToJson for Point {
    fn to_json(&self) -> json::Json {
        let mut d = TreeMap::new();
        d.insert("type".to_string(), "Point".to_string().to_json());
        d.insert("coordinates".to_string(), self.coordinates.to_json());
        d.to_json()
    }
}

/// MultiPoint
///
/// [GeoJSON Format Specification § 2.1.3](http://geojson.org/geojson-spec.html#multipoint)
pub struct MultiPoint {
    pub points: Vec<Point>,
}

impl ToJson for MultiPoint {
    fn to_json(&self) -> json::Json {
        let coordinates: Vec<Position> =
            self.points.iter().map(|p| p.coordinates.clone()).collect();
        let mut d = TreeMap::new();
        d.insert("type".to_string(), "MultiPoint".to_string().to_json());
        d.insert("coordinates".to_string(), coordinates.to_json());
        d.to_json()
    }
}

/// LineString
///
/// [GeoJSON Format Specification § 2.1.4](http://geojson.org/geojson-spec.html#linestring)
pub struct LineString {
    pub coordinates: Vec<Position>,
}

impl ToJson for LineString {
    fn to_json(&self) -> json::Json {
        let mut d = TreeMap::new();
        d.insert("type".to_string(), "LineString".to_string().to_json());
        d.insert("coordinates".to_string(), self.coordinates.to_json());
        d.to_json()
    }
}

/// MultiLineString
///
/// [GeoJSON Format Specification § 2.1.5](http://geojson.org/geojson-spec.html#multilinestring)
pub struct MultiLineString {
    pub line_strings: Vec<LineString>,
}

impl ToJson for MultiLineString {
    fn to_json(&self) -> json::Json {
        let coordinates: Vec<Vec<Position>> =
            self.line_strings.iter().map(|l| l.coordinates.clone()).collect();
        let mut d = TreeMap::new();
        d.insert("type".to_string(), "MultiLineString".to_string().to_json());
        d.insert("coordinates".to_string(), coordinates.to_json());
        d.to_json()
    }
}

/// Polygon
///
/// [GeoJSON Format Specification § 2.1.6](http://geojson.org/geojson-spec.html#polygon)
pub struct Polygon {
    pub exterior: Vec<Position>,
    pub holes: Vec<Vec<Position>>,  // TODO: this should be optional
}

impl Polygon {
    fn coordinates(&self) -> Vec<Vec<Position>> {
        let mut coordinates = self.holes.clone();
        coordinates.insert(0, self.exterior.clone());
        coordinates
    }
}

impl ToJson for Polygon {
    fn to_json(&self) -> json::Json {
        let mut d = TreeMap::new();
        d.insert("type".to_string(), "Polygon".to_string().to_json());
        d.insert("coordinates".to_string(), self.coordinates().to_json());
        d.to_json()
    }
}

/// MultiPolygon
///
/// [GeoJSON Format Specification § 2.1.7](http://geojson.org/geojson-spec.html#multipolygon)
pub struct MultiPolygon {
    pub polygons: Vec<Polygon>,
}

impl ToJson for MultiPolygon {
    fn to_json(&self) -> json::Json {
        let coordinates: Vec<Vec<Vec<Position>>> =
            self.polygons.iter().map(|p| p.coordinates()).collect();
        let mut d = TreeMap::new();
        d.insert("type".to_string(), "MultiPolygon".to_string().to_json());
        d.insert("coordinates".to_string(), coordinates.to_json());
        d.to_json()
    }
}

/// Geometry
pub enum Geometry {
    Point(Point),
    MultiPoint(MultiPoint),
    LineString(LineString),
    MultiLineString(MultiLineString),
    Polygon(Polygon),
    MultiPolygon(MultiPolygon),
    GeometryCollection(GeometryCollection),
}

impl ToJson for Geometry {
    fn to_json(&self) -> json::Json {
        match *self {
            Point(ref geom) => geom.to_json(),
            MultiPoint(ref geom) => geom.to_json(),
            LineString(ref geom) => geom.to_json(),
            MultiLineString(ref geom) => geom.to_json(),
            Polygon(ref geom) => geom.to_json(),
            MultiPolygon(ref geom) => geom.to_json(),
            GeometryCollection(ref geom) => geom.to_json(),
        }
    }
}

/// GeometryCollection
///
/// [GeoJSON Format Specification § 2.1.8](http://geojson.org/geojson-spec.html#geometry-collection)
pub struct GeometryCollection {
    geometries: Vec<Geometry>,
}

impl ToJson for GeometryCollection {
    fn to_json(&self) -> json::Json {
        let mut d = TreeMap::new();
        d.insert("type".to_string(), "GeometryCollection".to_string().to_json());
        d.insert("geometries".to_string(), self.geometries.to_json());
        d.to_json()
    }
}

/// Feature
///
/// [GeoJSON Format Specification § 2.2](http://geojson.org/geojson-spec.html#feature-objects)
pub struct Feature {
    geometry: Geometry,
    properties: json::Json,
}

impl ToJson for Feature {
    fn to_json(&self) -> json::Json {
        let mut d = TreeMap::new();
        d.insert("type".to_string(), "Feature".to_string().to_json());
        d.insert("geometry".to_string(), self.geometry.to_json());
        d.insert("properties".to_string(), self.properties.to_json());
        d.to_json()
    }
}

/// FeatureCollection
///
/// [GeoJSON Format Specification § 2.3](http://geojson.org/geojson-spec.html#feature-collection-objects)
pub struct FeatureCollection {
    features: Vec<Feature>,
}

impl ToJson for FeatureCollection {
    fn to_json(&self) -> json::Json {
        let mut d = TreeMap::new();
        d.insert("type".to_string(), "FeatureCollection".to_string().to_json());
        d.insert("features".to_string(), self.features.to_json());
        d.to_json()
    }
}

fn main() {
    let point = Point {
        coordinates: Position(vec![1., 2., 3.]),
    };

    let j: json::Json = point.to_json();
    let s: String = j.to_pretty_str();

    println!("{}", s);
}
<|endoftext|>


<commit_before>use mob;
use std::rand::Rng;
use world::{ World, Direction, TileKind, Pos };

#[deriving(Clone, Show)]
pub enum Action {
    Exit,
    Unknown(String),

    Walk(Direction),
    Open(Direction),
    Close(Direction),
    Rest,
    Auto,
    Look
}

pub struct Game {
    pub world: World,
    pub mobs:  Vec<mob::Mob>
}

/// Combined information about what's going on on a tile
/// which Mob, Items, etc
///
/// its state is frozen during creation and contains no references, so it can be easily copied around
#[deriving(Clone)]
struct TileInfo {
    is_walkable: bool,
    kind: TileKind,
    mob: Option<mob::Mob>,
    pos: Pos
}

impl TileInfo {
    fn new(game: &Game, pos: Pos) -> TileInfo {
        let tile = game.world.at(pos);
        let mob  = TileInfo::mob_at(&game.mobs, pos);
        TileInfo { pos: pos, is_walkable: tile.is_walkable(), mob: mob, kind: tile.kind }
    }

    fn mob_at(monsters: &Vec<mob::Mob>, pos: Pos) -> Option<mob::Mob> {
        monsters.iter().skip(1).find(|mob| mob.pos == pos).map(|mob| mob.clone())
    }
}

impl Game {
    pub fn new() -> Game {
        Game { world: World::new(), mobs: vec![] }
    }

    pub fn add_mob(&mut self, mob: mob::Mob) {
        self.mobs.push(mob)
    }

    pub fn act(&mut self, action: Action) {
        println!("{}", action);

        match action {
            Action::Walk(direction)  => self.walk(direction),
            Action::Open(direction)  => self.open_close(direction),
            Action::Close(direction) => self.open_close(direction),
            Action::Rest             => self.mobs[0].dec_hp(1),
            Action::Auto             => self.auto(),
            Action::Look             => self.look(),
            _  => ()
        }
    }

    fn walk(&mut self, dir: Direction) {
        let dst  = World::destination(self.current_pos(), dir);
        let tile = self.tile_info(dst);

        match tile {
            TileInfo { is_walkable: true, mob: Some(_), ..} => self.attack(dst),
            TileInfo { is_walkable: true, mob: None, ..}    => self.move_hero(dst),
            _ => println!("There is a {}", tile.kind)
        }
    }

    fn current_pos(&mut self) -> Pos {
        self.mobs[0].pos
    }

    fn tile_info(&mut self, pos: Pos) -> TileInfo {
        TileInfo::new(self, pos)
    }

    fn attack(&mut self, pos: Pos) {
        let (victim, dmg) = {
            let hero   = self.hero();
            let victim = self.mob_idx_at(pos).unwrap();

            let dmg = Game::dmg(hero);
            (victim, dmg)
        };

        let mut monster = &mut self.mobs[victim];
        monster.dec_hp(dmg);
        println!("You hit {}, the {} for {}/{} hp!", monster.name, monster.kind, dmg, monster.hp);
    }

    fn mob_idx_at(&self, pos: Pos) -> Option<uint> {
        self.mobs.iter().position(|m| m.pos == pos)
    }

    fn move_hero(&mut self, pos: Pos) {
        self.hero_mut().goto(pos);
    }

    fn hero(&self) -> &mob::Mob {
        &self.mobs[0]
    }

    fn hero_mut(&mut self) -> &mut mob::Mob {
        &mut self.mobs[0]
    }

    fn dmg(mob: &mob::Mob) -> uint {
        let mut rng = ::std::rand::task_rng();
        rng.gen_range(1u, mob.str as uint)
    }

    fn open_close(&mut self, dir: Direction) {
        let pos = self.mobs[0].pos;
        let dst = World::destination(pos, dir);
        let tile = self.world.at(dst).kind;
        match tile {
            TileKind::DoorClosed => self.world.set(dst, TileKind::DoorOpen),
            TileKind::DoorOpen   => self.world.set(dst, TileKind::DoorClosed),
            _ => ()
        }
    }

    fn auto(&mut self) {
        let pos = self.mobs[0].pos;
        let ns  = self.world.adjacent(pos);

        for n in ns.iter() {
            let (ref dir, ref tile_kind) = *n;

            match *tile_kind {
                TileKind::DoorClosed => self.open_close(*dir),
                TileKind::DoorOpen   => self.open_close(*dir),
                _ => ()
            }
            println!("{} -> {}", dir.clone(), tile_kind)
        }
    }

    fn look(&mut self) {
        println!("Look around");
    }
}
<commit_msg>fix game: Update for rust nightly<commit_after>use mob;
use std::rand::Rng;
use world::{ World, Direction, TileKind, Pos };

#[deriving(Clone, Show)]
pub enum Action {
    Exit,
    Unknown(String),

    Walk(Direction),
    Open(Direction),
    Close(Direction),
    Rest,
    Auto,
    Look
}

pub struct Game {
    pub world: World,
    pub mobs:  Vec<mob::Mob>
}

/// Combined information about what's going on on a tile
/// which Mob, Items, etc
///
/// its state is frozen during creation and contains no references, so it can be easily copied around
#[deriving(Clone)]
struct TileInfo {
    is_walkable: bool,
    kind: TileKind,
    mob: Option<mob::Mob>,
    pos: Pos
}

impl TileInfo {
    fn new(game: &Game, pos: Pos) -> TileInfo {
        let tile = game.world.at(pos);
        let mob  = TileInfo::mob_at(&game.mobs, pos);
        TileInfo { pos: pos, is_walkable: tile.is_walkable(), mob: mob, kind: tile.kind }
    }

    fn mob_at(monsters: &Vec<mob::Mob>, pos: Pos) -> Option<mob::Mob> {
        monsters.iter().skip(1).find(|mob| mob.pos == pos).map(|mob| mob.clone())
    }
}

impl Game {
    pub fn new() -> Game {
        Game { world: World::new(), mobs: vec![] }
    }

    pub fn add_mob(&mut self, mob: mob::Mob) {
        self.mobs.push(mob)
    }

    pub fn act(&mut self, action: Action) {
        println!("{}", action);

        match action {
            Action::Walk(direction)  => self.walk(direction),
            Action::Open(direction)  => self.open_close(direction),
            Action::Close(direction) => self.open_close(direction),
            Action::Rest             => self.mobs[0].dec_hp(1),
            Action::Auto             => self.auto(),
            Action::Look             => self.look(),
            _  => ()
        }
    }

    fn walk(&mut self, dir: Direction) {
        let dst  = World::destination(self.current_pos(), dir);
        let tile = self.tile_info(dst);

        match tile {
            TileInfo { is_walkable: true, mob: Some(_), ..} => self.attack(dst),
            TileInfo { is_walkable: true, mob: None, ..}    => self.move_hero(dst),
            _ => println!("There is a {}", tile.kind)
        }
    }

    fn current_pos(&mut self) -> Pos {
        self.mobs[0].pos
    }

    fn tile_info(&mut self, pos: Pos) -> TileInfo {
        TileInfo::new(self, pos)
    }

    fn attack(&mut self, pos: Pos) {
        let (victim, dmg) = {
            let hero   = self.hero();
            let victim = self.mob_idx_at(pos).unwrap();

            let dmg = Game::dmg(hero);
            (victim, dmg)
        };

        let mut monster = &mut self.mobs[victim];
        monster.dec_hp(dmg);
        println!("You hit {}, the {} for {}/{} hp!", monster.name, monster.kind, dmg, monster.hp);
    }

    fn mob_idx_at(&self, pos: Pos) -> Option<uint> {
        self.mobs.iter().position(|m| m.pos == pos)
    }

    fn move_hero(&mut self, pos: Pos) {
        self.hero_mut().goto(pos);
    }

    fn hero(&self) -> &mob::Mob {
        &self.mobs[0]
    }

    fn hero_mut(&mut self) -> &mut mob::Mob {
        &mut self.mobs[0]
    }

    fn dmg(mob: &mob::Mob) -> uint {
        let mut rng = ::std::rand::thread_rng();
        rng.gen_range(1u, mob.str as uint)
    }

    fn open_close(&mut self, dir: Direction) {
        let pos = self.mobs[0].pos;
        let dst = World::destination(pos, dir);
        let tile = self.world.at(dst).kind;
        match tile {
            TileKind::DoorClosed => self.world.set(dst, TileKind::DoorOpen),
            TileKind::DoorOpen   => self.world.set(dst, TileKind::DoorClosed),
            _ => ()
        }
    }

    fn auto(&mut self) {
        let pos = self.mobs[0].pos;
        let ns  = self.world.adjacent(pos);

        for n in ns.iter() {
            let (ref dir, ref tile_kind) = *n;

            match *tile_kind {
                TileKind::DoorClosed => self.open_close(*dir),
                TileKind::DoorOpen   => self.open_close(*dir),
                _ => ()
            }
            println!("{} -> {}", dir.clone(), tile_kind)
        }
    }

    fn look(&mut self) {
        println!("Look around");
    }
}
<|endoftext|>


<commit_before>extern crate log4rs;
extern crate hyper;

#[macro_use] extern crate nickel;
use nickel::{Nickel, HttpRouter, Request, Response, MiddlewareResult, MediaType, QueryString};
use nickel::status::StatusCode;
use hyper::header::Location;

use hyper::client::{Client,RequestBuilder};
use std::io::Read;

extern crate json;
use json::iterators::Members;

extern crate mustache;
use mustache::MapBuilder;

extern crate itertools;
use itertools::Itertools;
extern crate time;

// https://api.tfl.gov.uk/StopPoint?lat=51.423896&lon=-0.045525&stopTypes=NaptanOnstreetBusCoachStopPair&radius=300

fn json_for_request(rb: RequestBuilder) -> Result<json::JsonValue, String> {
    let mut res = match rb.send()
    {
        Ok(val) => val,
        Err(_) => {
            return Err("Can't connect to TfL".to_string());
        }
    };
    let mut buffer: String = String::new();
    res.read_to_string(&mut buffer).expect("can't read");
    let obj = match json::parse(&buffer)
    {
        Ok(val) => val,
        Err(_) => {
            return Err(format!("Bad json: {} ", buffer));
        }
    };
    return Ok(obj);
}

fn render_to_response<'a, D>(mut response: Response<'a, D>, path: &str, data: &mustache::Data) -> MiddlewareResult<'a, D> {
    let template = mustache::compile_path(path).expect("working template");
    let mut buffer: Vec<u8> = vec![];
    template.render_data(&mut buffer, data);
    response.set(MediaType::Html);
    response.send(buffer)
}

fn arrivals_handler<'a, D>(request: &mut Request<D>, mut response: Response<'a, D>) -> MiddlewareResult<'a, D> {
    let client = Client::new();
    let obj = match json_for_request(client.get(&format!("https://api.tfl.gov.uk/StopPoint/{}/Arrivals",
        request.param("stopid").unwrap_or("Missing stopid"))))
    {
        Ok(val) => val,
        Err(val) => {
            response.set(StatusCode::BadGateway);
            return response.send(val);
        }
    };

    let members = match obj.members() {
        Members::Some(val) => val,
        Members::None => {
            response.set(StatusCode::BadGateway);
            return response.send(format!("No stops in: {:?}", obj));
        }
    };
    let member_slice = members.as_slice();
    let last_item = member_slice[0].clone();
    let sorted_members = members.sorted_by(|a, b| a["expectedArrival"].as_str().unwrap().cmp(b["expectedArrival"].as_str().unwrap()));

    let data = MapBuilder::new()
        .insert_vec("buses", |vecbuilder| {
            let mut vecb = vecbuilder;
            for stop in sorted_members.clone() {
                vecb = vecb.push_map(|mapbuilder| {
                    let when = time::strptime(stop["expectedArrival"].as_str().unwrap(), "%FT%TZ").unwrap();
                    mapbuilder
                        .insert_str("line", stop["lineName"].as_str().unwrap())
                        .insert_str("destination", stop["destinationName"].as_str().unwrap())
                        .insert_str("towards", stop["towards"].as_str().unwrap())
                        .insert_str("expectedArrival", when.to_local().strftime("%H:%M").unwrap())
                });
            }
            vecb
        })
        .insert_str("stopName", last_item["stationName"].as_str().unwrap())
        .insert_str("stopNumber", last_item["platformName"].as_str().unwrap())
        .build();

    render_to_response(response, "resources/templates/arrivals.mustache", &data)
}

fn root_handler<'a, D>(_: &mut Request<D>, mut response: Response<'a, D>) -> MiddlewareResult<'a, D> {
    let data = MapBuilder::new().build();
    render_to_response(response, "resources/templates/root.mustache", &data)
}

fn search_handler<'a, D>(request: &mut Request<D>, mut response: Response<'a, D>) -> MiddlewareResult<'a, D> {
    //https://api.tfl.gov.uk/StopPoint/Search/knighton?faresOnly=False&app_id=&app_key=
    let client = Client::new();
    let query = request.query().get("query").expect("Missing query");
    let url = &format!("https://api.tfl.gov.uk/StopPoint/Search/{}?modes=bus", query);
    let obj = match json_for_request(client.get(url))
    {
        Ok(val) => val,
        Err(val) => {
            response.set(StatusCode::BadGateway);
            return response.send(val);
        }
    };

    let data = MapBuilder::new()
        .insert_vec("stops", |vecbuilder| {
            let mut vecb = vecbuilder;
            for stop in obj["matches"].members() {
                vecb = vecb.push_map(|mapbuilder| {
                    mapbuilder
                        .insert_str("id", stop["id"].as_str().unwrap())
                        .insert_str("name", stop["name"].as_str().unwrap())
                });
            }
            vecb
        })
        .insert_str("query", query)
        .build();
    render_to_response(response, "resources/templates/search.mustache", &data)
}

fn id_handler<'a, D>(request: &mut Request<D>, mut response: Response<'a, D>) -> MiddlewareResult<'a, D> {
    //https://api.tfl.gov.uk/StopPoint/Search/knighton?faresOnly=False&app_id=&app_key=
    let client = Client::new();
    let query = request.param("id").expect("Missing id");
    let url = &format!("https://api.tfl.gov.uk/StopPoint/{}", query);
    let obj = match json_for_request(client.get(url))
    {
        Ok(val) => val,
        Err(val) => {
            response.set(StatusCode::BadGateway);
            return response.send(val);
        }
    };

    let id = &obj["lineGroup"][0]["naptanIdReference"];
    match id {
        &json::JsonValue::Null => panic!("null!"),
        &json::JsonValue::String(ref val) => {
            response.set(Location(format!("/arrivals/{}", val)))
                    .set(StatusCode::PermanentRedirect);
        }
        _ => panic!("Something else!")
    };
    response.send("")
}

fn main() {
    /*let gridref = osgridref::OsGridRef::new(535987 as f32,171440 as f32);
    let latlon = gridref.to_lat_lon(datum::WGS84);
    println!("{:?}", latlon);*/
    log4rs::init_file("log.yaml", Default::default()).unwrap();
    let mut server = Nickel::new();
    let mut router = Nickel::router();

    router.get("/", root_handler);
    router.get("/search", search_handler);
    router.get("/id/:id", id_handler);
    router.get("/arrivals/:stopid", arrivals_handler);

    server.utilize(router);
    let port = 8000;
    server.listen(("127.0.0.1", port));
}
<commit_msg>Cope if no results<commit_after>extern crate log4rs;
extern crate hyper;

#[macro_use] extern crate nickel;
use nickel::{Nickel, HttpRouter, Request, Response, MiddlewareResult, MediaType, QueryString};
use nickel::status::StatusCode;
use hyper::header::Location;

use hyper::client::{Client,RequestBuilder};
use std::io::Read;

extern crate json;
use json::iterators::Members;

extern crate mustache;
use mustache::MapBuilder;

extern crate itertools;
use itertools::Itertools;
extern crate time;

// https://api.tfl.gov.uk/StopPoint?lat=51.423896&lon=-0.045525&stopTypes=NaptanOnstreetBusCoachStopPair&radius=300

fn json_for_request(rb: RequestBuilder) -> Result<json::JsonValue, String> {
    let mut res = match rb.send()
    {
        Ok(val) => val,
        Err(_) => {
            return Err("Can't connect to TfL".to_string());
        }
    };
    let mut buffer: String = String::new();
    res.read_to_string(&mut buffer).expect("can't read");
    let obj = match json::parse(&buffer)
    {
        Ok(val) => val,
        Err(_) => {
            return Err(format!("Bad json: {} ", buffer));
        }
    };
    return Ok(obj);
}

fn render_to_response<'a, D>(mut response: Response<'a, D>, path: &str, data: &mustache::Data) -> MiddlewareResult<'a, D> {
    let template = mustache::compile_path(path).expect("working template");
    let mut buffer: Vec<u8> = vec![];
    template.render_data(&mut buffer, data);
    response.set(MediaType::Html);
    response.send(buffer)
}

fn arrivals_handler<'a, D>(request: &mut Request<D>, mut response: Response<'a, D>) -> MiddlewareResult<'a, D> {
    let client = Client::new();
    let obj = match json_for_request(client.get(&format!("https://api.tfl.gov.uk/StopPoint/{}/Arrivals",
        request.param("stopid").unwrap_or("Missing stopid"))))
    {
        Ok(val) => val,
        Err(val) => {
            response.set(StatusCode::BadGateway);
            return response.send(val);
        }
    };

    let members = match obj.members() {
        Members::Some(val) => val,
        Members::None => {
            response.set(StatusCode::BadGateway);
            return response.send(format!("No stops in: {:?}", obj));
        }
    };
    let member_slice = members.as_slice();
    let data = {
        if member_slice.len() == 0 {
            MapBuilder::new().build()
        }
        else {
            let last_item = member_slice[0].clone();
            let sorted_members = members.sorted_by(|a, b| a["expectedArrival"].as_str().unwrap().cmp(b["expectedArrival"].as_str().unwrap()));

            MapBuilder::new()
                .insert_vec("buses", |vecbuilder| {
                    let mut vecb = vecbuilder;
                    for stop in sorted_members.clone() {
                        vecb = vecb.push_map(|mapbuilder| {
                            let when = time::strptime(stop["expectedArrival"].as_str().unwrap(), "%FT%TZ").unwrap();
                            mapbuilder
                                .insert_str("line", stop["lineName"].as_str().unwrap())
                                .insert_str("destination", stop["destinationName"].as_str().unwrap())
                                .insert_str("towards", stop["towards"].as_str().unwrap())
                                .insert_str("expectedArrival", when.to_local().strftime("%H:%M").unwrap())
                        });
                    }
                    vecb
                })
                .insert_str("stopName", last_item["stationName"].as_str().unwrap())
                .insert_str("stopNumber", last_item["platformName"].as_str().unwrap())
                .build()
            }
        };

    render_to_response(response, "resources/templates/arrivals.mustache", &data)
}

fn root_handler<'a, D>(_: &mut Request<D>, mut response: Response<'a, D>) -> MiddlewareResult<'a, D> {
    let data = MapBuilder::new().build();
    render_to_response(response, "resources/templates/root.mustache", &data)
}

fn search_handler<'a, D>(request: &mut Request<D>, mut response: Response<'a, D>) -> MiddlewareResult<'a, D> {
    //https://api.tfl.gov.uk/StopPoint/Search/knighton?faresOnly=False&app_id=&app_key=
    let client = Client::new();
    let query = request.query().get("query").expect("Missing query");
    let url = &format!("https://api.tfl.gov.uk/StopPoint/Search/{}?modes=bus", query);
    let obj = match json_for_request(client.get(url))
    {
        Ok(val) => val,
        Err(val) => {
            response.set(StatusCode::BadGateway);
            return response.send(val);
        }
    };

    let data = MapBuilder::new()
        .insert_vec("stops", |vecbuilder| {
            let mut vecb = vecbuilder;
            for stop in obj["matches"].members() {
                vecb = vecb.push_map(|mapbuilder| {
                    mapbuilder
                        .insert_str("id", stop["id"].as_str().unwrap())
                        .insert_str("name", stop["name"].as_str().unwrap())
                });
            }
            vecb
        })
        .insert_str("query", query)
        .build();
    render_to_response(response, "resources/templates/search.mustache", &data)
}

fn id_handler<'a, D>(request: &mut Request<D>, mut response: Response<'a, D>) -> MiddlewareResult<'a, D> {
    //https://api.tfl.gov.uk/StopPoint/Search/knighton?faresOnly=False&app_id=&app_key=
    let client = Client::new();
    let query = request.param("id").expect("Missing id");
    let url = &format!("https://api.tfl.gov.uk/StopPoint/{}", query);
    let obj = match json_for_request(client.get(url))
    {
        Ok(val) => val,
        Err(val) => {
            response.set(StatusCode::BadGateway);
            return response.send(val);
        }
    };

    let id = &obj["lineGroup"][0]["naptanIdReference"];
    match id {
        &json::JsonValue::Null => panic!("null!"),
        &json::JsonValue::String(ref val) => {
            response.set(Location(format!("/arrivals/{}", val)))
                    .set(StatusCode::PermanentRedirect);
        }
        _ => panic!("Something else!")
    };
    response.send("")
}

fn main() {
    /*let gridref = osgridref::OsGridRef::new(535987 as f32,171440 as f32);
    let latlon = gridref.to_lat_lon(datum::WGS84);
    println!("{:?}", latlon);*/
    log4rs::init_file("log.yaml", Default::default()).unwrap();
    let mut server = Nickel::new();
    let mut router = Nickel::router();

    router.get("/", root_handler);
    router.get("/search", search_handler);
    router.get("/id/:id", id_handler);
    router.get("/arrivals/:stopid", arrivals_handler);

    server.utilize(router);
    let port = 8000;
    server.listen(("127.0.0.1", port));
}
<|endoftext|>


<commit_before>extern crate sdl2;
extern crate vecmath;
extern crate crossbeam;

mod vec3d;
mod light;
mod scene;
mod material;
mod color;
mod shape;
mod sphere;
mod plane;

use scene::Scene;

use sdl2::pixels::PixelFormatEnum;
use sdl2::rect::Rect;
use sdl2::EventPump;
use sdl2::event::Event;
use sdl2::keyboard::Keycode;
use std::thread;

// Return true for quit
fn handle_events(scene: &mut Scene,
                 resolution: u32,
                 event_pump: &mut EventPump,
                 x: i32,
                 y: i32)
                 -> bool {
    for event in event_pump.poll_iter() {
        match event {
            Event::Quit { .. } |
            Event::KeyDown { keycode: Some(Keycode::Escape), .. } => return true,
            Event::KeyDown { keycode: Some(Keycode::W), .. } => {
                scene.mv_camera_fwd();
            }
            Event::KeyDown { keycode: Some(Keycode::S), .. } => {
                scene.mv_camera_back();
            }
            _ => {}
        }
    }

    // TODO: Y rotation is incorrect
    let x_dist = ((resolution as i32) / 2) - x;
    let y_dist = ((resolution as i32) / 2) - y;
    let x_rot = x_dist as f64 * (-0.001);
    let y_rot = y_dist as f64 * (-0.001);
    scene.rot_camera(x_rot, y_rot);
    false
}

// Test scene
fn test_scene() -> Scene {
    use light::Light;
    use vec3d::Vec3d;
    use sphere::Sphere;
    use plane::Plane;

    let camera_pos: Vec3d = [0.0, 0.0, -100.0];

    let camera_dir: Vec3d = [0.0, 0.0, 1.0];
    let camera_up: Vec3d = [0.0, 1.0, 0.0];
    let light_pos: Vec3d = [50.0, 5.0, -100.0];

    let light = Light::new(light_pos, color::Color(0.6, 0.6, 0.6));
    let mut scene = Scene::new(light, camera_pos, camera_dir, camera_up);
    scene.add_shape(Box::new(Sphere::new([-20.0, -15.0, 45.0], 5.0, color::Color(1.0, 0.5, 0.5))));
    scene.add_shape(Box::new(Sphere::new([15.0, -10.0, 35.0], 5.0, color::Color(0.4, 0.4, 0.4))));
    scene.add_shape(Box::new(Sphere::new([0.0, -10.0, 25.0], 5.0, color::Color(0.5, 0.5, 1.0))));
    scene.add_shape(Box::new(Sphere::new([0.0, -120.0, 55.0], 100.0, color::WHITE)));
    scene.add_shape(Box::new(Sphere::new([0.0, 120.0, 55.0], 100.0, color::Color(1.0, 1.0, 0.0))));
    scene.add_shape(Box::new(Sphere::new([0.0, 0.0, 200.0], 100.0, color::Color(1.0, 0.5, 1.0))));
    scene.add_shape(Box::new(Sphere::from_material([15.0, 0.0, 40.0], 5.0, material::MIRROR)));
    scene.add_shape(Box::new(Sphere::new([-45.0, 0.0, 45.0], 20.0, color::Color(0.1, 0.1, 0.1))));

    scene.add_shape(Box::new(Plane::new([0.0, -200.0, 0.0], [0.0, 1.0, 0.0], color::Color(0.4, 0.4, 0.4))));
    scene.add_shape(Box::new(Plane::new([0.0, 200.0, 0.0], [0.0, -1.0, 0.0], color::Color(0.4, 0.4, 0.4))));
    scene.add_shape(Box::new(Plane::from_material([200.0, 0.0, 0.0], [-1.0, 0.0, 0.0], material::MIRROR)));
    scene.add_shape(Box::new(Plane::new([-200.0, 0.0, 0.0], [1.0, 0.0, 0.0], color::Color(0.8, 0.4, 0.8))));
    scene.add_shape(Box::new(Plane::new([0.0, 0.0, 200.0], [0.0, 0.0, -1.0], color::Color(0.4, 0.4, 0.4))));
    scene.add_shape(Box::new(Plane::new([0.0, 0.0, -200.0], [0.0, 0.0, 1.0], color::Color(0.4, 0.4, 0.4))));
    scene
}

// A simple test code that uses SDL for rendering
fn main() {
    let resolution: u32 = 512;
    let mut scene = test_scene();
    let thread_cnt = 4;

    // Stuff from sdl2-rust example
    let sdl_context = sdl2::init().unwrap();
    let video_subsystem = sdl_context.video().unwrap();

    let window = video_subsystem.window("Ray", resolution, resolution)
        .position_centered()
        .opengl()
        .build()
        .unwrap();

    let mut renderer = window.renderer().build().unwrap();

    let mut texture =
        renderer.create_texture_streaming(PixelFormatEnum::RGB24, resolution, resolution)
            .unwrap();

    let mut event_pump = sdl_context.event_pump().unwrap();

    loop {
        let (_, x, y) = sdl_context.mouse().mouse_state();
        let quit = handle_events(&mut scene, resolution, &mut event_pump, x, y);
        if quit {
            break;
        }

        scene.step();

        texture.with_lock(None, |buffer: &mut [u8], pitch: usize| {
                let sc = &scene;
                let res_u = resolution as usize;
                for y in (0..(res_u / thread_cnt) + 1).map(|x| x * thread_cnt) {
                    let threads = if y + thread_cnt <= res_u {
                        thread_cnt
                    } else {
                        res_u % thread_cnt
                    };

                    if threads == 0 {
                        break;
                    }

                    let mut lines : Vec<Vec<(u8, u8, u8)>> = Vec::new();
                    for _ in (0..threads) {
                        lines.push(Vec::new());
                    }

                    crossbeam::scope(|scope| {
                        for (thread_num, vec) in lines.iter_mut().enumerate() {
                            scope.spawn(move || {
                                sc.line_iter_u8(res_u, res_u, y + thread_num).fold((), |(), x| vec.push(x));
                            });
                        }
                    });

                    for (thread_num, vec) in lines.iter_mut().enumerate() {
                        let line_start = (thread_num + y) * pitch;
                        let line_end = line_start + res_u * 3;
                        let line_buf = &mut buffer[line_start..line_end];
                        for (c, offset) in vec.iter().zip((0..).map(|x| 3 * x)) {
                            line_buf[offset + 0] = c.0;
                            line_buf[offset + 1] = c.1;
                            line_buf[offset + 2] = c.2;
                        }
                    }
                }
            })
            .unwrap();
        renderer.clear();
        renderer.copy(&texture,
                  None,
                  Some(Rect::new(0, 0, resolution, resolution)))
            .unwrap();
        renderer.present();
    }
}
<commit_msg>Allocate vectors once<commit_after>extern crate sdl2;
extern crate vecmath;
extern crate crossbeam;

mod vec3d;
mod light;
mod scene;
mod material;
mod color;
mod shape;
mod sphere;
mod plane;

use scene::Scene;

use sdl2::pixels::PixelFormatEnum;
use sdl2::rect::Rect;
use sdl2::EventPump;
use sdl2::event::Event;
use sdl2::keyboard::Keycode;
use std::thread;

// Return true for quit
fn handle_events(scene: &mut Scene,
                 resolution: u32,
                 event_pump: &mut EventPump,
                 x: i32,
                 y: i32)
                 -> bool {
    for event in event_pump.poll_iter() {
        match event {
            Event::Quit { .. } |
            Event::KeyDown { keycode: Some(Keycode::Escape), .. } => return true,
            Event::KeyDown { keycode: Some(Keycode::W), .. } => {
                scene.mv_camera_fwd();
            }
            Event::KeyDown { keycode: Some(Keycode::S), .. } => {
                scene.mv_camera_back();
            }
            _ => {}
        }
    }

    // TODO: Y rotation is incorrect
    let x_dist = ((resolution as i32) / 2) - x;
    let y_dist = ((resolution as i32) / 2) - y;
    let x_rot = x_dist as f64 * (-0.001);
    let y_rot = y_dist as f64 * (-0.001);
    scene.rot_camera(x_rot, y_rot);
    false
}

// Test scene
fn test_scene() -> Scene {
    use light::Light;
    use vec3d::Vec3d;
    use sphere::Sphere;
    use plane::Plane;

    let camera_pos: Vec3d = [0.0, 0.0, -100.0];

    let camera_dir: Vec3d = [0.0, 0.0, 1.0];
    let camera_up: Vec3d = [0.0, 1.0, 0.0];
    let light_pos: Vec3d = [50.0, 5.0, -100.0];

    let light = Light::new(light_pos, color::Color(0.6, 0.6, 0.6));
    let mut scene = Scene::new(light, camera_pos, camera_dir, camera_up);
    scene.add_shape(Box::new(Sphere::new([-20.0, -15.0, 45.0], 5.0, color::Color(1.0, 0.5, 0.5))));
    scene.add_shape(Box::new(Sphere::new([15.0, -10.0, 35.0], 5.0, color::Color(0.4, 0.4, 0.4))));
    scene.add_shape(Box::new(Sphere::new([0.0, -10.0, 25.0], 5.0, color::Color(0.5, 0.5, 1.0))));
    scene.add_shape(Box::new(Sphere::new([0.0, -120.0, 55.0], 100.0, color::WHITE)));
    scene.add_shape(Box::new(Sphere::new([0.0, 120.0, 55.0], 100.0, color::Color(1.0, 1.0, 0.0))));
    scene.add_shape(Box::new(Sphere::new([0.0, 0.0, 200.0], 100.0, color::Color(1.0, 0.5, 1.0))));
    scene.add_shape(Box::new(Sphere::from_material([15.0, 0.0, 40.0], 5.0, material::MIRROR)));
    scene.add_shape(Box::new(Sphere::new([-45.0, 0.0, 45.0], 20.0, color::Color(0.1, 0.1, 0.1))));

    scene.add_shape(Box::new(Plane::new([0.0, -200.0, 0.0], [0.0, 1.0, 0.0], color::Color(0.4, 0.4, 0.4))));
    scene.add_shape(Box::new(Plane::new([0.0, 200.0, 0.0], [0.0, -1.0, 0.0], color::Color(0.4, 0.4, 0.4))));
    scene.add_shape(Box::new(Plane::from_material([200.0, 0.0, 0.0], [-1.0, 0.0, 0.0], material::MIRROR)));
    scene.add_shape(Box::new(Plane::new([-200.0, 0.0, 0.0], [1.0, 0.0, 0.0], color::Color(0.8, 0.4, 0.8))));
    scene.add_shape(Box::new(Plane::new([0.0, 0.0, 200.0], [0.0, 0.0, -1.0], color::Color(0.4, 0.4, 0.4))));
    scene.add_shape(Box::new(Plane::new([0.0, 0.0, -200.0], [0.0, 0.0, 1.0], color::Color(0.4, 0.4, 0.4))));
    scene
}

// A simple test code that uses SDL for rendering
fn main() {
    let resolution: u32 = 512;
    let mut scene = test_scene();
    let thread_cnt = 4;

    // Stuff from sdl2-rust example
    let sdl_context = sdl2::init().unwrap();
    let video_subsystem = sdl_context.video().unwrap();

    let window = video_subsystem.window("Ray", resolution, resolution)
        .position_centered()
        .opengl()
        .build()
        .unwrap();

    let mut renderer = window.renderer().build().unwrap();

    let mut texture =
        renderer.create_texture_streaming(PixelFormatEnum::RGB24, resolution, resolution)
            .unwrap();

    let mut event_pump = sdl_context.event_pump().unwrap();

    let mut lines : Vec<Vec<(u8, u8, u8)>> = Vec::new();
    for _ in (0..thread_cnt) {
        lines.push(Vec::with_capacity(resolution as usize));
    }

    loop {
        let (_, x, y) = sdl_context.mouse().mouse_state();
        let quit = handle_events(&mut scene, resolution, &mut event_pump, x, y);
        if quit {
            break;
        }

        scene.step();

        texture.with_lock(None, |buffer: &mut [u8], pitch: usize| {
                let sc = &scene;
                let res_u = resolution as usize;
                for y in (0..(res_u / thread_cnt) + 1).map(|x| x * thread_cnt) {
                    let threads = if y + thread_cnt <= res_u {
                        thread_cnt
                    } else {
                        res_u % thread_cnt
                    };

                    if threads == 0 {
                        break;
                    }

                    crossbeam::scope(|scope| {
                        for (thread_num, vec) in lines[0..threads].iter_mut().enumerate() {
                            scope.spawn(move || {
                                vec.drain(..);
                                sc.line_iter_u8(res_u, res_u, y + thread_num).fold((), |(), x| vec.push(x));
                            });
                        }
                    });

                    for (thread_num, vec) in lines[0..threads].iter_mut().enumerate() {
                        let line_start = (thread_num + y) * pitch;
                        let line_end = line_start + res_u * 3;
                        let line_buf = &mut buffer[line_start..line_end];
                        for (c, offset) in vec.iter().zip((0..).map(|x| 3 * x)) {
                            line_buf[offset + 0] = c.0;
                            line_buf[offset + 1] = c.1;
                            line_buf[offset + 2] = c.2;
                        }
                    }
                }
            })
            .unwrap();
        renderer.clear();
        renderer.copy(&texture,
                  None,
                  Some(Rect::new(0, 0, resolution, resolution)))
            .unwrap();
        renderer.present();
    }
}
<|endoftext|>


<commit_before>#![feature(augmented_assignments)]
#![feature(non_ascii_idents)]
#![feature(pattern)]
#![feature(plugin)]
#![feature(test)]
#![plugin(clippy)]

#![allow(unused_features)]
#![warn(missing_debug_implementations, missing_copy_implementations,
        trivial_casts, trivial_numeric_casts,
        unused_import_braces, unused_qualifications)]

extern crate argparse;
extern crate ansi_term;
#[macro_use] extern crate itertools;
#[macro_use] extern crate log;
extern crate lru_cache;
extern crate rustc_serialize;
extern crate time;

#[macro_use] mod macros;
mod space;
mod identity;
mod motion;
mod landmark;
mod life;
mod mind;
mod substrate;

use std::fs::OpenOptions;
use std::error::Error;
use std::io;
use std::io::Write;
use std::process;

use argparse::{ArgumentParser, Store, StoreOption, StoreTrue, Print};
use log::{LogRecord, LogMetadata, LogLevelFilter, SetLoggerError};
use rustc_serialize::json;
use time::{Duration, get_time};

use identity::{Agent, Team};
use life::{WorldState, Commit, Patch};
use mind::{kickoff, iterative_deepening_kickoff, fixed_depth_sequence_kickoff,
           Variation};
use substrate::memory_free;

struct DebugLogger;

impl DebugLogger {
    pub fn init() -> Result<(), SetLoggerError> {
        log::set_logger(|max_log_level| {
            max_log_level.set(LogLevelFilter::Debug);
            Box::new(DebugLogger)
        })
    }
}

impl log::Log for DebugLogger {
    fn enabled(&self, _metadata: &LogMetadata) -> bool { true }

    fn log(&self, record: &LogRecord) {
        let mut log_file = OpenOptions::new()
            .write(true).append(true).create(true)
            .open("leafline.log").expect("couldn't open log file?!");
        let log_message = format!(
            "[{}] {}\n",
            time::now().strftime("%Y-%m-%d %H:%M:%S.%f").unwrap(),
            record.args()
        );
        log_file.write(&log_message.into_bytes())
            .expect("couldn't write to log file?!");
    }
}

#[derive(Debug, Clone)]
enum LookaheadBound {
    Depth(u8),
    DepthSequence(Vec<u8>),
    Seconds(u8),
}

impl LookaheadBound {
    pub fn duration(&self) -> Duration {
        match *self {
            LookaheadBound::Seconds(secs) => Duration::seconds(secs as i64),
            _ => moral_panic!("`duration()` called on non-Seconds \
                               LookaheadBound variant")
        }
    }

    pub fn new_from_sequence_depiction(depiction: String) -> Self{
        let depth_runes = depiction.split(',');
        let depth_sequence = depth_runes
            .map(|dd| dd.parse::<u8>().expect("couldn't parse depth sequence"))
            .collect::<Vec<_>>();
        LookaheadBound::DepthSequence(depth_sequence)
    }

    pub fn from_args(lookahead_depth: Option<u8>,
                     lookahead_depth_sequence: Option<String>,
                     lookahead_seconds: Option<u8>)
                     -> Result<Option<Self>, String> {
        let mut bound = None;
        let confirm_bound_is_none = |b: &Option<LookaheadBound>|
                                                -> Result<bool, String> {
            if b.is_some() {
                Err("more than one of `--depth`, `--depth-sequence`, \
                     or `--seconds` was passed".to_owned())
            } else {
                Ok(true)
            }
        };
        if let Some(depth) = lookahead_depth {
            try!(confirm_bound_is_none(&bound));
            bound = Some(LookaheadBound::Depth(depth));
        }
        if let Some(sequence_depiction) = lookahead_depth_sequence {
            try!(confirm_bound_is_none(&bound));
            bound = Some(LookaheadBound::new_from_sequence_depiction(
                sequence_depiction));
        }
        if let Some(seconds) = lookahead_seconds {
            try!(confirm_bound_is_none(&bound));
            bound = Some(LookaheadBound::Seconds(seconds));
        }
        Ok(bound)
    }
}

fn forecast(world: WorldState, bound: LookaheadBound, déjà_vu_bound: f32)
            -> (Vec<(Commit, f32, Variation)>, u8, Duration) {
    let start_thinking = get_time();
    let forecasts;
    let depth;
    match bound {
        LookaheadBound::Depth(ds) => {
            forecasts = kickoff(&world, ds, false, déjà_vu_bound);
            depth = ds;
        },
        LookaheadBound::DepthSequence(ds) => {
            depth = *ds.last().unwrap();
            forecasts = fixed_depth_sequence_kickoff(
                &world, ds, false, déjà_vu_bound);
            // XXX TODO: if we're just returning a number, it should be the
            // lowest depth, but we should really report all of them
        },
        LookaheadBound::Seconds(_) => {
            let (fs, ds) = iterative_deepening_kickoff(
                &world, bound.duration(), false, déjà_vu_bound);
            forecasts = fs;
            depth = ds;
        }
    }
    let stop_thinking = get_time();
    let thinking_time = stop_thinking - start_thinking;
    (forecasts, depth, thinking_time)
}

#[derive(RustcEncodable, RustcDecodable)]
struct Postcard {
    world: String,
    patch: Patch,
    hospitalization: Option<Agent>,
    thinking_time: u64,
    depth: u8,
    counterreplies: Vec<Patch>,
}

#[derive(RustcEncodable, RustcDecodable)]
struct LastMissive {
    the_triumphant: Option<Team>
}

fn correspondence(reminder: String, bound: LookaheadBound, déjà_vu_bound: f32) -> String {
    let in_medias_res = WorldState::reconstruct(reminder);
    let (mut forecasts, depth, sidereal) = forecast(in_medias_res, bound,
                                                    déjà_vu_bound);

    if !forecasts.is_empty() {
        let (determination, _karma, _variation) = forecasts.swap_remove(0);
        // XXX TODO FIXME: this doesn't distinguish amongst ascensions
        // (and we can imagine somewhat contrived situations where only
        // some of them are admissible movements)
        let counterreplies = determination.tree.lookahead()
            .iter().map(|c| c.patch).collect::<Vec<_>>();
        if counterreplies.is_empty() {
            if determination.tree.in_critical_endangerment(Team::Orange) {
                return json::encode(
                    &LastMissive { the_triumphant: Some(Team::Blue) }).unwrap()
            } else {
                return json::encode(
                    &LastMissive { the_triumphant: None }).unwrap()
            }
        }
        let postcard = Postcard {
            world: determination.tree.preserve(),
            patch: determination.patch,
            hospitalization: determination.hospitalization,
            thinking_time: sidereal.num_milliseconds() as u64,
            depth: depth,
            counterreplies: counterreplies,
        };
        json::encode(&postcard).unwrap()
    } else {
        if in_medias_res.in_critical_endangerment(Team::Blue) {
            json::encode(&LastMissive { the_triumphant: Some(Team::Orange) }).unwrap()
        } else {
            json::encode(&LastMissive { the_triumphant: None }).unwrap()
        }
    }
}

fn the_end() {
    println!("THE END");
    process::exit(0);
}

fn main() {
    // Does argparse not offer an analogue of Python's argparse's
    // `add_mutually_exclusive_group`
    // (https://docs.python.org/3/library/argparse.html#mutual-exclusion)?
    // Contribution opportunity if so??
    let mut lookahead_depth: Option<u8> = None;
    // TODO CONSIDER: would argparse's Collect action be cleaner?
    let mut lookahead_depth_sequence: Option<String> = None;
    let mut lookahead_seconds: Option<u8> = None;
    let mut from_runes: Option<String> = None;
    let mut correspond: bool = false;
    let mut déjà_vu_bound: f32 = 2.0;
    let mut debug_logging: bool = false;
    {
        let mut parser = ArgumentParser::new();
        parser.set_description("Leafline: an oppositional strategy game engine");
        parser.refer(&mut lookahead_depth).add_option(
            &["--depth"],
            StoreOption,
            "rank moves using AI minimax lookahead this deep");
        parser.refer(&mut lookahead_depth_sequence).add_option(
            &["--depth-sequence"],
            StoreOption,
            "rank moves using AI minimax lookahead to these depths");
        parser.refer(&mut lookahead_seconds).add_option(
            &["--seconds"],
            StoreOption,
            "rank moves using AI minimax for about this many seconds");
        parser.refer(&mut correspond).add_option(
            &["--correspond"],
            StoreTrue,
            "just output the serialization of the AI's top response and \
             legal replies thereto");
        parser.refer(&mut from_runes).add_option(
            &["--from"],
            StoreOption,
            "start a game from the given book of preservation runes");
        parser.refer(&mut déjà_vu_bound).add_option(
            &["--déjà-vu-bound", "--deja-vu-bound"],
            Store,
            "try to not store more entries in the déjà vu table than fit in \
             this many GiB of memory"
        );
        parser.refer(&mut debug_logging).add_option(
            &["--debug"],
            StoreTrue,
            "run with debug logging to file"
        );
        parser.add_option(&["--version", "-v"],
            Print(env!("CARGO_PKG_VERSION").to_owned()), "diplay the version");
        parser.parse_args_or_exit();
    }

    if debug_logging {
        DebugLogger::init().expect("couldn't initialize logging?!")
    }

    if correspond {
        let bound_maybe_result = LookaheadBound::from_args(
            lookahead_depth, lookahead_depth_sequence, lookahead_seconds
        );
        let bound = match bound_maybe_result {
            Ok(bound_maybe) => match bound_maybe {
                Some(bound) => bound,
                None => {
                    moral_panic!(
                        "`--correspond` passed without exactly \
                         one of `--depth`, `--depth-sequence`, or `--seconds`");
                },
            },
            Err(error) => {
                moral_panic!(error);
            }
        };
        let from = from_runes.expect("`--correspond` requires `--from`");
        println!("{}", correspondence(from, bound, déjà_vu_bound));
        process::exit(0);
    }

    println!("Welcome to Leafline v. {}!", env!("CARGO_PKG_VERSION"));
    println!("Leafline substrate accountant detected {:.3} GiB of free memory.",
             memory_free().in_gib());

    let mut world = match from_runes {
        Some(runes) => WorldState::reconstruct(runes),
        None => WorldState::new()
    };
    let mut premonitions: Vec<Commit>;
    let bound_maybe = LookaheadBound::from_args(
        lookahead_depth, lookahead_depth_sequence, lookahead_seconds
    ).unwrap();
    loop {
        match bound_maybe {
            None => {
                premonitions = world.lookahead();
                if premonitions.is_empty() {
                    // XXX TODO distinguish between deadlock and
                    // ultimate endangerment
                    the_end();
                }
                println!("{}", world);
                for (index, premonition) in premonitions.iter().enumerate() {
                    println!("{:>2}. {}", index, premonition)
                }
            },
            Some(ref bound) => {
                let (our_forecasts, depth, thinking_time) = forecast(
                    world, bound.clone(), déjà_vu_bound);
                let forecasts = our_forecasts;
                println!("{}", world);
                println!(
                    "(scoring alternatives {} levels deep took {} ms)",
                    depth, thinking_time.num_milliseconds()
                );
                premonitions = Vec::new();
                for (index, sight) in forecasts.into_iter().enumerate() {
                    let (commit, score, variation) = sight;
                    println!("{:>2}. {} — score {:.1}",
                             index, commit, score);
                    println!(
                        "      ‣ principal variation: {}",
                        variation.iter()
                            .map(|p| p.abbreviated_pagan_movement_rune())
                            .collect::<Vec<_>>().join(" ")
                    );
                    premonitions.push(commit);
                }

                if premonitions.is_empty() {
                    the_end();
                }
            }
        }

        loop {
            print!("\nSelect a move>> ");
            io::stdout().flush().expect("couldn't flush stdout");
            let mut input_buffer = String::new();
            io::stdin()
                .read_line(&mut input_buffer)
                .expect("couldn't read input");

            if input_buffer.trim() == "quit" {
                the_end();
            }

            let choice: usize = match input_buffer.trim().parse() {
                Ok(i) => i,
                Err(e) => {
                    println!("Error parsing choice: {:?}. Try again.", e);
                    continue;
                }
            };
            if choice < premonitions.len() {
                world = premonitions[choice].tree;
                break;
            } else {
                println!("{} isn't among the choices. Try again.",
                         choice);
            }
        }
    }
}

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

    #[test]
    fn concerning_correspondence_victory_conditions() {
        let blue_concession = correspondence(
            "R6k/6pp/8/8/8/8/8/8 b -".to_owned(),
            LookaheadBound::Depth(2), 1.0
        );
        assert_eq!("{\"the_triumphant\":\"Orange\"}".to_owned(),
                   blue_concession);
    }

}
<commit_msg>score display could be more prominent, fuchsian<commit_after>#![feature(augmented_assignments)]
#![feature(non_ascii_idents)]
#![feature(pattern)]
#![feature(plugin)]
#![feature(test)]
#![plugin(clippy)]

#![allow(unused_features)]
#![warn(missing_debug_implementations, missing_copy_implementations,
        trivial_casts, trivial_numeric_casts,
        unused_import_braces, unused_qualifications)]

extern crate argparse;
extern crate ansi_term;
#[macro_use] extern crate itertools;
#[macro_use] extern crate log;
extern crate lru_cache;
extern crate rustc_serialize;
extern crate time;

#[macro_use] mod macros;
mod space;
mod identity;
mod motion;
mod landmark;
mod life;
mod mind;
mod substrate;

use std::fs::OpenOptions;
use std::error::Error;
use std::io;
use std::io::Write;
use std::process;

use ansi_term::Colour as Color;
use argparse::{ArgumentParser, Store, StoreOption, StoreTrue, Print};
use log::{LogRecord, LogMetadata, LogLevelFilter, SetLoggerError};
use rustc_serialize::json;
use time::{Duration, get_time};

use identity::{Agent, Team};
use life::{WorldState, Commit, Patch};
use mind::{kickoff, iterative_deepening_kickoff, fixed_depth_sequence_kickoff,
           Variation};
use substrate::memory_free;

struct DebugLogger;

impl DebugLogger {
    pub fn init() -> Result<(), SetLoggerError> {
        log::set_logger(|max_log_level| {
            max_log_level.set(LogLevelFilter::Debug);
            Box::new(DebugLogger)
        })
    }
}

impl log::Log for DebugLogger {
    fn enabled(&self, _metadata: &LogMetadata) -> bool { true }

    fn log(&self, record: &LogRecord) {
        let mut log_file = OpenOptions::new()
            .write(true).append(true).create(true)
            .open("leafline.log").expect("couldn't open log file?!");
        let log_message = format!(
            "[{}] {}\n",
            time::now().strftime("%Y-%m-%d %H:%M:%S.%f").unwrap(),
            record.args()
        );
        log_file.write(&log_message.into_bytes())
            .expect("couldn't write to log file?!");
    }
}

#[derive(Debug, Clone)]
enum LookaheadBound {
    Depth(u8),
    DepthSequence(Vec<u8>),
    Seconds(u8),
}

impl LookaheadBound {
    pub fn duration(&self) -> Duration {
        match *self {
            LookaheadBound::Seconds(secs) => Duration::seconds(secs as i64),
            _ => moral_panic!("`duration()` called on non-Seconds \
                               LookaheadBound variant")
        }
    }

    pub fn new_from_sequence_depiction(depiction: String) -> Self{
        let depth_runes = depiction.split(',');
        let depth_sequence = depth_runes
            .map(|dd| dd.parse::<u8>().expect("couldn't parse depth sequence"))
            .collect::<Vec<_>>();
        LookaheadBound::DepthSequence(depth_sequence)
    }

    pub fn from_args(lookahead_depth: Option<u8>,
                     lookahead_depth_sequence: Option<String>,
                     lookahead_seconds: Option<u8>)
                     -> Result<Option<Self>, String> {
        let mut bound = None;
        let confirm_bound_is_none = |b: &Option<LookaheadBound>|
                                                -> Result<bool, String> {
            if b.is_some() {
                Err("more than one of `--depth`, `--depth-sequence`, \
                     or `--seconds` was passed".to_owned())
            } else {
                Ok(true)
            }
        };
        if let Some(depth) = lookahead_depth {
            try!(confirm_bound_is_none(&bound));
            bound = Some(LookaheadBound::Depth(depth));
        }
        if let Some(sequence_depiction) = lookahead_depth_sequence {
            try!(confirm_bound_is_none(&bound));
            bound = Some(LookaheadBound::new_from_sequence_depiction(
                sequence_depiction));
        }
        if let Some(seconds) = lookahead_seconds {
            try!(confirm_bound_is_none(&bound));
            bound = Some(LookaheadBound::Seconds(seconds));
        }
        Ok(bound)
    }
}

fn forecast(world: WorldState, bound: LookaheadBound, déjà_vu_bound: f32)
            -> (Vec<(Commit, f32, Variation)>, u8, Duration) {
    let start_thinking = get_time();
    let forecasts;
    let depth;
    match bound {
        LookaheadBound::Depth(ds) => {
            forecasts = kickoff(&world, ds, false, déjà_vu_bound);
            depth = ds;
        },
        LookaheadBound::DepthSequence(ds) => {
            depth = *ds.last().unwrap();
            forecasts = fixed_depth_sequence_kickoff(
                &world, ds, false, déjà_vu_bound);
            // XXX TODO: if we're just returning a number, it should be the
            // lowest depth, but we should really report all of them
        },
        LookaheadBound::Seconds(_) => {
            let (fs, ds) = iterative_deepening_kickoff(
                &world, bound.duration(), false, déjà_vu_bound);
            forecasts = fs;
            depth = ds;
        }
    }
    let stop_thinking = get_time();
    let thinking_time = stop_thinking - start_thinking;
    (forecasts, depth, thinking_time)
}

#[derive(RustcEncodable, RustcDecodable)]
struct Postcard {
    world: String,
    patch: Patch,
    hospitalization: Option<Agent>,
    thinking_time: u64,
    depth: u8,
    counterreplies: Vec<Patch>,
}

#[derive(RustcEncodable, RustcDecodable)]
struct LastMissive {
    the_triumphant: Option<Team>
}

fn correspondence(reminder: String, bound: LookaheadBound, déjà_vu_bound: f32) -> String {
    let in_medias_res = WorldState::reconstruct(reminder);
    let (mut forecasts, depth, sidereal) = forecast(in_medias_res, bound,
                                                    déjà_vu_bound);

    if !forecasts.is_empty() {
        let (determination, _karma, _variation) = forecasts.swap_remove(0);
        // XXX TODO FIXME: this doesn't distinguish amongst ascensions
        // (and we can imagine somewhat contrived situations where only
        // some of them are admissible movements)
        let counterreplies = determination.tree.lookahead()
            .iter().map(|c| c.patch).collect::<Vec<_>>();
        if counterreplies.is_empty() {
            if determination.tree.in_critical_endangerment(Team::Orange) {
                return json::encode(
                    &LastMissive { the_triumphant: Some(Team::Blue) }).unwrap()
            } else {
                return json::encode(
                    &LastMissive { the_triumphant: None }).unwrap()
            }
        }
        let postcard = Postcard {
            world: determination.tree.preserve(),
            patch: determination.patch,
            hospitalization: determination.hospitalization,
            thinking_time: sidereal.num_milliseconds() as u64,
            depth: depth,
            counterreplies: counterreplies,
        };
        json::encode(&postcard).unwrap()
    } else {
        if in_medias_res.in_critical_endangerment(Team::Blue) {
            json::encode(&LastMissive { the_triumphant: Some(Team::Orange) }).unwrap()
        } else {
            json::encode(&LastMissive { the_triumphant: None }).unwrap()
        }
    }
}

fn the_end() {
    println!("THE END");
    process::exit(0);
}

fn main() {
    // Does argparse not offer an analogue of Python's argparse's
    // `add_mutually_exclusive_group`
    // (https://docs.python.org/3/library/argparse.html#mutual-exclusion)?
    // Contribution opportunity if so??
    let mut lookahead_depth: Option<u8> = None;
    // TODO CONSIDER: would argparse's Collect action be cleaner?
    let mut lookahead_depth_sequence: Option<String> = None;
    let mut lookahead_seconds: Option<u8> = None;
    let mut from_runes: Option<String> = None;
    let mut correspond: bool = false;
    let mut déjà_vu_bound: f32 = 2.0;
    let mut debug_logging: bool = false;
    {
        let mut parser = ArgumentParser::new();
        parser.set_description("Leafline: an oppositional strategy game engine");
        parser.refer(&mut lookahead_depth).add_option(
            &["--depth"],
            StoreOption,
            "rank moves using AI minimax lookahead this deep");
        parser.refer(&mut lookahead_depth_sequence).add_option(
            &["--depth-sequence"],
            StoreOption,
            "rank moves using AI minimax lookahead to these depths");
        parser.refer(&mut lookahead_seconds).add_option(
            &["--seconds"],
            StoreOption,
            "rank moves using AI minimax for about this many seconds");
        parser.refer(&mut correspond).add_option(
            &["--correspond"],
            StoreTrue,
            "just output the serialization of the AI's top response and \
             legal replies thereto");
        parser.refer(&mut from_runes).add_option(
            &["--from"],
            StoreOption,
            "start a game from the given book of preservation runes");
        parser.refer(&mut déjà_vu_bound).add_option(
            &["--déjà-vu-bound", "--deja-vu-bound"],
            Store,
            "try to not store more entries in the déjà vu table than fit in \
             this many GiB of memory"
        );
        parser.refer(&mut debug_logging).add_option(
            &["--debug"],
            StoreTrue,
            "run with debug logging to file"
        );
        parser.add_option(&["--version", "-v"],
            Print(env!("CARGO_PKG_VERSION").to_owned()), "diplay the version");
        parser.parse_args_or_exit();
    }

    if debug_logging {
        DebugLogger::init().expect("couldn't initialize logging?!")
    }

    if correspond {
        let bound_maybe_result = LookaheadBound::from_args(
            lookahead_depth, lookahead_depth_sequence, lookahead_seconds
        );
        let bound = match bound_maybe_result {
            Ok(bound_maybe) => match bound_maybe {
                Some(bound) => bound,
                None => {
                    moral_panic!(
                        "`--correspond` passed without exactly \
                         one of `--depth`, `--depth-sequence`, or `--seconds`");
                },
            },
            Err(error) => {
                moral_panic!(error);
            }
        };
        let from = from_runes.expect("`--correspond` requires `--from`");
        println!("{}", correspondence(from, bound, déjà_vu_bound));
        process::exit(0);
    }

    println!("Welcome to Leafline v. {}!", env!("CARGO_PKG_VERSION"));
    println!("Leafline substrate accountant detected {:.3} GiB of free memory.",
             memory_free().in_gib());

    let mut world = match from_runes {
        Some(runes) => WorldState::reconstruct(runes),
        None => WorldState::new()
    };
    let mut premonitions: Vec<Commit>;
    let bound_maybe = LookaheadBound::from_args(
        lookahead_depth, lookahead_depth_sequence, lookahead_seconds
    ).unwrap();
    loop {
        match bound_maybe {
            None => {
                premonitions = world.lookahead();
                if premonitions.is_empty() {
                    // XXX TODO distinguish between deadlock and
                    // ultimate endangerment
                    the_end();
                }
                println!("{}", world);
                for (index, premonition) in premonitions.iter().enumerate() {
                    println!("{:>2}. {}", index, premonition)
                }
            },
            Some(ref bound) => {
                let (our_forecasts, depth, thinking_time) = forecast(
                    world, bound.clone(), déjà_vu_bound);
                let forecasts = our_forecasts;
                println!("{}", world);
                println!(
                    "(scoring alternatives {} levels deep took {} ms)",
                    depth, thinking_time.num_milliseconds()
                );
                premonitions = Vec::new();
                for (index, sight) in forecasts.into_iter().enumerate() {
                    let (commit, score, variation) = sight;
                    println!("{:>2}. {} — score {}",
                             index, commit,
                             Color::Purple.bold()
                                 .paint(&format!("{:.1}", score)));
                    println!(
                        "      ‣ principal variation: {}",
                        variation.iter()
                            .map(|p| p.abbreviated_pagan_movement_rune())
                            .collect::<Vec<_>>().join(" ")
                    );
                    premonitions.push(commit);
                }

                if premonitions.is_empty() {
                    the_end();
                }
            }
        }

        loop {
            print!("\nSelect a move>> ");
            io::stdout().flush().expect("couldn't flush stdout");
            let mut input_buffer = String::new();
            io::stdin()
                .read_line(&mut input_buffer)
                .expect("couldn't read input");

            if input_buffer.trim() == "quit" {
                the_end();
            }

            let choice: usize = match input_buffer.trim().parse() {
                Ok(i) => i,
                Err(e) => {
                    println!("Error parsing choice: {:?}. Try again.", e);
                    continue;
                }
            };
            if choice < premonitions.len() {
                world = premonitions[choice].tree;
                break;
            } else {
                println!("{} isn't among the choices. Try again.",
                         choice);
            }
        }
    }
}

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

    #[test]
    fn concerning_correspondence_victory_conditions() {
        let blue_concession = correspondence(
            "R6k/6pp/8/8/8/8/8/8 b -".to_owned(),
            LookaheadBound::Depth(2), 1.0
        );
        assert_eq!("{\"the_triumphant\":\"Orange\"}".to_owned(),
                   blue_concession);
    }

}
<|endoftext|>