<commit_before>#![allow(dead_code)]
extern crate byteorder;

use std::env;
use std::io::Read;
use std::io::Result;
use std::fs::File;
use byteorder::{ByteOrder, LittleEndian};

// Bytecode instructions
const OP_LOAD: u8 = 1; // arity: 2 - (value: u64, dest: u8)
const OP_ADD: u8 = 2; // arity: 3 - (a: u8, b: u8, dest: u8)
const OP_LT: u8 = 3; // arity: 2 - (a: u8, b: u8, dest: u8)
const OP_RETURN: u8 = 4; // arity: 1 - (reg: u8)

fn interpret(code: &[u8]) -> u64 {
    let mut pc = 0; // "Program counter", the index of the current instruction.
    let mut regs = [0u64; 256]; // Array of 256 zeros.

    loop {
        let op = code[pc];
        pc += 1;
        match op {
            OP_LOAD => {
                // Read 64 bits from the code (BUG: right now I'm only reading
                // one byte of the number!
                let val = code[pc] as u64;
                pc += 8;
                let dest = code[pc] as usize;
                pc += 1;
                regs[dest] = val;
            },
            OP_ADD => {
                let a = code[pc] as usize;
                pc += 1;
                let b = code[pc] as usize;
                pc += 1;
                let dest = code[pc] as usize;
                pc += 1;
                regs[dest] = regs[a] + regs[b];
            },
            OP_LT => {
                let a = code[pc] as usize;
                pc += 1;
                let b = code[pc] as usize;
                pc += 1;
                let dest = code[pc] as usize;
                pc += 1;
                regs[dest] = (a < b) as u64;
            },
            OP_RETURN => {
                let r = code[pc] as usize;
                return regs[r];
            },
            _ => panic!("Invalid opcode at offset {}", pc)
        }
    }
}

fn read_file(filename: &str) -> Result<Vec<u8>> {
    let mut f = try!(File::open(filename));
    let mut result = vec![];
    try!(f.read_to_end(&mut result));
    Ok(result)
}

fn main() {
    let args: Vec<String> = env::args().collect();

    println!("{:?} args: {:?}", args.len() - 1, &args[1..]);

    let code = [
        OP_LOAD, 73, 0, 0, 0, 0, 0, 0, 0, 0, // load the number 73 into R0.
        OP_LOAD, 68, 0, 0, 0, 0, 0, 0, 0, 1, // load the number 68 into R1.
        OP_ADD, 0, 1, 2, // compute R0 + R1 and store it in R2.
        OP_RETURN, 2
    ];

    // should print 73 + 68, which is 141.
    println!("{}", interpret(&code))
}

#[test]
fn test_op_add() {
    let code = [
        OP_LOAD, 73, 0, 0, 0, 0, 0, 0, 0, 0,
        OP_LOAD, 68, 0, 0, 0, 0, 0, 0, 0, 1,
        OP_ADD, 0, 1, 2,
        OP_RETURN, 2
    ];

    assert_eq!(141, interpret(&code))
}

#[test]
fn test_op_lt() {
    let code = [
        OP_LOAD, 73, 0, 0, 0, 0, 0, 0, 0, 0,
        OP_LOAD, 68, 0, 0, 0, 0, 0, 0, 0, 1,
        OP_LT, 0, 1, 2,
        OP_RETURN, 2
    ];

    assert_eq!(1, interpret(&code))
}
<commit_msg>Fix OP_LT and rename OP_LOAD to OP_LOAD_U8<commit_after>#![allow(dead_code)]
extern crate byteorder;

use std::env;
use std::io::Read;
use std::io::Result;
use std::fs::File;
use byteorder::{ByteOrder, LittleEndian};

// Bytecode instructions
const OP_LOAD_U8: u8 = 1; // arity: 2 - (value: u64, dest: u8)
const OP_ADD: u8 = 2; // arity: 3 - (a: u8, b: u8, dest: u8)
const OP_LT: u8 = 3; // arity: 2 - (a: u8, b: u8, dest: u8)
const OP_RETURN: u8 = 4; // arity: 1 - (reg: u8)

fn interpret(code: &[u8]) -> u64 {
    let mut pc = 0; // "Program counter", the index of the current instruction.
    let mut regs = [0u64; 256]; // Array of 256 zeros.

    loop {
        let op = code[pc];
        pc += 1;
        match op {
            OP_LOAD_U8 => {
                let val = code[pc] as u64;
                pc += 1;
                let dest = code[pc] as usize;
                pc += 1;
                regs[dest] = val;
            },
            OP_ADD => {
                let a = code[pc] as usize;
                pc += 1;
                let b = code[pc] as usize;
                pc += 1;
                let dest = code[pc] as usize;
                pc += 1;
                regs[dest] = regs[a] + regs[b];
            },
            OP_LT => {
                let a = code[pc] as usize;
                pc += 1;
                let b = code[pc] as usize;
                pc += 1;
                let dest = code[pc] as usize;
                pc += 1;
                regs[dest] = (regs[a] < regs[b]) as u64;
            },
            OP_RETURN => {
                let r = code[pc] as usize;
                return regs[r];
            },
            _ => panic!("Invalid opcode at offset {}", pc)
        }
    }
}

fn read_file(filename: &str) -> Result<Vec<u8>> {
    let mut f = try!(File::open(filename));
    let mut result = vec![];
    try!(f.read_to_end(&mut result));
    Ok(result)
}

fn main() {
    let args: Vec<String> = env::args().collect();

    println!("{:?} args: {:?}", args.len() - 1, &args[1..]);

    let code = [
        OP_LOAD_U8, 73, 0, // load the number 73 into R0.
        OP_LOAD_U8, 68, 1, // load the number 68 into R1.
        OP_ADD, 0, 1, 2, // compute R0 + R1 and store it in R2.
        OP_RETURN, 2
    ];

    // should print 73 + 68, which is 141.
    println!("{}", interpret(&code))
}

#[test]
fn test_op_add() {
    let code = [
        OP_LOAD_U8, 73, 0,
        OP_LOAD_U8, 68, 1,
        OP_ADD, 0, 1, 2,
        OP_RETURN, 2
    ];

    assert_eq!(141, interpret(&code));
}

#[test]
fn test_op_lt() {
    let code1 = [
        OP_LOAD_U8, 73, 0,
        OP_LOAD_U8, 68, 1,
        OP_LT, 0, 1, 2,
        OP_RETURN, 2
    ];

    assert_eq!(0, interpret(&code1));

    let code2 = [
        OP_LOAD_U8, 68, 0,
        OP_LOAD_U8, 73, 1,
        OP_LT, 0, 1, 2,
        OP_RETURN, 2
    ];

    assert_eq!(1, interpret(&code2));
}
<|endoftext|>


<commit_before>//#![feature(libc)]

extern crate libc;
extern crate rand;
extern crate rustc_serialize;

use rand::*;

mod yubikey;

fn random_challenge() -> Vec<u8> {
    let mut vec: Vec<u8> = Vec::new();
    for _ in 0..64 {
        vec.push(rand::random());
    }
    vec
}

fn main() {
    yubikey::yubikey_init();
    let yk = yubikey::get_yubikey();

    let challenge = &random_challenge();

    match yubikey::challenge_response(yk, 2, challenge, false) {
        Err(_)       => println!("error occured"),
        Ok(result) => println!("{}", result)
    }
}
<commit_msg>Allow unused functions & dead_code<commit_after>#![allow(unused_variables)]
#![allow(dead_code)]

extern crate libc;
extern crate rand;
extern crate rustc_serialize;

use rand::*;

mod yubikey;

fn random_challenge() -> Vec<u8> {
    let mut vec: Vec<u8> = Vec::new();
    for _ in 0..64 {
        vec.push(rand::random());
    }
    vec
}

fn main() {
    yubikey::yubikey_init();
    let yk = yubikey::get_yubikey();

    let challenge = &random_challenge();

    match yubikey::challenge_response(yk, 2, challenge, false) {
        Err(_)     => println!("error occured"),
        Ok(result) => println!("{}", result)
    }
}
<|endoftext|>


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

#[macro_use] extern crate diesel;
#[macro_use] extern crate diesel_codegen;
#[macro_use] extern crate serde_derive;

extern crate dotenv;
extern crate rocket;
extern crate rocket_contrib;
extern crate serde_json;
extern crate serde_yaml;

use diesel::prelude::*;
use diesel::mysql::MysqlConnection;
use rocket_contrib::{JSON};
use rocket::response::{Response};
use rocket::State;
use std::sync::Mutex;
use std::io;

mod options;
mod req_headers;
pub mod hopplerdb;
pub mod models;
pub mod schema;

use options::{PostEventsOptions};
use req_headers::ReqHeaders;
use rocket::response::NamedFile;
use models::{DbEvent};

// Our database mutex so we can pass it to each route handler
type DbConn = Mutex<MysqlConnection>;

#[options("/events")]
fn options_events_handler<'a>(req_headers: ReqHeaders) -> Response<'a> {
    Response::build()
        .raw_header("access-control-allow-credentials", "true")
        .raw_header("Access-Control-Allow-Origin", req_headers.origin)
        .raw_header("Access-Control-Allow-Methods", "OPTIONS, POST")
        .raw_header("Access-Control-Allow-Headers", "Content-Type")
        .finalize()
}

#[options("/events?<req_options>")]
fn options_events_handler_with_options<'a>(req_headers: ReqHeaders, req_options: PostEventsOptions) -> Response<'a> {
    Response::build()
        .raw_header("access-control-allow-credentials", "true")
        .raw_header("Access-Control-Allow-Origin", req_headers.origin)
        .raw_header("Access-Control-Allow-Methods", "OPTIONS, POST")
        .raw_header("Access-Control-Allow-Headers", "Content-Type")
        .finalize()
}

#[get("/getjs")]
fn get_hopplerjs() -> io::Result<NamedFile> {
    NamedFile::open("node_modules/hopplerjs/dist/hopplerjs.js")
}

#[get("/events")]
fn get_events(db: State<DbConn>) -> String {
    use schema::events::dsl::*;

    let db = db.inner().lock().unwrap();
    let results = events
        .load::<DbEvent>(&*db)
        .expect("Error loading events");

    let mut output:String = String::from("{ series:");
    for event in results {
        let json = serde_json::to_string_pretty(&event).unwrap();
        output = output + "[" + &json + "],";
    }

    output = output + "}";

    format!("{}", output)
}

#[post("/events?<req_options>", data = "<events>")]
fn post_events<'a>(db: State<DbConn>, mut events: JSON<models::EventsList>, req_headers: ReqHeaders, req_options: PostEventsOptions) -> Response<'a> {
    use schema::events;

    let mut user_override:Option<&str> = None;

    if let Some(ref header_name) = req_options.uh {
        if let Some(ref header_value) = req_headers.headers.get(header_name) {
            user_override = Some(header_value);
            println!("Have user override {} {}", header_name, header_value);
        }
    }

    let db = db.inner().lock().unwrap();
    for event in events.events.iter_mut() {
        if let Some(username) = user_override {
            event.username = username.into();
        }
        diesel::insert(event).into(events::table)
            .execute(&*db)
            .expect("Error saving new event");
    }

    println!("Wrote {} events to the DB", events.events.len());

    Response::build()
        .raw_header("access-control-allow-credentials", "true")
        .raw_header("Access-Control-Allow-Origin", req_headers.origin)
        .raw_header("Access-Control-Allow-Methods", "OPTIONS, POST")
        .raw_header("Access-Control-Allow-Headers", "Content-Type")
        .finalize()
}

fn main() {
    let dbconnection = hopplerdb::establish_connection();
    rocket::ignite().manage(Mutex::new(dbconnection)).mount("/", routes![get_hopplerjs, options_events_handler, options_events_handler_with_options, get_events, post_events]).launch();
}<commit_msg>Run migrations on startup<commit_after>#![feature(plugin, custom_derive)]
#![plugin(rocket_codegen)]

#[macro_use] extern crate diesel;
#[macro_use] extern crate diesel_codegen;
#[macro_use] extern crate serde_derive;

extern crate dotenv;
extern crate rocket;
extern crate rocket_contrib;
extern crate serde_json;
extern crate serde_yaml;

use diesel::prelude::*;
use diesel::mysql::MysqlConnection;
use rocket_contrib::{JSON};
use rocket::response::{Response};
use rocket::State;
use std::sync::Mutex;
use std::io;

mod options;
mod req_headers;
pub mod hopplerdb;
pub mod models;
pub mod schema;

use options::{PostEventsOptions};
use req_headers::ReqHeaders;
use rocket::response::NamedFile;
use models::{DbEvent};

// Our database mutex so we can pass it to each route handler
type DbConn = Mutex<MysqlConnection>;

#[options("/events")]
fn options_events_handler<'a>(req_headers: ReqHeaders) -> Response<'a> {
    Response::build()
        .raw_header("access-control-allow-credentials", "true")
        .raw_header("Access-Control-Allow-Origin", req_headers.origin)
        .raw_header("Access-Control-Allow-Methods", "OPTIONS, POST")
        .raw_header("Access-Control-Allow-Headers", "Content-Type")
        .finalize()
}

#[options("/events?<req_options>")]
fn options_events_handler_with_options<'a>(req_headers: ReqHeaders, req_options: PostEventsOptions) -> Response<'a> {
    Response::build()
        .raw_header("access-control-allow-credentials", "true")
        .raw_header("Access-Control-Allow-Origin", req_headers.origin)
        .raw_header("Access-Control-Allow-Methods", "OPTIONS, POST")
        .raw_header("Access-Control-Allow-Headers", "Content-Type")
        .finalize()
}

#[get("/getjs")]
fn get_hopplerjs() -> io::Result<NamedFile> {
    NamedFile::open("node_modules/hopplerjs/dist/hopplerjs.js")
}

#[get("/events")]
fn get_events(db: State<DbConn>) -> String {
    use schema::events::dsl::*;

    let db = db.inner().lock().unwrap();
    let results = events
        .load::<DbEvent>(&*db)
        .expect("Error loading events");

    let mut output:String = String::from("{ series:");
    for event in results {
        let json = serde_json::to_string_pretty(&event).unwrap();
        output = output + "[" + &json + "],";
    }

    output = output + "}";

    format!("{}", output)
}

#[post("/events?<req_options>", data = "<events>")]
fn post_events<'a>(db: State<DbConn>, mut events: JSON<models::EventsList>, req_headers: ReqHeaders, req_options: PostEventsOptions) -> Response<'a> {
    use schema::events;

    let mut user_override:Option<&str> = None;

    if let Some(ref header_name) = req_options.uh {
        if let Some(ref header_value) = req_headers.headers.get(header_name) {
            user_override = Some(header_value);
            println!("Have user override {} {}", header_name, header_value);
        }
    }

    let db = db.inner().lock().unwrap();
    for event in events.events.iter_mut() {
        if let Some(username) = user_override {
            event.username = username.into();
        }
        diesel::insert(event).into(events::table)
            .execute(&*db)
            .expect("Error saving new event");
    }

    println!("Wrote {} events to the DB", events.events.len());

    Response::build()
        .raw_header("access-control-allow-credentials", "true")
        .raw_header("Access-Control-Allow-Origin", req_headers.origin)
        .raw_header("Access-Control-Allow-Methods", "OPTIONS, POST")
        .raw_header("Access-Control-Allow-Headers", "Content-Type")
        .finalize()
}

fn main() {
    let dbconnection = hopplerdb::establish_connection();
    diesel::migrations::run_pending_migrations(&dbconnection).expect("Failed to run DB migrations.");
    rocket::ignite().manage(Mutex::new(dbconnection)).mount("/", routes![get_hopplerjs, options_events_handler, options_events_handler_with_options, get_events, post_events]).launch();
}<|endoftext|>


<commit_before>//
// Copyright (c) 2015 Rodolphe Breard
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
//

use libreauth::oath::TOTPBuilder;
use std::io;

fn get_secret_key() -> Option<String> {
    let mut key = String::new();
    let read_len = match io::stdin().read_line(&mut key) {
        Ok(l) => l,
        Err(_) => {
            return None;
        }
    };
    if read_len != 0 {
        Some(key.trim().to_string())
    } else {
        None
    }
}

fn main() {
    loop {
        let key = match get_secret_key() {
            Some(k) => k,
            None => {
                return;
            }
        };
        let code = TOTPBuilder::new()
            .base32_key(&key)
            .finalize()
            .unwrap()
            .generate();
        println!("{}", code);
    }
}
<commit_msg>Simplify the code<commit_after>//
// Copyright (c) 2015 Rodolphe Breard
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
//

use libreauth::oath::TOTPBuilder;
use std::io;

fn get_secret_key() -> Option<String> {
    let mut key = String::new();
    if io::stdin().read_line(&mut key).ok()? != 0 {
        Some(key.trim().to_string())
    } else {
        None
    }
}

fn main() {
    loop {
        let key = match get_secret_key() {
            Some(k) => k,
            None => {
                return;
            }
        };
        let code = TOTPBuilder::new()
            .base32_key(&key)
            .finalize()
            .unwrap()
            .generate();
        println!("{}", code);
    }
}
<|endoftext|>


<commit_before>extern crate docopt;
extern crate rustc_serialize;

use docopt::Docopt;

const USAGE: &'static str = "
how-do-i-escape: Prints escape sequences for unicode graphemes

Usage:
  how-do-i-escape <grapheme>
  how-do-i-escape (--help | --version)

Options:
  -h, --help  Show this screen.
  --version   Show version.

Example:
  $ how-do-i-escape \u{00A7}
  css  = \"\\00A7\"
  html = §
  js   = \"\\u00A7\"
";

#[derive(RustcDecodable)]
struct Args {
    arg_grapheme: String
}

fn main() {
  let args: Args = Docopt::new(USAGE)
    .and_then(|d| d.help(true).version(Some("v0.1.0".to_string())).decode())
    .unwrap_or_else(|e| e.exit());

  let grapheme = args.arg_grapheme;

  println!(r#"css  = "{}""#, escape_grapheme(&grapheme, as_css));
  println!(r#"html = {}"#, escape_grapheme(&grapheme, as_html));
  println!(r#"js   = "{}""#, escape_grapheme(&grapheme, as_js));
}

fn escape_grapheme<F>(grapheme: &str, int_to_escape_sequence: F) -> String
  where F: Fn(u32) -> String
{
  grapheme
    .chars()
    .map(|ch| ch as u32)
    .map(int_to_escape_sequence)
    .fold(String::new(), |c, s| {
      let mut copy = c;
      copy.push_str(&s);
      copy
    })
}

fn as_css(i: u32) -> String {
  format!("\\{:01$X}", i, 4)
}

fn as_html(i: u32) -> String {
  format!("&#x{:01$X};", i, 4)
}

fn as_js(i: u32) -> String {
  if i <= 0xFFFF {
    format!("\\u{:01$X}", i, 4)
  } else {
    format!("\\u{{{:X}}}", i)
  }
}

#[cfg(test)]
mod tests {
  use super::{as_css, as_html, as_js};

  #[test]
  fn test_as_css() {
    assert!(as_css(0) == r"\0000");
    assert!(as_css(0xFFFF) == r"\FFFF");
    assert!(as_css(0xBEEF) != r"\beef");
    //assert!(as_css(0x10000) == ?);
  }

  #[test]
  fn test_as_html() {
    assert!(as_html(0) == r"�");
    assert!(as_html(0xFFFF) == r"");
    assert!(as_html(0xBEEF) != r"뻯");
    //assert!(as_html(0x10000) == ?);
  }

  #[test]
  fn test_as_js() {
    assert!(as_js(0) == r"\u0000");
    assert!(as_js(0xFFFF) == r"\uFFFF");
    assert!(as_js(0xBEEF) != r"\ubeef");
    assert!(as_js(0x10000) == r"\u{10000}");
    assert!(as_js(0x10FFFF) == r"\u{10FFFF}");
    assert!(as_js(0xFFFFFFFF) == r"\u{FFFFFFFF}");
  }
}
<commit_msg>Version comes from manifest rather than baked into code<commit_after>extern crate docopt;
extern crate rustc_serialize;

use docopt::Docopt;

const USAGE: &'static str = "
how-do-i-escape: Prints escape sequences for unicode graphemes

Usage:
  how-do-i-escape <grapheme>
  how-do-i-escape (--help | --version)

Options:
  -h, --help  Show this screen.
  --version   Show version.

Example:
  $ how-do-i-escape \u{00A7}
  css  = \"\\00A7\"
  html = §
  js   = \"\\u00A7\"
";

#[derive(RustcDecodable)]
struct Args {
    arg_grapheme: String
}

fn main() {
  let version = format!("v{}", env!("CARGO_PKG_VERSION"));

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

  let grapheme = args.arg_grapheme;

  println!(r#"css  = "{}""#, escape_grapheme(&grapheme, as_css));
  println!(r#"html = {}"#, escape_grapheme(&grapheme, as_html));
  println!(r#"js   = "{}""#, escape_grapheme(&grapheme, as_js));
}

fn escape_grapheme<F>(grapheme: &str, int_to_escape_sequence: F) -> String
  where F: Fn(u32) -> String
{
  grapheme
    .chars()
    .map(|ch| ch as u32)
    .map(int_to_escape_sequence)
    .fold(String::new(), |c, s| {
      let mut copy = c;
      copy.push_str(&s);
      copy
    })
}

fn as_css(i: u32) -> String {
  format!("\\{:01$X}", i, 4)
}

fn as_html(i: u32) -> String {
  format!("&#x{:01$X};", i, 4)
}

fn as_js(i: u32) -> String {
  if i <= 0xFFFF {
    format!("\\u{:01$X}", i, 4)
  } else {
    format!("\\u{{{:X}}}", i)
  }
}

#[cfg(test)]
mod tests {
  use super::{as_css, as_html, as_js};

  #[test]
  fn test_as_css() {
    assert!(as_css(0) == r"\0000");
    assert!(as_css(0xFFFF) == r"\FFFF");
    assert!(as_css(0xBEEF) != r"\beef");
    //assert!(as_css(0x10000) == ?);
  }

  #[test]
  fn test_as_html() {
    assert!(as_html(0) == r"�");
    assert!(as_html(0xFFFF) == r"");
    assert!(as_html(0xBEEF) != r"뻯");
    //assert!(as_html(0x10000) == ?);
  }

  #[test]
  fn test_as_js() {
    assert!(as_js(0) == r"\u0000");
    assert!(as_js(0xFFFF) == r"\uFFFF");
    assert!(as_js(0xBEEF) != r"\ubeef");
    assert!(as_js(0x10000) == r"\u{10000}");
    assert!(as_js(0x10FFFF) == r"\u{10FFFF}");
    assert!(as_js(0xFFFFFFFF) == r"\u{FFFFFFFF}");
  }
}
<|endoftext|>


<commit_before>/**
 * s3-file-upload
 */

extern crate rusoto;
extern crate crypto;

use std::env;
use std::fs;
use std::path::Path;
use std::fs::File;
use std::io::{stdin, Read, Write};
use std::clone::Clone;

use rusoto::{ProfileProvider, Region};
use rusoto::s3::{Object, S3Client, ListObjectsV2Request, PutObjectRequest};
use rusoto::default_tls_client;

use crypto::md5::Md5;
use crypto::digest::Digest;

/**
 * Struct for files
 */
#[derive(Clone)]
struct LocalFile
{
    path: std::path::PathBuf
}

/**
 * Get the mime type for a local file
 */
fn local_file_get_mime(file: &LocalFile) -> String
{
    let extension = file.path.extension().unwrap().to_str();

    let mime_type = match extension.as_ref()
    {
        Some(&"html") => "text/html",
        Some(&"css")  => "text/css",
        Some(&"png")  => "image/png",
        Some(&"gif")  => "image/gif",
        Some(&"jpg")  => "image/jpeg",
        Some(&"xml")  => "application/xml",
        _             => "application/octet-stream",
    };

    return mime_type.to_owned();
}

/**
 * Get the MD5 checksum of a local file
 */
fn local_file_get_md5(file: &LocalFile) -> String
{
    let mut file_handle = File::open(&file.path).unwrap();
    let mut contents: Vec<u8> = Vec::new();

    match file_handle.read_to_end(&mut contents)
    {
        Ok(_) =>
        {
            let mut md5_checksum = Md5::new();
            md5_checksum.input(&contents);
            return md5_checksum.result_str();
        }
        Err(error) =>
        {
            println!("Error: {}", error);
            return String::from("");
        }
    }
}

/**
 * Get a relative path for a local file
 */
fn local_file_get_relative_path(file: &LocalFile, local_path: &String) -> String
{
    let full_path: String = file.path.as_path().to_str().unwrap().to_owned();
    let last_path_section = full_path.split(local_path)
        .last()
        .unwrap()
        .replace("\\", "/");

    return last_path_section.to_owned();
}

/**
 * Upload a local file to an s3 bucket location
 */
fn local_file_upload_to_bucket(file: &LocalFile, local_path: &String, bucket_name: &String)
{
    let provider = ProfileProvider::with_configuration(Path::new("credentials"), "user");
    let client = S3Client::new(default_tls_client().unwrap(), provider, Region::EuWest1);

    let file = file.clone();

    print!("Uploading \"{}\" to \"{}/{}\"...", file.path.to_str().unwrap(), bucket_name, local_file_get_relative_path(&file, &local_path));
    Some(std::io::stdout().flush());

    let mut file_handle = File::open(&file.path).unwrap();
    let mut contents: Vec<u8> = Vec::new();

    match file_handle.read_to_end(&mut contents)
    {
        Ok(_) =>
        {
            let request = PutObjectRequest
            {
                bucket: bucket_name.to_owned(),
                key: local_file_get_relative_path(&file, &local_path),
                body: Some(contents),
                acl: Some(String::from("public-read")),
                content_type: Some(local_file_get_mime(&file)),
                ..PutObjectRequest::default()
            };

            match client.put_object(&request)
            {
                Ok(_) =>
                {
                    println!(" DONE");
                }
                Err(error) =>
                {
                   println!(" Error: {}", error);
                }
            }
        }
        Err(error) =>
        {
            println!("Error: {}", error);
        }
    }
}

/**
 * Get objects in an s3 bucket
 */
fn get_bucket_objects(bucket_name: &String, objects: &mut Vec<Object>)
{
    let provider = ProfileProvider::with_configuration(Path::new("credentials"), "user");
    let client = S3Client::new(default_tls_client().unwrap(), provider, Region::EuWest1);

    let request = ListObjectsV2Request
    {
        bucket: bucket_name.to_owned(),
        ..ListObjectsV2Request::default()
    };

    if let Ok(response) = client.list_objects_v2(&request)
    {
        for object in &response.contents.unwrap()
        {
            objects.push(object.clone());
        }
    }

}

/**
 * Get files from a local path
 */
fn get_local_files(local_path: &String, files: &mut Vec<LocalFile>, ignored_directories: &mut Vec<String>)
{
    if let Ok(entries) = fs::read_dir(Path::new(&local_path))
    {
        for entry in entries
        {
            let file = entry.unwrap();

            if let Ok(metadata) = file.metadata()
            {
                if metadata.is_dir()
                {
                    if !ignored_directories.contains(&file.file_name().to_str().unwrap().to_owned())
                    {
                        get_local_files(&file.path().to_str().unwrap().to_owned(), files, ignored_directories);
                    }
                }
                else
                {
                    files.push(LocalFile { path: file.path() });
                }
            }
        }
    }
}

/**
 * Main function
 */
fn main()
{
    if env::args().len() < 3
    {
        println!("\nUsage:\n\ns3-file-upload LOCAL_PATH BUCKET_NAME [OPTIONS]");
        println!("\nOptions:\n\n--ignored_directories    List of directory names using a comma separator, e.g. --ignored_directories=ignored_dir_one,ignored_dir_two");
        return;
    }

    let local_path  = env::args().nth(1).unwrap();
    let bucket_name = env::args().nth(2).unwrap();

    if !Path::new("credentials").exists()
    {
        println!("credentials file could not be found");
        return;
    }

    let mut ignored_directories: Vec<String> = Vec::new();

    // Set the ignored directories if the ignored_directories parameter is set
    for parameter in env::args()
    {
        if parameter.contains("--ignored_directories=")
        {
            let directories = parameter.split("=").nth(1).unwrap();

            for directory in directories.split(",")
            {
                ignored_directories.push(String::from(directory));
            }
        }
    }

    let mut files: Vec<LocalFile> = Vec::new();
    get_local_files(&local_path, &mut files, &mut ignored_directories);

    // NOTE: bucket_objects is currently not used for anything.
    let mut bucket_objects: Vec<Object> = Vec::new();
    get_bucket_objects(&bucket_name, &mut bucket_objects);

    println!("\nFiles found to be uploaded:\n");

    for file in &files
    {
        println!("{} [{}]", file.path.to_str().unwrap(), local_file_get_md5(file));
    }

    let mut input_string = String::new();

    while input_string != "y" && input_string != "n"
    {
        println!("\nConfirm upload? <y/N>");

        input_string.clear();
        stdin().read_line(&mut input_string).expect("Did not input string");
        input_string = String::from(input_string.trim().to_lowercase());

        if input_string == "" || input_string == "no"
        {
            input_string = String::from("n");
        }
        else if input_string == "yes"
        {
            input_string = String::from("y");
        }
    }

    let confirm_upload = input_string == "y";

    if confirm_upload
    {
        for file in &files
        {
            local_file_upload_to_bucket(&file, &local_path, &bucket_name);
        }
    }
    else
    {
        println!("Upload cancelled")
    }
}
<commit_msg>Only new/modified files are uploaded<commit_after>/**
 * s3-file-upload
 */

extern crate rusoto;
extern crate crypto;

use std::env;
use std::fs;
use std::path::Path;
use std::path::PathBuf;
use std::fs::{File, remove_file};
use std::io::{stdin, Read, Write};
use std::clone::Clone;
use std::collections::HashMap;

use rusoto::{ProfileProvider, Region};
use rusoto::s3::{Object, S3Client, ListObjectsV2Request, GetObjectRequest, PutObjectRequest};
use rusoto::default_tls_client;

use crypto::md5::Md5;
use crypto::digest::Digest;

/**
 * Struct for files
 */
#[derive(Clone)]
struct LocalFile
{
    path: PathBuf
}

/**
 * Get the mime type for a local file
 */
fn local_file_get_mime(file: &LocalFile) -> String
{
    let extension = file.path.extension().unwrap().to_str();

    let mime_type = match extension.as_ref()
    {
        Some(&"html") => "text/html",
        Some(&"css")  => "text/css",
        Some(&"png")  => "image/png",
        Some(&"gif")  => "image/gif",
        Some(&"jpg")  => "image/jpeg",
        Some(&"xml")  => "application/xml",
        Some(&"txt")  => "text/plain",
        _             => "application/octet-stream",
    };

    return mime_type.to_owned();
}

/**
 * Get the MD5 checksum of a local file
 */
fn local_file_get_md5(file: &LocalFile) -> String
{
    let mut file_handle = File::open(&file.path).unwrap();
    let mut contents: Vec<u8> = Vec::new();

    match file_handle.read_to_end(&mut contents)
    {
        Ok(_) =>
        {
            let mut md5_checksum = Md5::new();
            md5_checksum.input(&contents);
            return md5_checksum.result_str();
        }
        Err(error) =>
        {
            println!("Error: {}", error);
            return String::from("");
        }
    }
}

/**
 * Get a relative path for a local file
 */
fn local_file_get_relative_path(file: &LocalFile, local_path: &String) -> String
{
    let full_path: String = file.path.as_path().to_str().unwrap().to_owned();
    let last_path_section = full_path.split(local_path)
        .last()
        .unwrap()
        .replace("\\", "/");

    return last_path_section.to_owned();
}

/**
 * Upload a local file to an s3 bucket location
 */
fn local_file_upload_to_bucket(file: &LocalFile, local_path: &String, bucket_name: &String, public_file: bool)
{
    let provider = ProfileProvider::with_configuration(Path::new("credentials"), "user");
    let client = S3Client::new(default_tls_client().unwrap(), provider, Region::EuWest1);

    let file = file.clone();

    print!("Uploading \"{}\" to \"{}/{}\"...", file.path.to_str().unwrap(), bucket_name, local_file_get_relative_path(&file, &local_path));
    Some(std::io::stdout().flush());

    let mut file_handle = File::open(&file.path).expect("Could not open file");
    let mut contents: Vec<u8> = Vec::new();

    match file_handle.read_to_end(&mut contents)
    {
        Ok(_) =>
        {
            let mut acl = String::from("private");
            if public_file
            {
                acl = String::from("public-read");
            }

            let request = PutObjectRequest
            {
                bucket: bucket_name.to_owned(),
                key: local_file_get_relative_path(&file, &local_path),
                body: Some(contents),
                acl: Some(acl),
                content_type: Some(local_file_get_mime(&file)),
                ..PutObjectRequest::default()
            };

            match client.put_object(&request)
            {
                Ok(_) =>
                {
                    println!(" DONE");
                }
                Err(error) =>
                {
                   println!(" Error: {}", error);
                }
            }
        }
        Err(error) =>
        {
            println!("Error: {}", error);
        }
    }
}

/**
 * Create the local file checksums
 */
fn local_file_create_checksums(files: &Vec<LocalFile>, local_path: &String) -> LocalFile
{
    let mut path = PathBuf::from(local_path);
    path.push("checksums.txt");

    let mut checksums_file = File::create(path.to_str().unwrap()).expect("Could not create checksums");

    for file in files.clone()
    {
        let checksums_line = format!("{} {}\n", local_file_get_relative_path(&file, local_path), local_file_get_md5(&file));
        checksums_file.write(checksums_line.as_bytes()).expect("Could not write checksums");
    }

    return LocalFile { path: path };
}

/**
 * Delete the local file checksums
 */
fn local_file_delete_checksums(local_path: &String)
{
    let mut path = PathBuf::from(local_path);
    path.push("checksums.txt");

    remove_file(path).expect("Could not remove checksums");
}

/**
 * Get objects in an s3 bucket
 */
fn get_bucket_objects(bucket_name: &String, objects: &mut Vec<Object>)
{
    let provider = ProfileProvider::with_configuration(Path::new("credentials"), "user");
    let client = S3Client::new(default_tls_client().unwrap(), provider, Region::EuWest1);

    let request = ListObjectsV2Request
    {
        bucket: bucket_name.to_owned(),
        ..ListObjectsV2Request::default()
    };

    if let Ok(response) = client.list_objects_v2(&request)
    {
        for object in &response.contents.unwrap()
        {
            objects.push(object.clone());
        }
    }
}

/**
 * Struct for checksums
 */
#[derive(Clone)]
struct Checksums
{
    files: HashMap<String, String>
}

/**
 * Get the s3 bucket checksums
 */
fn get_bucket_object_checksums(bucket_name: &String, bucket_objects: &Vec<Object>) -> Option<Checksums>
{
    match bucket_objects.iter().position(|obj| obj.clone().key.unwrap() == String::from("checksums.txt"))
    {
        Some(index) =>
        {
            let provider = ProfileProvider::with_configuration(Path::new("credentials"), "user");
            let client = S3Client::new(default_tls_client().unwrap(), provider, Region::EuWest1);

            let obj = bucket_objects.get(index).clone().unwrap();

            let request = GetObjectRequest
            {
                bucket: bucket_name.to_owned(),
                key: obj.clone().key.unwrap(),
                ..GetObjectRequest::default()
            };

            if let Ok(response) = client.get_object(&request)
            {
                let contents_bytes = response.body.unwrap();
                let contents = String::from_utf8_lossy(&contents_bytes);

                let mut hash_map = HashMap::new();

                for line in contents.split("\n")
                {
                    let mut line_split = line.split_whitespace();
                    let path = line_split.next();
                    let md5 = line_split.next();

                    if path != None && md5 != None
                    {
                        let path = String::from(path.unwrap());
                        let md5 = String::from(md5.unwrap());
                        hash_map.insert(path, md5);
                    }
                }

                let checksums = Checksums {files: hash_map};
                Some(checksums)
            }
            else
            {
                None
            }
        },
        None =>
        {
            None
        }
    }
}

/**
 * Check if the file matches the checksums
 */
fn local_file_matches_checksums(file: &LocalFile, checksums: &Checksums) -> bool
{
    for (path, md5) in &checksums.files
    {
        if local_file_get_md5(file) == md5.to_owned()
        {
            return true;
        }
    }
    return false;
}

/**
 * Get files from a local path
 */
fn get_local_files(local_path: &String, files: &mut Vec<LocalFile>, ignored_directories: &mut Vec<String>)
{
    if let Ok(entries) = fs::read_dir(Path::new(&local_path))
    {
        for entry in entries
        {
            let file = entry.unwrap();

            if let Ok(metadata) = file.metadata()
            {
                if metadata.is_dir()
                {
                    if !ignored_directories.contains(&file.file_name().to_str().unwrap().to_owned())
                    {
                        get_local_files(&file.path().to_str().unwrap().to_owned(), files, ignored_directories);
                    }
                }
                else
                {
                    files.push(LocalFile { path: file.path() });
                }
            }
        }
    }
}

/**
 * Main function
 */
fn main()
{
    if env::args().len() < 3
    {
        println!("\nUsage:\n\ns3-file-upload LOCAL_PATH BUCKET_NAME [OPTIONS]");
        println!("\nOptions:\n\n--ignored_directories    List of directory names using a comma separator, e.g. --ignored_directories=ignored_dir_one,ignored_dir_two");
        return;
    }

    let local_path  = env::args().nth(1).unwrap();
    let bucket_name = env::args().nth(2).unwrap();

    if !Path::new("credentials").exists()
    {
        println!("credentials file could not be found");
        return;
    }

    let mut ignored_directories: Vec<String> = Vec::new();

    // Set the ignored directories if the ignored_directories parameter is set
    for parameter in env::args()
    {
        if parameter.contains("--ignored_directories=")
        {
            let directories = parameter.split("=").nth(1).unwrap();

            for directory in directories.split(",")
            {
                ignored_directories.push(String::from(directory));
            }
        }
    }

    let mut files: Vec<LocalFile> = Vec::new();
    get_local_files(&local_path, &mut files, &mut ignored_directories);

    let mut bucket_objects: Vec<Object> = Vec::new();
    get_bucket_objects(&bucket_name, &mut bucket_objects);

    let checksums = match get_bucket_object_checksums(&bucket_name, &bucket_objects)
    {
        Some(checksums) =>
        {
            Some(checksums)
        },
        None =>
        {
            let hash_map = HashMap::new();
            let checksums = Checksums {files: hash_map};
            Some(checksums)
        }
    }.unwrap();

    println!("\nFiles found to be uploaded:\n");

    for file in &files
    {
        if !local_file_matches_checksums(&file, &checksums)
        {
            println!("{}", local_file_get_relative_path(file, &local_path));
        }
    }

    let mut input_string = String::new();

    while input_string != "y" && input_string != "n"
    {
        println!("\nConfirm upload? <y/N>");

        input_string.clear();
        stdin().read_line(&mut input_string).expect("Did not input string");
        input_string = String::from(input_string.trim().to_lowercase());

        if input_string == "" || input_string == "no"
        {
            input_string = String::from("n");
        }
        else if input_string == "yes"
        {
            input_string = String::from("y");
        }
    }

    let confirm_upload = input_string == "y";

    if confirm_upload
    {
        for file in &files
        {
            if !local_file_matches_checksums(&file, &checksums)
            {
                local_file_upload_to_bucket(&file, &local_path, &bucket_name, true);
            }
        }

        let new_checksums = local_file_create_checksums(&files, &local_path);
        local_file_upload_to_bucket(&new_checksums, &local_path, &bucket_name, false);
        local_file_delete_checksums(&local_path);
    }
    else
    {
        println!("Upload cancelled")
    }
}
<|endoftext|>


<commit_before>/*
  Rust is an Expression-Based Systems language.
  Declare main function
*/
fn main() {

    // Metaprogramming calling a macro and passing staticaly allocated string arg
    println!("Hello, world!");

    /*
      Variable binding expression.
      <let expression> is Full Pattern for Pattern Matching.
      Rust's Type Inference automatically determines an expression's Type.
      Alternatively be explicit and supply an expression's Type after a colon.
      Or simply annotate the Type in a comment to understand the Type Rust infers.
      Rust Bindings are immutable by Default.
      Mutable Bindings require 'mut' prefix for Type Safety.
    */
    let a: i32 = 5; // assign immutable binding 'a' with Type i32 and Value 5

    // If statement based on Branch concept
    if a == 5 {
      println!("x is five");
    } else {
      println!("x is not five");
    }

    let (x, y, z) = (1, 2, 3);  // assign immutable bindings x, y, z to 1, 2, 3

    let mut b: i32 = 10; // assign mutable binding 'b'
    b = 20; // re-assign to new Value

    /*
      Rust's 'If' is an Expression. It returns a value to initialise
      the 'Let' binding ("Declaration Statement") with. 'Let' is assigned an Expression.
      Assignment to an already bound variable is an Expression.
      Assignment Value in Rust is the special Unit Type () in Rust's Type System.
      In other languages 'If' is a Statements (does not return value).

      Rust's "Expression Statement" converts an Expression into a Statement.
      Semi-colons separate each Expression.
      Semi-colons convert Expression into Statement, throws Value away, returns Unit Type ()
      (causing Type Error when expect a different Type such as i32 to be valid)
    */
    let c: i32 = if b == 20 { 25 } else { 15 };  // Explicitely specify 'c' as of Integer Type
    println!("b is: {}", b); // Interpret interpolation with moustaches
    println!("c is: {}", c);

    incr(b, c); // Call Fn passing Args
    print_sum(b, c); // Call Fn passing Args

    // Compound Data Structure Type Tuple is simple fixed size ordered list where &str is String Slice
    let tuple1: (i32, &str) = (1, "hello"); // Mutable. Type annotated. Without: let x = (1, "hello");
    let mut tuple2: (i32, i32) = (1, 2); // Type annotated. Without: let x = (1, "hello");
    let tuple3: (i32, i32) = (3, 4);

    // Note that for a Tuple with a Single value the trailing commas are mandatory
    let (x,): (i32,) = (1,);

    if tuple2 != tuple3 {
        tuple2 = tuple3; // Assign Tuple to another Mutable Tuple (with same Arity and Types)
        println!("tuple2 and tuple3 not equal so assigning");
    } else {
        println!("tuple2 and tuple3 do not have same Arity, Types, Order, and Value, not assigning");
    }

    /*
      Destructuring Let (access parameters of Tuple).
      Let statement performs Multiple Bindings assignments when LHS and RHS Pattern Match
    */
    let (t1, t2) = tuple2;

    println!("t1 and t2 is {}", t1 + t2); // Note: Expects t1 and t2 to be same Type

    // Tuples to return Multiple Values from a Function
    let (tnt1, tnt2) = next_two(5);
    println!("tnt1, tnt2 = {}, {}", tnt1, tnt2);

    try_structs();

    try_tuple_structs();

    try_newtype();

    try_enums();
}

/*
  Rust Structs are of Record Type form similar to Tuples.
  Elements in Structs each have a Name (aka Field/Member) and a Type
*/

// Declare a Struct with a name (camel case). Values Immutable by Default
struct PointInSpace {
    x: i32,
    y: i32
}

fn try_structs() {
  // Create Instance (Mutable) of Struct using Let and set each Field (any order)
  let mut origin = PointInSpace { x: 0, y: 0 }; // origin: PointInSpace

  origin.x = 20;

  println!("Origin is at ({}, {})", origin.x, origin.y); // Dot Notation to access Fields
}

/*
  Rust Tuple Structs are a hybrid data type similar to Tuple and Struct.
  Elements in Tuple Structs DO NOT have a Name, they only have a Type.
  Structs are preferred over Tuple Structs since they have actual Names for Elements.
  Note: Tuple Structs must use round brackets and colon
*/

struct Color (
  i32,
  i32,
  i32
);

struct PointInWater (
  i32,
  i32,
  i32
);

fn try_tuple_structs() {
  let black = Color(0, 0, 0);
  let origin = PointInWater(0, 0, 0);
}

// Declare a Newtype.
struct Inches (i32);

// Newtypes are Tuple Structs with only One Element. Beneficial for creating
// New Types similar to others (Cloning)
fn try_newtype() {
  let length = Inches(10);
  // Extract inner Integer Type by Desctructing the Let
  let Inches(integer_length) = length; // Assigns length to integer_length
  println!("Newtype length is {} inches", integer_length);
}

// Enums variants are a "Sum Type" that ties a set of alternatives to a specific name
fn try_enums() {
  // Define a Character to be either a Digit or Other. The alternative names may be
  // used via their fully scoped names i.e. Character::Other
  // Sub-datastructure types allowable in an Enum include Structs, Tuple Structs
  // Enums do not have access to operators: ==, !=, *, +, <, >=

  enum Character {
    Digit(i32), // Character::Digit is a name tied to an i32
    Other, // Other is just a name (not tied to a specific Type)
  }

  // Assignment
  let ten  = Character::Digit(10);

}

// Returned Tuple is a Single Value (containing Multiple Values)
fn next_two(x: i32) -> (i32, i32) { (x + 1, x + 2) }

// All control paths must Return a Value
fn incr(m: i32, n: i32) -> i32 {
    if n < m {
      return n; // Early Return
    } else {
      println!("incrementing n from: {}", n);
      return n + 10;
    }
}

/*
  Rust forces Declare expected Type of incoming Fn Args. Type Inference in Fn body.
  Rust allows Declaration expected Type of outgoing Return Value after '->'
*/
fn print_sum(m: i32, n: i32) -> () { // Return the Unit Type () (aka nil)
    if m < n {
      println!("sum with m < n is: {}", m + n);
    } else {
      println!("sum with m >= n is: {}", m + n);
    }
}<commit_msg>Enums with Imported Ordering Standard Module for Namespace comparisons<commit_after>// Import the Ordering Enum from the Rust Standard Library
// http://doc.rust-lang.org/std/cmp/enum.Ordering.html
// Ordering Enum form used for Comparisons
// enum Ordering {
//   Less,
//   Equal,
//   Greater,
// }
// :: Symbol indicates a Namespace
// 'std' Module contains Sub-Module 'cmp' containing Sub-Sub-Module 'Ordering'
use std::cmp::Ordering;

// Configure the Ordering Enum
// 'cmp' is a Function that compares two given parameters and returns an Ordering
// where depending on the difference between these parameters, the returned Ordering
// may be Ordering:Less, Ordering::Greater, or Ordering::Equal
// Variants of the Enum (Ordering) are Namespaced under the Enum itself.
fn cmp(a: i32, b: i32) -> Ordering {
  if a < b      { Ordering::Less }
  else if a > b { Ordering::Greater }
  else          { Ordering::Equal }
}

/*
  Rust is an Expression-Based Systems language.
  Declare main function
*/
fn main() {

    // Metaprogramming calling a macro and passing staticaly allocated string arg
    println!("Hello, world!");

    /*
      Variable binding expression.
      <let expression> is Full Pattern for Pattern Matching.
      Rust's Type Inference automatically determines an expression's Type.
      Alternatively be explicit and supply an expression's Type after a colon.
      Or simply annotate the Type in a comment to understand the Type Rust infers.
      Rust Bindings are immutable by Default.
      Mutable Bindings require 'mut' prefix for Type Safety.
    */
    let a: i32 = 5; // assign immutable binding 'a' with Type i32 and Value 5

    // If statement based on Branch concept
    if a == 5 {
      println!("x is five");
    } else {
      println!("x is not five");
    }

    let (x, y, z) = (1, 2, 3);  // assign immutable bindings x, y, z to 1, 2, 3

    let mut b: i32 = 10; // assign mutable binding 'b'
    b = 20; // re-assign to new Value

    /*
      Rust's 'If' is an Expression. It returns a value to initialise
      the 'Let' binding ("Declaration Statement") with. 'Let' is assigned an Expression.
      Assignment to an already bound variable is an Expression.
      Assignment Value in Rust is the special Unit Type () in Rust's Type System.
      In other languages 'If' is a Statements (does not return value).

      Rust's "Expression Statement" converts an Expression into a Statement.
      Semi-colons separate each Expression.
      Semi-colons convert Expression into Statement, throws Value away, returns Unit Type ()
      (causing Type Error when expect a different Type such as i32 to be valid)
    */
    let c: i32 = if b == 20 { 25 } else { 15 };  // Explicitely specify 'c' as of Integer Type
    println!("b is: {}", b); // Interpret interpolation with moustaches
    println!("c is: {}", c);

    incr(b, c); // Call Fn passing Args
    print_sum(b, c); // Call Fn passing Args

    // Compound Data Structure Type Tuple is simple fixed size ordered list where &str is String Slice
    let tuple1: (i32, &str) = (1, "hello"); // Mutable. Type annotated. Without: let x = (1, "hello");
    let mut tuple2: (i32, i32) = (1, 2); // Type annotated. Without: let x = (1, "hello");
    let tuple3: (i32, i32) = (3, 4);

    // Note that for a Tuple with a Single value the trailing commas are mandatory
    let (x,): (i32,) = (1,);

    if tuple2 != tuple3 {
        tuple2 = tuple3; // Assign Tuple to another Mutable Tuple (with same Arity and Types)
        println!("tuple2 and tuple3 not equal so assigning");
    } else {
        println!("tuple2 and tuple3 do not have same Arity, Types, Order, and Value, not assigning");
    }

    /*
      Destructuring Let (access parameters of Tuple).
      Let statement performs Multiple Bindings assignments when LHS and RHS Pattern Match
    */
    let (t1, t2) = tuple2;

    println!("t1 and t2 is {}", t1 + t2); // Note: Expects t1 and t2 to be same Type

    // Tuples to return Multiple Values from a Function
    let (tnt1, tnt2) = next_two(5);
    println!("tnt1, tnt2 = {}, {}", tnt1, tnt2);

    try_structs();

    try_tuple_structs();

    try_newtype();

    try_enums();
}

/*
  Rust Structs are of Record Type form similar to Tuples.
  Elements in Structs each have a Name (aka Field/Member) and a Type
*/

// Declare a Struct with a name (camel case). Values Immutable by Default
struct PointInSpace {
    x: i32,
    y: i32
}

fn try_structs() {
  // Create Instance (Mutable) of Struct using Let and set each Field (any order)
  let mut origin = PointInSpace { x: 0, y: 0 }; // origin: PointInSpace

  origin.x = 20;

  println!("Origin is at ({}, {})", origin.x, origin.y); // Dot Notation to access Fields
}

/*
  Rust Tuple Structs are a hybrid data type similar to Tuple and Struct.
  Elements in Tuple Structs DO NOT have a Name, they only have a Type.
  Structs are preferred over Tuple Structs since they have actual Names for Elements.
  Note: Tuple Structs must use round brackets and colon
*/

struct Color (
  i32,
  i32,
  i32
);

struct PointInWater (
  i32,
  i32,
  i32
);

fn try_tuple_structs() {
  let black = Color(0, 0, 0);
  let origin = PointInWater(0, 0, 0);
}

// Declare a Newtype.
struct Inches (i32);

// Newtypes are Tuple Structs with only One Element. Beneficial for creating
// New Types similar to others (Cloning)
fn try_newtype() {
  let length = Inches(10);
  // Extract inner Integer Type by Desctructing the Let
  let Inches(integer_length) = length; // Assigns length to integer_length
  println!("Newtype length is {} inches", integer_length);
}

// Enums variants are a "Sum Type" that ties a set of alternatives to a specific name
fn try_enums() {
  // Define a Character to be either a Digit or Other. The alternative names may be
  // used via their fully scoped names i.e. Character::Other
  // Sub-datastructure types allowable in an Enum include Structs, Tuple Structs
  // Enums do not have access to operators: ==, !=, *, +, <, >=

  enum Character {
    Digit(i32), // Character::Digit is a name tied to an i32
    Other, // Other is just a name (not tied to a specific Type)
  }

  // Assignment
  let ten  = Character::Digit(10);

  let x = 5;
  let y = 10;

  // 'ordering' variable is of Ordering type (ordering: Ordering)
  // so it contains one of the three values defined in the 'cmp' Function
  // and we discover which one by using Conditional Operators to check
  let ordering = cmp(x, y);

  if ordering == Ordering::Less {
      println!("Less");
  } else if ordering == Ordering::Greater {
      println!("Greater");
  } else if ordering == Ordering::Equal {
      println!("Equal");
  }

}

// Returned Tuple is a Single Value (containing Multiple Values)
fn next_two(x: i32) -> (i32, i32) { (x + 1, x + 2) }

// All control paths must Return a Value
fn incr(m: i32, n: i32) -> i32 {
    if n < m {
      return n; // Early Return
    } else {
      println!("incrementing n from: {}", n);
      return n + 10;
    }
}

/*
  Rust forces Declare expected Type of incoming Fn Args. Type Inference in Fn body.
  Rust allows Declaration expected Type of outgoing Return Value after '->'
*/
fn print_sum(m: i32, n: i32) -> () { // Return the Unit Type () (aka nil)
    if m < n {
      println!("sum with m < n is: {}", m + n);
    } else {
      println!("sum with m >= n is: {}", m + n);
    }
}<|endoftext|>


<commit_before>trait Normalize {
  fn normalize(&self) -> Self;
}

#[derive(Clone, Copy, Debug, Eq, PartialEq)]
enum Const {
  Data,
  Codata,
}

impl Normalize for Const {
  fn normalize(&self) -> Const {
    *self
  }
}

impl ToString for Const {
  fn to_string(&self) -> String {
    match *self {
      Const::Data => "data".to_string(),
      Const::Codata => "codata".to_string(),
    }
  }
}

#[derive(Clone, Debug, Eq, PartialEq)]
struct Var {
  name: String,
  idx: i32,
}

impl Var {
  pub fn new(name: &str, idx: i32) -> Var {
    Var {
      name: name.to_string(),
      idx: idx,
    }
  }

  pub fn shift(&self) -> Var {
    Var {
      name: self.name.clone(),
      idx: self.idx + 1,
    }
  }
}

impl Normalize for Var {
  fn normalize(&self) -> Var {
    self.clone()
  }
}

impl ToString for Var {
  fn to_string(&self) -> String {
    if self.name.len() == 0 {
      "".to_string()
    } else if self.idx == 0 {
      self.name.clone()
    } else {
      self.name.clone() + "@" + &self.idx.to_string()
    }
  }
}

#[derive(Clone, Debug, Eq, PartialEq)]
enum Expr {
  // Type system constants
  Const(Const),
  // Bound variables
  Var(Var),
  // Lambda
  Lam(Var, Box<Expr>, Box<Expr>),
  // "forall"
  Pi(Var, Box<Expr>, Box<Expr>),
  // Function application
  App(Box<Expr>, Box<Expr>),
}

impl Expr {
  pub fn constant(constant: Const) -> Expr {
    Expr::Const(constant)
  }
  pub fn var(v: &Var) -> Expr {
    Expr::Var(v.clone())
  }
  pub fn lam(var: Var, ty: Expr, body: Expr) -> Expr {
    Expr::Lam(var, Box::new(ty), Box::new(body))
  }
  pub fn pi(var: Var, ty: Expr, body: Expr) -> Expr {
    Expr::Pi(var, Box::new(ty), Box::new(body))
  }
  pub fn app(f: Expr, arg: Expr) -> Expr {
    Expr::App(Box::new(f), Box::new(arg))
  }

  pub fn is_constant(&self) -> bool {
    match *self {
      Expr::Const(_) => true,
      _ => false,
    }
  }
  pub fn is_var(&self) -> bool {
    match *self {
      Expr::Var(_) => true,
      _ => false,
    }
  }
  pub fn is_lam(&self) -> bool {
    match *self {
      Expr::Lam(_, _, _) => true,
      _ => false,
    }
  }
  pub fn is_pi(&self) -> bool {
    match *self {
      Expr::Pi(_, _, _) => true,
      _ => false,
    }
  }
  pub fn is_app(&self) -> bool {
    match *self {
      Expr::App(_, _) => true,
      _ => false,
    }
  }
}

fn constant(c: Const) -> Expr {
  Expr::constant(c)
}
fn var(v: &Var) -> Expr {
  Expr::var(v)
}
fn lam(var: Var, ty: Expr, body: Expr) -> Expr {
  Expr::lam(var, ty, body)
}
fn pi(var: Var, ty: Expr, body: Expr) -> Expr {
  Expr::pi(var, ty, body)
}
fn app(f: Expr, arg: Expr) -> Expr {
  Expr::app(f, arg)
}

fn replace(val: &Var, with: &Expr, body: &Expr) -> Expr {
  use Expr::*;
  match *body {
    Const(ref constant) => body.clone(),
    Var(ref var) => {
      if *var == *val {
        with.clone()
      } else {
        body.clone()
      }
    }
    Lam(ref var, ref ty, ref body) => {
      // TODO: Check var == val
      // let val = if *var == *val {
      //   &val
      // } else {
      //   &val
      // };
      let ty = Box::new(replace(val, with, &ty));
      let body = Box::new(replace(val, with, &body));
      Lam(var.clone(), ty, body)
    }
    Pi(ref var, ref ty, ref body) => {
      // TODO: Check var == val
      let ty = Box::new(replace(val, with, &ty));
      let body = Box::new(replace(val, with, &body));
      Pi(var.clone(), ty, body)
    }
    App(ref f, ref arg) => {
      let f = Box::new(replace(val, with, &f));
      let arg = Box::new(replace(val, with, &arg));
      App(f, arg)
    }
  }
}

impl Normalize for Expr {
  fn normalize(&self) -> Expr {
    use Expr::*;
    // println!("normalize {}", self.to_string());
    match *self {
      Const(ref constant) => Const(constant.normalize()),
      Var(ref var) => Var(var.normalize()),
      Lam(ref var, ref ty, ref body) => {
        let l = &Lam(var.normalize(),
                     Box::new(ty.normalize()),
                     Box::new(body.normalize()));
        l.clone()
      }
      Pi(ref var, ref ty, ref body) => {
        let p = &Pi(var.normalize(),
                    Box::new(ty.normalize()),
                    Box::new(body.normalize()));
        p.clone()
      }
      App(ref f, ref arg) => {
        let f = f.normalize();
        let arg = arg.normalize();
        if let Lam(var, ty, body) = f {
          replace(&var, &arg, &body)
        } else if let Pi(var, ty, body) = f {
          replace(&var, &arg, &body)
        } else {
          panic!("f isn't a function {}", f.to_string())
        }
      }
    }
  }
}

impl ToString for Expr {
  fn to_string(&self) -> String {
    use Expr::*;
    match *self {
      Const(ref constant) => constant.to_string(),
      Var(ref var) => var.to_string(),
      Lam(ref var, ref ty, ref body) => {
        let vars = var.to_string();
        if vars.len() == 0 {
          ty.to_string() + " -> " + &body.to_string()
        } else {
          "(".to_string() + &var.to_string() + " : " + &ty.to_string() +
          ") -> " + &body.to_string()
        }
      }
      Pi(ref var, ref ty, ref body) => {
        let vars = var.to_string();
        if vars.len() == 0 {
          "forall (".to_string() + &ty.to_string() + ") -> " + &body.to_string()
        } else {
          "forall (".to_string() + &var.to_string() + " : " +
          &ty.to_string() + ") -> " + &body.to_string()
        }
      }
      App(ref f, ref arg) => {
        let mut s = String::new();
        if f.is_lam() || f.is_pi() {
          s.push('(');
          s.push_str(&f.to_string()[..]);
          s.push(')');
        } else {
          s.push_str(&f.to_string()[..]);
        }
        s.push(' ');
        if arg.is_lam() || arg.is_pi() {
          s.push('(');
          s.push_str(&arg.to_string()[..]);
          s.push(')');
        } else {
          s.push_str(&arg.to_string()[..]);
        }
        // + " ".to_string() + (if arg.is_lam() || arg.is_pi() {
        //   "(".to_string() + &arg.to_string() + ")"
        // } else {
        //   &arg.to_string()
        // })
        s
      }
    }
  }
}

fn main() {
  // println!("{}", Const::Data.to_string());
  // let a = Var::new("a", 0);
  // let x = Var::new("x", 0);
  // let expra = var(&a);
  // let exprx = var(&x);
  // println!("{}", x.to_string());
  // let id = pi(a.clone(), constant(Const::Data), lam(x, expra, exprx));
  // println!("{}", id.to_string());
  // println!("{}", id.normalize().to_string());
  // let apply_int = app(id.clone(), var(&Var::new("int", 0)));
  // println!("{}", apply_int.to_string());
  // println!("{}", apply_int.normalize().to_string());
  // let apply_id = app(app(id, var(&Var::new("int", 0))), var(&Var::new("1", 0)));
  // println!("{}", apply_id.to_string());
  // println!("{}", apply_id.normalize().to_string());

  // (   \(id : forall (a : *) -> a -> a)
  // ->  id (forall (a : *) -> a -> a) id  -- Apply the identity function to itself
  // )
  //
  // -- id
  // (\(a : *) -> \(x : a) -> x)

  let a = Var::new("a", 0);
  let x = Var::new("x", 0);
  let id = Var::new("id", 0);
  let unused = Var::new("", 0);
  let ty = pi(a.clone(),
              constant(Const::Data),
              lam(unused, Expr::var(&a), Expr::var(&a)));
  println!("{}", ty.to_string());
  let id_impl = pi(a.clone(),
                   constant(Const::Data),
                   lam(x.clone(), Expr::var(&a), Expr::var(&x)));
  println!("{}", id_impl.to_string());
  let id2 = lam(id.clone(),
                ty.clone(),
                app(app(Expr::var(&id.clone()), ty.clone()),
                    Expr::var(&id.clone())));
  println!("{}", id2.to_string());
  let id2app = app(lam(id.clone(),
                       ty.clone(),
                       app(app(Expr::var(&id.clone()), ty.clone()),
                           Expr::var(&id.clone()))),
                   id_impl.clone());
  println!("{}", id2app.to_string());
  println!("{}", id2app.normalize().to_string());
}

#[test]
fn test_to_string() {
  let codata = Const::Codata;
  println!("{}", codata.to_string());
  assert_eq!("codata", codata.to_string());
  let a = Var::new("a", 0);
  let x = Var::new("x", 0);
  let expra = var(&a);
  let exprx = var(&x);
  println!("{}", x.to_string());
  assert_eq!("x", x.to_string());
  let id = pi(a.clone(), constant(Const::Data), lam(x, expra, exprx));
  println!("{}", id.to_string());
  assert_eq!("forall (a : data) -> (x : a) -> x", id.to_string());
  let apply_id = app(app(id, var(&Var::new("int", 0))), var(&Var::new("1", 0)));
  println!("{}", apply_id.to_string());
  assert_eq!("(forall (a : data) -> (x : a) -> x) int 1",
             apply_id.to_string());
}
<commit_msg>Fix bad recursion in normalize so that applying id to id correctly normalizes to id.<commit_after>trait Normalize {
  fn normalize(&self) -> Self;
}

#[derive(Clone, Copy, Debug, Eq, PartialEq)]
enum Const {
  Data,
  Codata,
}

impl Normalize for Const {
  fn normalize(&self) -> Const {
    *self
  }
}

impl ToString for Const {
  fn to_string(&self) -> String {
    match *self {
      Const::Data => "data".to_string(),
      Const::Codata => "codata".to_string(),
    }
  }
}

#[derive(Clone, Debug, Eq, PartialEq)]
struct Var {
  name: String,
  idx: i32,
}

impl Var {
  pub fn new(name: &str, idx: i32) -> Var {
    Var {
      name: name.to_string(),
      idx: idx,
    }
  }

  pub fn shift(&self) -> Var {
    Var {
      name: self.name.clone(),
      idx: self.idx + 1,
    }
  }
}

impl Normalize for Var {
  fn normalize(&self) -> Var {
    self.clone()
  }
}

impl ToString for Var {
  fn to_string(&self) -> String {
    if self.name.len() == 0 {
      "".to_string()
    } else if self.idx == 0 {
      self.name.clone()
    } else {
      self.name.clone() + "@" + &self.idx.to_string()
    }
  }
}

#[derive(Clone, Debug, Eq, PartialEq)]
enum Expr {
  // Type system constants
  Const(Const),
  // Bound variables
  Var(Var),
  // Lambda
  Lam(Var, Box<Expr>, Box<Expr>),
  // "forall"
  Pi(Var, Box<Expr>, Box<Expr>),
  // Function application
  App(Box<Expr>, Box<Expr>),
}

impl Expr {
  pub fn constant(constant: Const) -> Expr {
    Expr::Const(constant)
  }
  pub fn var(v: &Var) -> Expr {
    Expr::Var(v.clone())
  }
  pub fn lam(var: Var, ty: Expr, body: Expr) -> Expr {
    Expr::Lam(var, Box::new(ty), Box::new(body))
  }
  pub fn pi(var: Var, ty: Expr, body: Expr) -> Expr {
    Expr::Pi(var, Box::new(ty), Box::new(body))
  }
  pub fn app(f: Expr, arg: Expr) -> Expr {
    Expr::App(Box::new(f), Box::new(arg))
  }

  pub fn is_constant(&self) -> bool {
    match *self {
      Expr::Const(_) => true,
      _ => false,
    }
  }
  pub fn is_var(&self) -> bool {
    match *self {
      Expr::Var(_) => true,
      _ => false,
    }
  }
  pub fn is_lam(&self) -> bool {
    match *self {
      Expr::Lam(_, _, _) => true,
      _ => false,
    }
  }
  pub fn is_pi(&self) -> bool {
    match *self {
      Expr::Pi(_, _, _) => true,
      _ => false,
    }
  }
  pub fn is_app(&self) -> bool {
    match *self {
      Expr::App(_, _) => true,
      _ => false,
    }
  }
}

fn constant(c: Const) -> Expr {
  Expr::constant(c)
}
fn var(v: &Var) -> Expr {
  Expr::var(v)
}
fn lam(var: Var, ty: Expr, body: Expr) -> Expr {
  Expr::lam(var, ty, body)
}
fn pi(var: Var, ty: Expr, body: Expr) -> Expr {
  Expr::pi(var, ty, body)
}
fn app(f: Expr, arg: Expr) -> Expr {
  Expr::app(f, arg)
}

fn replace(val: &Var, with: &Expr, body: &Expr) -> Expr {
  use Expr::*;
  // println!("replace {} with {} in {}",
  //          val.to_string(),
  //          with.to_string(),
  //          body.to_string());
  match *body {
    Const(ref constant) => body.clone(),
    Var(ref var) => {
      if *var == *val {
        with.clone()
      } else {
        body.clone()
      }
    }
    Lam(ref var, ref ty, ref body) => {
      if *val == *var {
        Lam(var.clone(), ty.clone(), body.clone())
      } else {
        let ty = Box::new(replace(val, with, &ty));
        let body = Box::new(replace(val, with, &body));
        Lam(var.clone(), ty, body)
      }
    }
    Pi(ref var, ref ty, ref body) => {
      if *val == *var {
        Pi(var.clone(), ty.clone(), body.clone())
      } else {
        let ty = Box::new(replace(val, with, &ty));
        let body = Box::new(replace(val, with, &body));
        Pi(var.clone(), ty, body)
      }
    }
    App(ref f, ref arg) => {
      let f = Box::new(replace(val, with, &f));
      let arg = Box::new(replace(val, with, &arg));
      App(f, arg)
    }
  }
}

fn find_first_var(body: &Expr) -> Var {
  use Expr::*;
  match *body {
    Const(ref constant) => panic!("constant"),
    Var(ref var) => var.clone(),
    Lam(ref var, ref ty, ref body) => var.clone(),
    Pi(ref var, ref ty, ref body) => var.clone(),
    App(ref f, ref arg) => find_first_var(f),
  }
}

impl Normalize for Expr {
  fn normalize(&self) -> Expr {
    use Expr::*;
    // println!("normalize {}", self.to_string());
    match *self {
      Const(ref constant) => Const(constant.normalize()),
      Var(ref var) => Var(var.normalize()),
      Lam(ref var, ref ty, ref body) => {
        let l = &Lam(var.normalize(),
                     Box::new(ty.normalize()),
                     Box::new(body.normalize()));
        l.clone()
      }
      Pi(ref var, ref ty, ref body) => {
        let p = &Pi(var.normalize(),
                    Box::new(ty.normalize()),
                    Box::new(body.normalize()));
        p.clone()
      }
      App(ref f, ref arg) => {
        // let f = f.normalize();
        let f = *f.clone();
        let arg = arg.normalize();
        if let Lam(var, ty, body) = f {
          replace(&var, &arg, &body).normalize()
        } else if let Pi(var, ty, body) = f {
          replace(&var, &arg, &body).normalize()
        } else if let App(ref f_in, ref arg_in) = f {
          replace(&find_first_var(&f_in), &arg_in, &f_in).normalize()
        } else {
          panic!("f isn't a function {}", f.to_string())
          // replace
        }
      }
    }
  }
}

impl ToString for Expr {
  fn to_string(&self) -> String {
    use Expr::*;
    match *self {
      Const(ref constant) => constant.to_string(),
      Var(ref var) => var.to_string(),
      Lam(ref var, ref ty, ref body) => {
        let vars = var.to_string();
        if vars.len() == 0 {
          ty.to_string() + " -> " + &body.to_string()
        } else {
          "(".to_string() + &var.to_string() + " : " + &ty.to_string() +
          ") -> " + &body.to_string()
        }
      }
      Pi(ref var, ref ty, ref body) => {
        let vars = var.to_string();
        if vars.len() == 0 {
          "forall (".to_string() + &ty.to_string() + ") -> " + &body.to_string()
        } else {
          "forall (".to_string() + &var.to_string() + " : " +
          &ty.to_string() + ") -> " + &body.to_string()
        }
      }
      App(ref f, ref arg) => {
        let mut s = String::new();
        if f.is_lam() || f.is_pi() {
          s.push('(');
          s.push_str(&f.to_string()[..]);
          s.push(')');
        } else {
          s.push_str(&f.to_string()[..]);
        }
        s.push(' ');
        if arg.is_lam() || arg.is_pi() {
          s.push('(');
          s.push_str(&arg.to_string()[..]);
          s.push(')');
        } else {
          s.push_str(&arg.to_string()[..]);
        }
        // + " ".to_string() + (if arg.is_lam() || arg.is_pi() {
        //   "(".to_string() + &arg.to_string() + ")"
        // } else {
        //   &arg.to_string()
        // })
        s
      }
    }
  }
}

fn main() {
  // println!("{}", Const::Data.to_string());
  // let a = Var::new("a", 0);
  // let x = Var::new("x", 0);
  // let expra = var(&a);
  // let exprx = var(&x);
  // println!("{}", x.to_string());
  // let id = pi(a.clone(), constant(Const::Data), lam(x, expra, exprx));
  // println!("{}", id.to_string());
  // println!("{}", id.normalize().to_string());
  // let apply_int = app(id.clone(), var(&Var::new("int", 0)));
  // println!("{}", apply_int.to_string());
  // println!("{}", apply_int.normalize().to_string());
  // let apply_id = app(app(id, var(&Var::new("int", 0))), var(&Var::new("1", 0)));
  // println!("{}", apply_id.to_string());
  // println!("{}", apply_id.normalize().to_string());

  // (   \(id : forall (a : *) -> a -> a)
  // ->  id (forall (a : *) -> a -> a) id  -- Apply the identity function to itself
  // )
  //
  // -- id
  // (\(a : *) -> \(x : a) -> x)

  // ∀(a : *) → a → a
  //
  // λ(a : *) → λ(x : a) → x

  let a = Var::new("a", 0);
  let x = Var::new("x", 0);
  let id = Var::new("id", 0);
  let unused = Var::new("", 0);
  let ty = pi(a.clone(),
              constant(Const::Data),
              lam(unused, Expr::var(&a), Expr::var(&a)));
  println!("{}", ty.to_string());
  let id_impl = pi(a.clone(),
                   constant(Const::Data),
                   lam(x.clone(), Expr::var(&a), Expr::var(&x)));
  println!("{}", id_impl.to_string());
  let id2 = lam(id.clone(),
                ty.clone(),
                app(app(Expr::var(&id.clone()), ty.clone()),
                    Expr::var(&id.clone())));
  println!("{}", id2.to_string());
  let id2app = app(lam(id.clone(),
                       ty.clone(),
                       app(app(Expr::var(&id.clone()), ty.clone()),
                           Expr::var(&id.clone()))),
                   id_impl.clone());
  println!("{}", id2app.to_string());
  println!("{}", id2app.normalize().to_string());
}

#[test]
fn test_to_string() {
  let codata = Const::Codata;
  println!("{}", codata.to_string());
  assert_eq!("codata", codata.to_string());
  let a = Var::new("a", 0);
  let x = Var::new("x", 0);
  let expra = var(&a);
  let exprx = var(&x);
  println!("{}", x.to_string());
  assert_eq!("x", x.to_string());
  let id = pi(a.clone(), constant(Const::Data), lam(x, expra, exprx));
  println!("{}", id.to_string());
  assert_eq!("forall (a : data) -> (x : a) -> x", id.to_string());
  let apply_id = app(app(id, var(&Var::new("int", 0))), var(&Var::new("1", 0)));
  println!("{}", apply_id.to_string());
  assert_eq!("(forall (a : data) -> (x : a) -> x) int 1",
             apply_id.to_string());
}
<|endoftext|>


<commit_before>#![allow(dead_code)]
#![feature(core)]
#![feature(old_io)]
#![feature(std_misc)]

extern crate sdl2;
extern crate sdl2_image;
extern crate time;

mod math;
mod world;
mod game;
mod input;
mod render;

use std::time::Duration;
use std::old_io::timer;
use std::path::Path;

use time::PreciseTime;

use sdl2::video::{Window, WindowPos, OPENGL};
use sdl2::mouse;
use sdl2::render::{RenderDriverIndex, ACCELERATED, Renderer};
use sdl2::pixels::PixelFormatEnum;
use sdl2::event::Event;
use sdl2_image::LoadSurface;

const WINDOW_WIDTH  :i32 = 3 * 320;
const WINDOW_HEIGHT :i32 = 3 * 240;

const W :usize = 320;
const H :usize = 240;

pub fn main() {
    let sdl_context = sdl2::init(sdl2::INIT_VIDEO).unwrap();

    let res_path = Path::new("/Users/jaburns/dev/rustycast/res");

    let window = match Window::new("RustyCast", WindowPos::PosCentered, WindowPos::PosCentered, WINDOW_WIDTH, WINDOW_HEIGHT, OPENGL) {
        Ok(window) => window,
        Err(err) => panic!("failed to create window: {}", err)
    };

    window.set_grab(true);
    mouse::set_relative_mouse_mode(true);

    let renderer = match Renderer::from_window(window, RenderDriverIndex::Auto, ACCELERATED) {
        Ok(renderer) => renderer,
        Err(err) => panic!("failed to create renderer: {}", err)
    };

    let mut sky = match LoadSurface::from_file(&res_path.join("sky.png")) {
        Ok(surface) => surface,
        Err(err) => panic!(format!("Failed to load png: {}", err))
    };

    let mut texture = renderer.create_texture_streaming(PixelFormatEnum::RGB24, (W as i32, H as i32)).unwrap();
    let mut drawer = renderer.drawer();
    let mut event_pump = sdl_context.event_pump();

    let mut inputs = input::InputState::new();
    let mut game = game::Game {
        sector: world::SectorIndex(0),
        pos: math::V2_ORIGIN,
        face_angle: 0.0,
        look_angle: 0.0,
        world: &world::temp(),
        show_map: false,
        t: 0.0
    };

    'main : loop {
        let last_time = PreciseTime::now();

        for event in event_pump.poll_iter() {
            inputs.check_event(&event);
            match event {
                Event::Quit {..} => { break 'main; },
                _ => {}
            }
        }

        if inputs.has_key(input::Key::Quit) {
            break 'main;
        }

        game.step(&inputs);
        texture.with_lock(None, |buffer, _| {
            game.render(&mut sky, buffer, W, H);
        }).unwrap();

        drawer.clear();
        drawer.copy(&texture, None, None);
        drawer.present();

        let delta_time = last_time.to(PreciseTime::now()).num_milliseconds();
        timer::sleep(Duration::milliseconds(15 - delta_time));
    }
}

<commit_msg>Removed old_timer<commit_after>#![allow(dead_code)]
#![feature(core)]
#![feature(std_misc)] // Used for std::time::Duration
#![feature(thread_sleep)]

extern crate sdl2;
extern crate sdl2_image;
extern crate time;

mod math;
mod world;
mod game;
mod input;
mod render;

use std::time::Duration;
use std::thread;
use std::path::Path;

use time::PreciseTime;

use sdl2::video::{Window, WindowPos, OPENGL};
use sdl2::mouse;
use sdl2::render::{RenderDriverIndex, ACCELERATED, Renderer};
use sdl2::pixels::PixelFormatEnum;
use sdl2::event::Event;
use sdl2_image::LoadSurface;

const WINDOW_WIDTH  :i32 = 1 * 320;
const WINDOW_HEIGHT :i32 = 1 * 240;

const W :usize = 320;
const H :usize = 240;

pub fn main() {
    let sdl_context = sdl2::init(sdl2::INIT_VIDEO).unwrap();

    let res_path = Path::new("/Users/jaburns/dev/rustycast/res");

    let window = match Window::new("RustyCast", WindowPos::PosCentered, WindowPos::PosCentered, WINDOW_WIDTH, WINDOW_HEIGHT, OPENGL) {
        Ok(window) => window,
        Err(err) => panic!("failed to create window: {}", err)
    };

    window.set_grab(true);
    mouse::set_relative_mouse_mode(true);

    let renderer = match Renderer::from_window(window, RenderDriverIndex::Auto, ACCELERATED) {
        Ok(renderer) => renderer,
        Err(err) => panic!("failed to create renderer: {}", err)
    };

    let mut sky = match LoadSurface::from_file(&res_path.join("sky.png")) {
        Ok(surface) => surface,
        Err(err) => panic!(format!("Failed to load png: {}", err))
    };

    let mut texture = renderer.create_texture_streaming(PixelFormatEnum::RGB24, (W as i32, H as i32)).unwrap();
    let mut drawer = renderer.drawer();
    let mut event_pump = sdl_context.event_pump();

    let mut inputs = input::InputState::new();
    let mut game = game::Game {
        sector: world::SectorIndex(0),
        pos: math::V2_ORIGIN,
        face_angle: 0.0,
        look_angle: 0.0,
        world: &world::temp(),
        show_map: false,
        t: 0.0
    };

    'main : loop {
        let last_time = PreciseTime::now();

        for event in event_pump.poll_iter() {
            inputs.check_event(&event);
            match event {
                Event::Quit {..} => { break 'main; },
                _ => {}
            }
        }

        if inputs.has_key(input::Key::Quit) {
            break 'main;
        }

        game.step(&inputs);
        texture.with_lock(None, |buffer, _| {
            game.render(&mut sky, buffer, W, H);
        }).unwrap();

        drawer.clear();
        drawer.copy(&texture, None, None);
        drawer.present();

        let delta_time = last_time.to(PreciseTime::now()).num_milliseconds();
        thread::sleep(Duration::milliseconds(15 - delta_time));
    }
}

<|endoftext|>


<commit_before>use std::fmt;
use std::str::FromStr;

#[derive(Debug, PartialEq)]
enum Atom {
    Number(f64),
    Symbol(String)
}

#[derive(Debug, PartialEq)]
enum ParseAtomError {
    IncorrectSymbolName
}

impl fmt::Display for Atom {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            Atom::Number(n) => {
                write!(f, "{}", n)
            },
            Atom::Symbol(ref name) => {
                write!(f, "'{}", name)
            }
        }
    }
}

impl FromStr for Atom {
    type Err = ParseAtomError;

    fn from_str(s: &str) -> Result<Atom, ParseAtomError> {
        match s.parse::<f64>() {
            Ok(f) => {
                Ok(Atom::Number(f))
            },
            Err(..) => {
                match s.chars().next() {
                    Some(c) if !c.is_numeric() => {
                        Ok(Atom::Symbol(s.to_string()))
                    },
                    _ => {
                        Err(ParseAtomError::IncorrectSymbolName)
                    }
                }
            }
        }
    }
}

fn tokenize(s: &str) -> Vec<String> {
    s.replace("("," ( ")
        .replace(")", " ) ")
        .replace("\n", " ")
        .split(' ')
        .filter(|s| { !s.is_empty() })
        .map(|s| { s.to_string() })
        .collect()
}

#[cfg_attr(test, allow(dead_code))]
fn main() {
}

#[cfg(test)]
mod tests {
    use super::Atom::{self, Number, Symbol};
    use super::ParseAtomError::IncorrectSymbolName;
    use super::tokenize;

    #[test]
    fn test_parse_integer() {
        assert_eq!(Number(64f64), "64".parse::<Atom>().ok().unwrap())
    }

    #[test]
    fn test_parse_float() {
        assert_eq!(Number(64.5), "64.5".parse::<Atom>().ok().unwrap())
    }

    #[test]
    fn test_parse_symbol() {
        assert_eq!(Symbol("name".to_string()), "name".parse::<Atom>().ok().unwrap())
    }

    #[test]
    fn test_parse_incorrect_symbol_starting_with_digit() {
        assert_eq!(IncorrectSymbolName, "6name".parse::<Atom>().err().unwrap())
    }

    #[test]
    fn test_tokenize_dense_expression() {
        let expected_result = ["(", "def", "a", "1", ")"]
                                .iter()
                                .map(|s| { s.to_string() })
                                .collect();
        assert_eq!(expected_result, tokenize("(def a 1)"))
    }

    #[test]
    fn test_tokenize_sparse_expression() {
        let expected_result = ["(", "def", "a", "1", ")"]
                                .iter()
                                .map(|s| { s.to_string() })
                                .collect();
        assert_eq!(expected_result, tokenize(" ( \n def a\n1)   \n"))
    }
}
<commit_msg>Implement read function of lisp interpreter<commit_after>use std::fmt;
use std::str::FromStr;

#[derive(Debug, PartialEq)]
enum Atom {
    Number(f64),
    Symbol(String)
}

#[derive(Debug, PartialEq)]
enum Sexp {
    Atom(Atom),
    List(Vec<Sexp>)
}

#[derive(Debug, PartialEq)]
enum ParseAtomError {
    IncorrectSymbolName
}

impl fmt::Display for Atom {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            Atom::Number(n) => {
                write!(f, "{}", n)
            },
            Atom::Symbol(ref name) => {
                write!(f, "'{}", name)
            }
        }
    }
}

impl FromStr for Atom {
    type Err = ParseAtomError;

    fn from_str(s: &str) -> Result<Atom, ParseAtomError> {
        match s.parse::<f64>() {
            Ok(f) => {
                Ok(Atom::Number(f))
            },
            Err(..) => {
                match s.chars().next() {
                    Some(c) if !c.is_numeric() => {
                        Ok(Atom::Symbol(s.to_string()))
                    },
                    _ => {
                        Err(ParseAtomError::IncorrectSymbolName)
                    }
                }
            }
        }
    }
}

fn tokenize(s: &str) -> Vec<String> {
    s.replace("("," ( ")
        .replace(")", " ) ")
        .replace("\n", " ")
        .split(' ')
        .filter(|s| { !s.is_empty() })
        .map(|s| { s.to_string() })
        .collect()
}

#[derive(Debug, PartialEq)]
enum ReadState {
    StartRead,
    OpenList,
    AtomRead(Atom),
    CloseList,
    EndRead,
}

#[derive(Debug, PartialEq)]
enum ReadError {
}

fn read(tokens: Vec<String>) -> Result<Vec<Sexp>, ReadError> {
    let mut state = ReadState::StartRead;
    let mut iter = tokens.iter();
    let mut result = vec![];
    loop {
        match state {
            ReadState::StartRead => {
                match iter.next() {
                    Some(s) if *s == "(" => {
                        state = ReadState::OpenList;
                        result.push(Sexp::List(vec![]));
                    },
                    None => {
                        state = ReadState::EndRead;
                    },
                    _ => {
                        state = ReadState::EndRead;
                    }
                }
            },
            ReadState::OpenList => {
                match iter.next() {
                    Some(s) if *s == ")" => {
                        state = ReadState::CloseList;
                    },
                    Some(s) => {
                        state = ReadState::AtomRead(s.parse::<Atom>().ok().unwrap());
                    },
                    _ => {
                        state = ReadState::EndRead;
                    }
                }
            },
            ReadState::CloseList => {
                match iter.next() {
                    Some(s) if *s == "(" => {
                        state = ReadState::OpenList;
                        result.push(Sexp::List(vec![]));
                    },
                    _ => {
                        state = ReadState::EndRead;
                    }
                }
            },
            ReadState::AtomRead(atom) => {
                if let Some(Sexp::List(mut current_list)) = result.pop() {
                    current_list.push(Sexp::Atom(atom));
                    result.push(Sexp::List(current_list));
                }
                match iter.next() {
                    Some(s) if *s == ")" => {
                        state = ReadState::CloseList;
                    },
                    Some(s) => {
                        state = ReadState::AtomRead(s.parse::<Atom>().ok().unwrap());
                    },
                    _ => {
                        state = ReadState::EndRead;
                    }
                }
            },
            ReadState::EndRead => {
                return Ok(result)
            }
        }
    }
}

#[cfg_attr(test, allow(dead_code))]
fn main() {
}

#[cfg(test)]
mod tests {
    use super::Atom::{self, Number, Symbol};
    use super::Sexp;
    use super::ParseAtomError::IncorrectSymbolName;
    use super::{tokenize, read};

    #[test]
    fn test_parse_integer() {
        assert_eq!(Number(64f64), "64".parse::<Atom>().ok().unwrap())
    }

    #[test]
    fn test_parse_float() {
        assert_eq!(Number(64.5), "64.5".parse::<Atom>().ok().unwrap())
    }

    #[test]
    fn test_parse_symbol() {
        assert_eq!(Symbol("name".to_string()), "name".parse::<Atom>().ok().unwrap())
    }

    #[test]
    fn test_parse_incorrect_symbol_starting_with_digit() {
        assert_eq!(IncorrectSymbolName, "6name".parse::<Atom>().err().unwrap())
    }

    #[test]
    fn test_tokenize_dense_expression() {
        let expected_result = ["(", "def", "a", "1", ")"]
                                .iter()
                                .map(|s| { s.to_string() })
                                .collect();
        assert_eq!(expected_result, tokenize("(def a 1)"))
    }

    #[test]
    fn test_tokenize_sparse_expression() {
        let expected_result = ["(", "def", "a", "1", ")"]
                                .iter()
                                .map(|s| { s.to_string() })
                                .collect();
        assert_eq!(expected_result, tokenize(" ( \n def a\n1)   \n"))
    }

    #[test]
    fn test_read_empty() {
        let expected_result = vec![];
        assert_eq!(expected_result, read(tokenize("")).ok().unwrap())
    }

    #[test]
    fn test_read_single_expression() {
        let expected_result =  vec![Sexp::List(vec![Sexp::Atom(Symbol("def".to_string())),
                                                    Sexp::Atom(Symbol("a".to_string())),
                                                    Sexp::Atom(Number(1f64))])];
        assert_eq!(expected_result, read(tokenize("(def a 1)")).ok().unwrap())
    }

    #[test]
    fn test_read_multiple_expression() {
        let expected_result =  vec![Sexp::List(vec![Sexp::Atom(Symbol("def".to_string())),
                                                    Sexp::Atom(Symbol("a".to_string())),
                                                    Sexp::Atom(Number(1f64))]),
                                    Sexp::List(vec![Sexp::Atom(Symbol("def".to_string())),
                                                    Sexp::Atom(Symbol("b".to_string())),
                                                    Sexp::Atom(Number(2f64))]),
                                    Sexp::List(vec![Sexp::Atom(Symbol("+".to_string())),
                                                    Sexp::Atom(Symbol("a".to_string())),
                                                    Sexp::Atom(Symbol("b".to_string()))])];
        assert_eq!(expected_result, read(tokenize("(def a 1)(def b 2)(+ a b)")).ok().unwrap())
    }
}
<|endoftext|>