<commit_before>#![feature(ip_addr, std_misc, convert)]

use std::net::UdpSocket;
use std::net::{SocketAddr, IpAddr, Ipv4Addr};
use std::str::FromStr;
use std::str;
use std::string::String;
use std::convert::AsRef;
use std::thread;
use std::ptr;
use std::mem::{zeroed, transmute};

pub mod ops;
use ops::{in_op, out_op};

#[macro_use]
#[cfg(debug_assertions)]
pub mod testing;
#[cfg(debug_assertions)]
pub mod tests;

#[derive(Clone, Copy)]
struct Player {
  id: i16,
  last_received_packet_at: u64, // In nanoseconds
}

#[derive(Clone, Copy)]
struct Game {
  id: u8,
  name: &'static str,
  players: [Player; 10]
}

impl Game {
  fn new(id: u8, name: &'static str) -> Game {
    Game {
      id: id,
      name: name,
      players: unsafe { zeroed() }
    }
  }
}

const MAX_GAMES: usize = 20;
const MAX_GAME_NAME_SIZE: usize = 15;

fn error_response(socket: &UdpSocket, addr: SocketAddr, message: &str) {
  unsafe {
    let message_buf: &[u8] = transmute(message);
    let buf_len = message_buf.len() + 1;

    // TODO perhaps instead of allocating here, keep ahold of an out_buf or something
    // and pass it here.

    let mut buf = Vec::with_capacity(buf_len);
    buf.set_len(buf_len);
    let mut buf = buf.into_boxed_slice();

    buf[0] = out_op::ERROR;
    ptr::copy_nonoverlapping(&message_buf[0], &mut buf[1], message_buf.len());

    socket.send_to(&buf, addr).unwrap();
  }
}

fn start_server(ip: &str) {
  let ip_addr = Ipv4Addr::from_str(ip).unwrap();
  let addr = SocketAddr::new(IpAddr::V4(ip_addr), 34561);

  let socket = match UdpSocket::bind(addr) {
    Ok(s) => s,
    Err(e) => panic!("Failed to bind socket! {}", e)
  };

  println!("I survived! (now listening??)");

  let mut buf = [0u8; 256];
  let mut game_names = [[0u8; MAX_GAME_NAME_SIZE]; MAX_GAMES];
  let mut games: [Option<Game>; MAX_GAMES] = [None; MAX_GAMES];
  'receiving: loop {
    match socket.recv_from(&mut buf) {
      Err(e) => println!("Failed to receive :(! error: {}", e),

      Ok((len, src_addr)) => {

        // ======================= BASIC PING =================
        if len == 1 && buf[0] == 1 {
          println!("ping!!!");
          let response = [1u8];
          socket.send_to(&response, src_addr).unwrap();
          continue;
        }

        // ==================== OTHER OPERATIONS ==============
        match buf[0] {
          in_op::NEW_GAME => {
            let name_len = buf[1] as usize;
            let name = match str::from_utf8(&buf[2..name_len+2]) {
              Ok(s) => s,
              Err(e) => {
                error_response(&socket, src_addr, "Invalid game name");
                continue 'receiving;
              }
            };
            println!("Making a game with a name of {}", name);

            let mut index: isize = -1;
            for i in 0..MAX_GAMES {
              match games[i] {
                Some(game) => {
                  println!("CHECKING IF \"{}\" == \"{}\"", game.name, name);
                  if game.name == name {
                    let msg = format!("Game name {} already taken", name);
                    error_response(&socket, src_addr, msg.as_str());
                    continue 'receiving;
                  }
                }

                None => {
                  index = i as isize;
                  break;
                }
              }
            }

            if index < 0 {
              error_response(&socket, src_addr, "Server is too full to accept another game");
              continue 'receiving;
            }
            let index = index as usize;

            unsafe {
              ptr::copy_nonoverlapping(&buf[2], &mut game_names[index][0], name_len);
              games[index] = Some(
                Game::new(
                  index as u8,
                  transmute::<_, &'static str>(&game_names[index][0..name_len]))
              );
            }

            // TODO accept a packet id so that the client can know what responses mean.
            // (maybe)
            let response = [out_op::GAME_CREATED];
            socket.send_to(&response, src_addr).unwrap();

            println!("MADE GAME");
          }

          _ => {
            // TODO send back an UNKNOWN_OP op
            println!("Unknown opcode {} in a {}-byte packet from {}", buf[0], len, src_addr);
          }
        }
      }
    }
  }
}

#[allow(dead_code)]
fn main() {
  let mut stdin = std::io::stdin();

  println!("Enter ip address!");
  let mut ipstr = String::new();
  stdin.read_line(&mut ipstr).unwrap();

  ipstr.pop();
  start_server(ipstr.as_ref());
}

#[test]
fn a0_start_test_server() {
  thread::spawn(move || {
    start_server("127.0.0.1");
  });
}

<commit_msg>now we got an out_buf for all responses<commit_after>#![feature(ip_addr, std_misc, convert)]

use std::net::UdpSocket;
use std::net::{SocketAddr, IpAddr, Ipv4Addr};
use std::str::FromStr;
use std::str;
use std::string::String;
use std::convert::AsRef;
use std::thread;
use std::ptr;
use std::mem::{zeroed, transmute};

pub mod ops;
use ops::{in_op, out_op};

#[macro_use]
#[cfg(debug_assertions)]
pub mod testing;
#[cfg(debug_assertions)]
pub mod tests;

#[derive(Clone, Copy)]
struct Player {
  id: i16,
  last_received_packet_at: u64, // In nanoseconds
}

#[derive(Clone, Copy)]
struct Game {
  id: u8,
  name: &'static str,
  players: [Player; 10]
}

impl Game {
  fn new(id: u8, name: &'static str) -> Game {
    Game {
      id: id,
      name: name,
      players: unsafe { zeroed() }
    }
  }
}

const MAX_GAMES: usize = 20;
const MAX_GAME_NAME_SIZE: usize = 15;

fn error_response(socket: &UdpSocket, addr: SocketAddr, message: &str, buf: &mut [u8]) {
  unsafe {
    let message_buf: &[u8] = transmute(message);
    let buf_len = message_buf.len() + 1;

    buf[0] = out_op::ERROR;
    ptr::copy_nonoverlapping(&message_buf[0], &mut buf[1], message_buf.len());

    socket.send_to(&buf[0..buf_len], addr).unwrap();
  }
}

fn start_server(ip: &str) {
  let ip_addr = Ipv4Addr::from_str(ip).unwrap();
  let addr = SocketAddr::new(IpAddr::V4(ip_addr), 34561);

  let socket = match UdpSocket::bind(addr) {
    Ok(s) => s,
    Err(e) => panic!("Failed to bind socket! {}", e)
  };

  println!("I survived! (now listening??)");

  let mut in_buf = [0u8; 1024];
  let mut out_buf = [0u8; 1024];

  let mut game_names = [[0u8; MAX_GAME_NAME_SIZE]; MAX_GAMES];
  let mut games: [Option<Game>; MAX_GAMES] = [None; MAX_GAMES];

  'receiving: loop {
    match socket.recv_from(&mut in_buf) {
      Err(e) => println!("Failed to receive :(! error: {}", e),

      Ok((len, src_addr)) => {

        // ======================= BASIC PING =================
        if len == 1 && in_buf[0] == 1 {
          println!("ping!!!");
          let response = [1u8];
          socket.send_to(&response, src_addr).unwrap();
          continue;
        }

        // ==================== OTHER OPERATIONS ==============
        match in_buf[0] {
          in_op::NEW_GAME => {
            let name_len = in_buf[1] as usize;
            let name = match str::from_utf8(&in_buf[2..name_len+2]) {
              Ok(s) => s,
              Err(_) => {
                error_response(&socket, src_addr, "Invalid game name", &mut out_buf);
                continue 'receiving;
              }
            };
            println!("Making a game with a name of {}", name);

            let mut index: isize = -1;
            for i in 0..MAX_GAMES {
              match games[i] {
                Some(game) => {
                  println!("CHECKING IF \"{}\" == \"{}\"", game.name, name);
                  if game.name == name {
                    let msg = format!("Game name {} already taken", name);
                    error_response(&socket, src_addr, msg.as_str(), &mut out_buf);
                    continue 'receiving;
                  }
                }

                None => {
                  index = i as isize;
                  break;
                }
              }
            }

            if index < 0 {
              error_response(
                  &socket, src_addr,
                  "Server is too full to accept another game",
                  &mut out_buf
              );
              continue 'receiving;
            }
            let index = index as usize;

            unsafe {
              ptr::copy_nonoverlapping(&in_buf[2], &mut game_names[index][0], name_len);
              games[index] = Some(
                Game::new(
                  index as u8,
                  transmute::<_, &'static str>(&game_names[index][0..name_len]))
              );
            }

            // TODO accept a packet id so that the client can know what responses mean.
            // (maybe)
            let response = [out_op::GAME_CREATED];
            socket.send_to(&response, src_addr).unwrap();

            println!("MADE GAME");
          }

          _ => {
            // TODO send back an UNKNOWN_OP op
            println!("Unknown opcode {} in a {}-byte packet from {}", in_buf[0], len, src_addr);
          }
        }
      }
    }
  }
}

#[allow(dead_code)]
fn main() {
  let mut stdin = std::io::stdin();

  println!("Enter ip address!");
  let mut ipstr = String::new();
  stdin.read_line(&mut ipstr).unwrap();

  ipstr.pop();
  start_server(ipstr.as_ref());
}

#[test]
fn a0_start_test_server() {
  thread::spawn(move || {
    start_server("127.0.0.1");
  });
}

<|endoftext|>


<commit_before>type FreeVariable = u64;
type VariableName<'a> = &'a str;

/// AST node expression
enum Expression<'a> {
    Number(f64),
    Boolean(bool),

    /// simple variable
    Variable(&'a VariableName<'a>),

    /// fn (x, y, z) -> expr
    Lambda(&'a [VariableName<'a>], &'a Expression<'a>),

    /// let x = expr in expr
    Let(VariableName<'a>, &'a Expression<'a>, &'a Expression<'a>),

    /// some_fn (x, y, z)
    FnCall(&'a Expression<'a>, &'a[Expression<'a>])
}

enum Type<'a> {
    Number,
    Boolean,
    Unbound(FreeVariable),
    Lambda(&'a [Type<'a>], &'a Type<'a>),

}

type TaggedType<'a> = (Expression<'a>, Type<'a>);

fn main() {
    println!("Hello, world!");
}
<commit_msg>start working on `w`<commit_after>use std::fmt::Display;
use std::collections::HashMap;

type FreeVariable = u64;
type VariableName<'a> = &'a str;

/// AST node expression
#[derive(Clone, Debug)]
enum Expression<'a> {
    Number(f64),
    Boolean(bool),

    /// simple variable
    Variable(VariableName<'a>),

    /// fn (x, y, z) -> expr
    Lambda(&'a [VariableName<'a>], &'a Expression<'a>),

    /// let x = expr in expr
    Let(VariableName<'a>, &'a Expression<'a>, &'a Expression<'a>),

    /// some_fn (x, y, z)
    FnCall(&'a Expression<'a>, &'a [ &'a Expression<'a>])
}

#[derive(Debug)]
enum Type<'a> {
    Number,
    Boolean,
    Unbound(FreeVariable),
    Lambda(&'a [Type<'a>], &'a Type<'a>),

}

type TaggedType<'a> = (Expression<'a>, Type<'a>);

type TypeEnv<'a> = HashMap<VariableName<'a>, Type<'a>>;

struct VariableGen { current: FreeVariable }
impl VariableGen {
    fn next(&mut self) -> FreeVariable { self.current += 1; self.current }
}

/// Run algorithm W on expr with given type env, yielding the resolved
/// type of the expression, or the type error encountered.
fn w<'a>(expr: &Expression<'a>, env: &mut TypeEnv<'a>, inst: &mut VariableGen) -> Type<'a> {
    match *expr {
        Expression::Number(_) => Type::Number,
        Expression::Boolean(_) => Type::Boolean,
        Expression::Variable(name) => Type::Unbound(inst.next()),
        _ => Type::Unbound(inst.next()),
    }
}

fn main() {
    // FIXME: this is so gnarly.
    // let incr = \x -> 123.0 in incr (123.0) :: Number
    let num = Expression::Number(123.0);
    let incr = Expression::Variable("incr");
    let args = ["x"];
    let args_ = [&num];
    let fn_call = Expression::FnCall(&incr, &args_);
    let lambda = Expression::Lambda(&args, &num);

    let expr = Expression::Let(
        "incr", &lambda,
        &fn_call);

    let mut inst = VariableGen {current: 0};
    let mut env = HashMap::new();

    println!("this is the thing: {:?}\n", w(&expr, &mut env, &mut inst))
}
<|endoftext|>


<commit_before>//!                             The Unlicense
//! This is free and unencumbered software released into the public domain.
//!
//! Anyone is free to copy, modify, publish, use, compile, sell, or
//! distribute this software, either in source code form or as a compiled
//! binary, for any purpose, commercial or non-commercial, and by any
//! means.
//!
//! In jurisdictions that recognize copyright laws, the author or authors
//! of this software dedicate any and all copyright interest in the
//! software to the public domain. We make this dedication for the benefit
//! of the public at large and to the detriment of our heirs and
//! successors. We intend this dedication to be an overt act of
//! relinquishment in perpetuity of all present and future rights to this
//! software under copyright law.
//!
//! THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
//! EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
//! MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
//! IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
//! OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
//! ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
//! OTHER DEALINGS IN THE SOFTWARE.
//!
//! For more information, please refer to <http://unlicense.org>
//!

extern crate iron;
extern crate router;
#[macro_use]
extern crate hyper;
#[macro_use]
extern crate lazy_static;
extern crate regex;
extern crate hyper_rustls;
extern crate urlencoded;
extern crate persistent;
extern crate lru_cache;
extern crate time;
extern crate crypto_hash;

use std::vec::Vec;
use std::ops::Deref;
use std::sync::{Arc, Mutex};
use std::io::Read;
use std::collections::HashMap;
use std::option::Option;
use std::result::Result;

use hyper::client::{Client as HyperClient, Response as HyperResponse};
use hyper::net::HttpsConnector as HyperHttpsConnector;
use hyper::status::StatusCode as HyperStatusCode;
use hyper::header as hyper_header;
use hyper_rustls::TlsClient as HyperRustlsClient;

use iron::{IronResult, IronError, Iron};
use iron::{Request as IronRequest, Response as IronResponse, Chain as IronChain,
           Plugin as IronPlugin};
use iron::{AfterMiddleware, Handler};
use iron::typemap::Key;

use router::{Router, NoRoute};
use urlencoded::{UrlEncodedQuery, UrlDecodingError};
use persistent::Write;

use regex::Regex;
use lru_cache::LruCache;
use crypto_hash::{Algorithm as HashAlgorithm, hex_digest};

header! { (XPoweredBy, "X-Powered-By") => [String] }

fn verify_md5(md5: &str) -> bool {
    lazy_static! {
        static ref MD5_RE: Regex = Regex::new("^[0-9a-f]{32}$").unwrap();
    }

    MD5_RE.is_match(md5)
}

#[derive(Clone, Eq, PartialEq, Hash, Debug)]
struct AvatarCacheKey {
    email_md5: String,
    size: Option<i64>,
    default: Option<String>,
}

impl AvatarCacheKey {
    fn from_request(req: &mut IronRequest) -> Result<AvatarCacheKey, IronResponse> {
        let email_md5 = req.extensions
            .get::<Router>()
            .unwrap()
            .find("email_md5")
            .unwrap_or("")
            .to_lowercase();

        if !verify_md5(email_md5.as_str()) {
            return Err(IronResponse::with((HyperStatusCode::NotFound)));
        }

        let maybe_query = req.get::<UrlEncodedQuery>()
            .map(|val| Some(val))
            .unwrap_or_else(|err: UrlDecodingError| {
                match err {
                    UrlDecodingError::EmptyQuery => Some(HashMap::new()),
                    _ => None,
                }
            });

        let query = match maybe_query {
            Some(m) => m,
            None => return Err(IronResponse::with((HyperStatusCode::BadRequest))),
        };

        let size_str = query.get("s")
            .map(|s_vec| s_vec.clone())
            .map(|s_vec| { if s_vec.len() > 0 { Some(s_vec) } else { None } })
            .unwrap_or(None)
            .map(|s_vec| s_vec[0].clone());

        let default = query.get("d")
            .map(|d_vec| d_vec.clone())
            .map(|d_vec| { if d_vec.len() > 0 { Some(d_vec) } else { None } })
            .unwrap_or(None)
            .map(|d_vec| d_vec[0].clone());

        let size: Option<i64> = match size_str {
            Some(m) => {
                match m.parse::<i64>() {
                    Ok(m) => Some(m),
                    Err(_) => return Err(IronResponse::with((HyperStatusCode::BadRequest))),
                }
            }
            None => None,
        };

        Ok(AvatarCacheKey {
            email_md5: email_md5,
            size: size,
            default: default,
        })
    }
}

#[derive(Clone)]
struct AvatarCacheData {
    pub etag: hyper_header::EntityTag,
    pub last_modified: hyper_header::HttpDate,
    pub content_type: hyper_header::ContentType,
    pub content_disposition: Option<hyper_header::ContentDisposition>,
    pub cache_control: hyper_header::CacheControl,
    pub expires: hyper_header::HttpDate,
}

impl AvatarCacheData {
    fn from_response(res: &HyperResponse, res_body: &[u8]) -> AvatarResult<AvatarCacheData> {
        let etag = hyper_header::EntityTag::strong(hex_digest(HashAlgorithm::SHA256,
                                                              res_body.to_vec()));
        let last_modified = res.headers
            .get::<hyper_header::LastModified>()
            .map(|m| m.deref().clone())
            .unwrap_or_else(|| hyper_header::HttpDate(time::now()));

        let content_type = try!(res.headers
            .get::<hyper_header::ContentType>()
            .map(|m| m.clone())
            .ok_or_else(|| IronResponse::with((HyperStatusCode::BadGateway))));

        let content_disposition =
            res.headers.get::<hyper_header::ContentDisposition>().map(|m| m.clone());
        let cache_control =
            hyper_header::CacheControl(vec![hyper_header::CacheDirective::MaxAge(600 as u32)]);
        let expires = hyper_header::HttpDate(time::now() + time::Duration::minutes(10));

        Ok(AvatarCacheData {
            etag: etag,
            last_modified: last_modified,
            content_type: content_type,
            content_disposition: content_disposition,
            cache_control: cache_control,
            expires: expires,
        })
    }

    fn set_cache_headers(&self, mut res: &mut IronResponse) {
        res.headers.set(hyper_header::ETag(self.etag.clone()));
        res.headers.set(hyper_header::LastModified(self.last_modified.clone()));
        if let Some(m) = self.content_disposition.clone() {
            res.headers.set(m);
        }
        res.headers.set(self.cache_control.clone());
        res.headers.set(hyper_header::Expires(self.expires.clone()));

    }
}

#[derive(Clone)]
struct AvatarCache;

impl Key for AvatarCache {
    type Value = LruCache<AvatarCacheKey, AvatarCacheData>;
}

struct Avatar {
    pub buf: Vec<u8>,
    pub ac_key: AvatarCacheKey,
    pub ac_data: AvatarCacheData,
}

type AvatarResult<T> = Result<T, IronResponse>;

impl Avatar {
    fn make_origin_url(ac_key: &AvatarCacheKey) -> String {
        let mut origin_url = format!("https://secure.gravatar.com/avatar/{}", ac_key.email_md5);

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

        if let Some(ref m) = ac_key.size {
            params.push(format!("s={}", m));
        }

        if let Some(ref m) = ac_key.default {
            params.push(format!("d={}", m));
        }

        if params.len() > 0 {
            origin_url.push_str("?");
            origin_url.push_str(params.join("&").as_str());
        }

        origin_url
    }

    pub fn from_response(res: &mut HyperResponse, ac_key: AvatarCacheKey) -> AvatarResult<Avatar> {
        if res.status != hyper::Ok {
            return Err(IronResponse::with((HyperStatusCode::BadGateway)));
        }

        let mut avatar_body: Vec<u8> = Vec::new();

        try!(res.read_to_end(&mut avatar_body)
            .map_err(|_| IronResponse::with((HyperStatusCode::BadGateway))));

        let ac_data = try!(AvatarCacheData::from_response(res, avatar_body.as_slice()));

        Ok(Avatar {
            buf: avatar_body,
            ac_key: ac_key,
            ac_data: ac_data,
        })

    }

    pub fn fetch(ac_key: AvatarCacheKey) -> AvatarResult<Avatar> {
        lazy_static! {
            static ref HYPER_CLIENT: HyperClient = HyperClient::with_connector(HyperHttpsConnector::new(HyperRustlsClient::new()));
        }

        let origin_url = Avatar::make_origin_url(&ac_key);

        let mut origin_res = try!(HYPER_CLIENT.get(origin_url.as_str())
            .send()
            .map_err(|_| IronResponse::with((HyperStatusCode::BadGateway))));

        Avatar::from_response(&mut origin_res, ac_key)
    }
}

struct MainHandler;

impl MainHandler {
    fn read_from_avatar_cache(&self,
                              ac_key: &AvatarCacheKey,
                              avatar_cache_mutex: &Arc<Mutex<LruCache<AvatarCacheKey,
                                                                      AvatarCacheData>>>)
                              -> Option<AvatarCacheData> {
        let mut avatar_cache = avatar_cache_mutex.lock().unwrap();

        let ac_data = match avatar_cache.get_mut(ac_key).map(|m| m.clone()) {
            Some(m) => m,
            None => return None,
        };

        if ac_data.expires > hyper_header::HttpDate(time::now()) {
            Some(ac_data)
        } else {
            avatar_cache.remove(ac_key);
            None
        }
    }

    fn response_with_200(&self, avatar: &Avatar) -> IronResult<IronResponse> {
        let mut res = IronResponse::with((avatar.ac_data.content_type.deref().clone(),
                                          HyperStatusCode::Ok,
                                          avatar.buf.clone()));

        avatar.ac_data.set_cache_headers(&mut res);
        Ok(res)

    }

    fn response_with_304(&self, ac_data: &AvatarCacheData) -> IronResult<IronResponse> {
        let mut res = IronResponse::with((ac_data.content_type.deref().clone(),
                                          HyperStatusCode::NotModified));

        ac_data.set_cache_headers(&mut res);

        Ok(res)
    }
}

impl Handler for MainHandler {
    fn handle(&self, mut req: &mut IronRequest) -> IronResult<IronResponse> {
        let avatar_cache_mutex = req.get::<Write<AvatarCache>>().unwrap();

        let ac_key = match AvatarCacheKey::from_request(&mut req) {
            Ok(v) => v,
            Err(res) => return Ok(res),
        };

        let maybe_if_modified_since = req.headers.get::<hyper_header::IfModifiedSince>();
        let maybe_if_none_match = req.headers.get::<hyper_header::IfNoneMatch>();

        if let Some(if_none_match) = maybe_if_none_match {
            // Prefer ETag.
            if let Some(ac_data) = self.read_from_avatar_cache(&ac_key, &avatar_cache_mutex) {
                if let &hyper_header::IfNoneMatch::Items(ref items) = if_none_match {
                    if items.as_slice().contains(&ac_data.etag) {
                        return self.response_with_304(&ac_data);
                    }
                }
            }
        } else if let Some(if_modified_since) = maybe_if_modified_since {
            if let Some(ac_data) = self.read_from_avatar_cache(&ac_key, &avatar_cache_mutex) {
                if if_modified_since.deref() == &ac_data.last_modified {
                    return self.response_with_304(&ac_data);
                }
            }
        }

        let avatar = match Avatar::fetch(ac_key) {
            Ok(v) => v,
            Err(res) => return Ok(res),
        };

        let res_result = self.response_with_200(&avatar);
        {
            let mut avatar_cache = avatar_cache_mutex.lock().unwrap();

            avatar_cache.insert(avatar.ac_key, avatar.ac_data);
        }
        res_result
    }
}

struct CustomErrorMsg;

impl CustomErrorMsg {
    fn alter_response(&self, res: IronResponse) -> IronResponse {
        if res.status == Some(HyperStatusCode::NotFound) {
            IronResponse::with((HyperStatusCode::NotFound, "HTTP 404: Not Found."))

        } else if res.status == Some(HyperStatusCode::BadRequest) {
            IronResponse::with((HyperStatusCode::BadRequest, "HTTP 400: Bad Request."))

        } else if res.status == Some(HyperStatusCode::BadGateway) {
            IronResponse::with((HyperStatusCode::BadGateway, "HTTP 502: Bad Gateway."))

        } else {
            res

        }
    }
}

impl AfterMiddleware for CustomErrorMsg {
    fn after(&self, _: &mut IronRequest, res: IronResponse) -> IronResult<IronResponse> {
        Ok(self.alter_response(res))
    }

    fn catch(&self, _: &mut IronRequest, err: IronError) -> IronResult<IronResponse> {
        let res = err.response;
        let res = self.alter_response(res);

        if let Some(_) = err.error.downcast::<NoRoute>() {
            return Ok(res);
        }

        Err(IronError {
            error: err.error,
            response: res,
        })
    }
}

struct AddXPoweredBy;

impl AddXPoweredBy {
    fn add_header(&self, res: &mut IronResponse) {
        res.headers.set(XPoweredBy("Rust".to_owned()));
    }
}

impl AfterMiddleware for AddXPoweredBy {
    fn after(&self, _: &mut IronRequest, mut res: IronResponse) -> IronResult<IronResponse> {
        self.add_header(&mut res);

        Ok(res)
    }

    fn catch(&self, _: &mut IronRequest, err: IronError) -> IronResult<IronResponse> {
        let mut res = err.response;
        let inner_err = err.error;
        self.add_header(&mut res);

        Err(IronError {
            error: inner_err,
            response: res,
        })
    }
}

fn main() {
    let mut router = Router::new();

    router.get("/avatar/:email_md5", MainHandler, "email_md5");

    let mut chain = IronChain::new(router);
    chain.link_after(CustomErrorMsg);
    chain.link_after(AddXPoweredBy);

    chain.link(Write::<AvatarCache>::both(LruCache::new(102400)));

    Iron::new(chain).http("localhost:3000").unwrap();
}
<commit_msg>Small Optimization in Logic.<commit_after>//!                             The Unlicense
//! This is free and unencumbered software released into the public domain.
//!
//! Anyone is free to copy, modify, publish, use, compile, sell, or
//! distribute this software, either in source code form or as a compiled
//! binary, for any purpose, commercial or non-commercial, and by any
//! means.
//!
//! In jurisdictions that recognize copyright laws, the author or authors
//! of this software dedicate any and all copyright interest in the
//! software to the public domain. We make this dedication for the benefit
//! of the public at large and to the detriment of our heirs and
//! successors. We intend this dedication to be an overt act of
//! relinquishment in perpetuity of all present and future rights to this
//! software under copyright law.
//!
//! THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
//! EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
//! MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
//! IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
//! OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
//! ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
//! OTHER DEALINGS IN THE SOFTWARE.
//!
//! For more information, please refer to <http://unlicense.org>
//!

extern crate iron;
extern crate router;
#[macro_use]
extern crate hyper;
#[macro_use]
extern crate lazy_static;
extern crate regex;
extern crate hyper_rustls;
extern crate urlencoded;
extern crate persistent;
extern crate lru_cache;
extern crate time;
extern crate crypto_hash;

use std::vec::Vec;
use std::ops::Deref;
use std::sync::{Arc, Mutex};
use std::io::Read;
use std::collections::HashMap;
use std::option::Option;
use std::result::Result;

use hyper::client::{Client as HyperClient, Response as HyperResponse};
use hyper::net::HttpsConnector as HyperHttpsConnector;
use hyper::status::StatusCode as HyperStatusCode;
use hyper::header as hyper_header;
use hyper_rustls::TlsClient as HyperRustlsClient;

use iron::{IronResult, IronError, Iron};
use iron::{Request as IronRequest, Response as IronResponse, Chain as IronChain,
           Plugin as IronPlugin, AfterMiddleware as IronAfterMiddleware, Handler as IronHandler};
use iron::typemap::Key as IronTypeMapKey;

use router::{Router, NoRoute};
use urlencoded::{UrlEncodedQuery, UrlDecodingError};
use persistent::Write;

use regex::Regex;
use lru_cache::LruCache;
use crypto_hash::{Algorithm as HashAlgorithm, hex_digest};

header! { (XPoweredBy, "X-Powered-By") => [String] }

fn verify_md5(md5: &str) -> bool {
    lazy_static! {
        static ref MD5_RE: Regex = Regex::new("^[0-9a-f]{32}$").unwrap();
    }

    MD5_RE.is_match(md5)
}

#[derive(Clone, Eq, PartialEq, Hash, Debug)]
struct AvatarCacheKey {
    email_md5: String,
    size: Option<i64>,
    default: Option<String>,
}

impl AvatarCacheKey {
    fn from_request(req: &mut IronRequest) -> AvatarResult<AvatarCacheKey> {
        let email_md5 = req.extensions
            .get::<Router>()
            .unwrap()
            .find("email_md5")
            .unwrap_or("")
            .to_lowercase();

        if !verify_md5(email_md5.as_str()) {
            return Err(IronResponse::with((HyperStatusCode::NotFound)));
        }

        let maybe_query = req.get::<UrlEncodedQuery>()
            .map(|val| Some(val))
            .unwrap_or_else(|err: UrlDecodingError| {
                match err {
                    UrlDecodingError::EmptyQuery => Some(HashMap::new()),
                    _ => None,
                }
            });

        let query = match maybe_query {
            Some(m) => m,
            None => return Err(IronResponse::with((HyperStatusCode::BadRequest))),
        };

        let size_str = query.get("s")
            .map(|s_vec| s_vec.clone())
            .map(|s_vec| { if s_vec.len() > 0 { Some(s_vec) } else { None } })
            .unwrap_or(None)
            .map(|s_vec| s_vec[0].clone());

        let default = query.get("d")
            .map(|d_vec| d_vec.clone())
            .map(|d_vec| { if d_vec.len() > 0 { Some(d_vec) } else { None } })
            .unwrap_or(None)
            .map(|d_vec| d_vec[0].clone());

        let size: Option<i64> = match size_str {
            Some(m) => {
                match m.parse::<i64>() {
                    Ok(m) => Some(m),
                    Err(_) => return Err(IronResponse::with((HyperStatusCode::BadRequest))),
                }
            }
            None => None,
        };

        Ok(AvatarCacheKey {
            email_md5: email_md5,
            size: size,
            default: default,
        })
    }
}

#[derive(Clone)]
struct AvatarCacheData {
    pub etag: hyper_header::EntityTag,
    pub last_modified: hyper_header::HttpDate,
    pub content_type: hyper_header::ContentType,
    pub content_disposition: Option<hyper_header::ContentDisposition>,
    pub cache_control: hyper_header::CacheControl,
    pub expires: hyper_header::HttpDate,
}

impl AvatarCacheData {
    fn from_response(res: &HyperResponse, res_body: &[u8]) -> AvatarResult<AvatarCacheData> {
        let etag = hyper_header::EntityTag::strong(hex_digest(HashAlgorithm::SHA256,
                                                              res_body.to_vec()));
        let last_modified = res.headers
            .get::<hyper_header::LastModified>()
            .map(|m| m.deref().clone())
            .unwrap_or_else(|| hyper_header::HttpDate(time::now()));

        let content_type = try!(res.headers
            .get::<hyper_header::ContentType>()
            .map(|m| m.clone())
            .ok_or_else(|| IronResponse::with((HyperStatusCode::BadGateway))));

        let content_disposition =
            res.headers.get::<hyper_header::ContentDisposition>().map(|m| m.clone());
        let cache_control =
            hyper_header::CacheControl(vec![hyper_header::CacheDirective::MaxAge(600 as u32)]);
        let expires = hyper_header::HttpDate(time::now() + time::Duration::minutes(10));

        Ok(AvatarCacheData {
            etag: etag,
            last_modified: last_modified,
            content_type: content_type,
            content_disposition: content_disposition,
            cache_control: cache_control,
            expires: expires,
        })
    }

    fn set_cache_headers(&self, mut res: &mut IronResponse) {
        res.headers.set(hyper_header::ETag(self.etag.clone()));
        res.headers.set(hyper_header::LastModified(self.last_modified.clone()));
        if let Some(m) = self.content_disposition.clone() {
            res.headers.set(m);
        }
        res.headers.set(self.cache_control.clone());
        res.headers.set(hyper_header::Expires(self.expires.clone()));

    }
}

#[derive(Clone)]
struct AvatarCache;

impl IronTypeMapKey for AvatarCache {
    type Value = LruCache<AvatarCacheKey, AvatarCacheData>;
}

struct Avatar {
    pub buf: Vec<u8>,
    pub ac_key: AvatarCacheKey,
    pub ac_data: AvatarCacheData,
}

type AvatarResult<T> = Result<T, IronResponse>;

impl Avatar {
    fn make_origin_url(ac_key: &AvatarCacheKey) -> String {
        let mut origin_url = format!("https://secure.gravatar.com/avatar/{}", ac_key.email_md5);

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

        if let Some(ref m) = ac_key.size {
            params.push(format!("s={}", m));
        }

        if let Some(ref m) = ac_key.default {
            params.push(format!("d={}", m));
        }

        if params.len() > 0 {
            origin_url.push_str("?");
            origin_url.push_str(params.join("&").as_str());
        }

        origin_url
    }

    pub fn from_response(res: &mut HyperResponse, ac_key: AvatarCacheKey) -> AvatarResult<Avatar> {
        if res.status != hyper::Ok {
            return Err(IronResponse::with((HyperStatusCode::BadGateway)));
        }

        let mut avatar_body: Vec<u8> = Vec::new();

        try!(res.read_to_end(&mut avatar_body)
            .map_err(|_| IronResponse::with((HyperStatusCode::BadGateway))));

        let ac_data = try!(AvatarCacheData::from_response(res, avatar_body.as_slice()));

        Ok(Avatar {
            buf: avatar_body,
            ac_key: ac_key,
            ac_data: ac_data,
        })

    }

    pub fn fetch(ac_key: AvatarCacheKey) -> AvatarResult<Avatar> {
        lazy_static! {
            static ref HYPER_CLIENT: HyperClient = HyperClient::with_connector(HyperHttpsConnector::new(HyperRustlsClient::new()));
        }

        let origin_url = Avatar::make_origin_url(&ac_key);

        let mut origin_res = try!(HYPER_CLIENT.get(origin_url.as_str())
            .send()
            .map_err(|_| IronResponse::with((HyperStatusCode::BadGateway))));

        Avatar::from_response(&mut origin_res, ac_key)
    }
}

struct MainHandler;

impl MainHandler {
    fn read_from_avatar_cache(&self,
                              ac_key: &AvatarCacheKey,
                              avatar_cache_mutex: &Arc<Mutex<LruCache<AvatarCacheKey,
                                                                      AvatarCacheData>>>)
                              -> Option<AvatarCacheData> {
        let mut avatar_cache = avatar_cache_mutex.lock().unwrap();

        let ac_data = match avatar_cache.get_mut(ac_key).map(|m| m.clone()) {
            Some(m) => m,
            None => return None,
        };

        if ac_data.expires > hyper_header::HttpDate(time::now()) {
            Some(ac_data)
        } else {
            avatar_cache.remove(ac_key);
            None
        }
    }

    fn response_with_200(&self, avatar: &Avatar) -> IronResult<IronResponse> {
        let mut res = IronResponse::with((avatar.ac_data.content_type.deref().clone(),
                                          HyperStatusCode::Ok,
                                          avatar.buf.clone()));

        avatar.ac_data.set_cache_headers(&mut res);
        Ok(res)

    }

    fn response_with_304(&self, ac_data: &AvatarCacheData) -> IronResult<IronResponse> {
        let mut res = IronResponse::with((ac_data.content_type.deref().clone(),
                                          HyperStatusCode::NotModified));

        ac_data.set_cache_headers(&mut res);

        Ok(res)
    }
}

impl IronHandler for MainHandler {
    fn handle(&self, mut req: &mut IronRequest) -> IronResult<IronResponse> {
        let avatar_cache_mutex = req.get::<Write<AvatarCache>>().unwrap();

        let ac_key = match AvatarCacheKey::from_request(&mut req) {
            Ok(v) => v,
            Err(res) => return Ok(res),
        };

        let maybe_if_modified_since = req.headers.get::<hyper_header::IfModifiedSince>();
        let maybe_if_none_match = req.headers.get::<hyper_header::IfNoneMatch>();

        if let Some(if_none_match) = maybe_if_none_match {
            // Prefer ETag.
            if let Some(ac_data) = self.read_from_avatar_cache(&ac_key, &avatar_cache_mutex) {
                if let &hyper_header::IfNoneMatch::Items(ref items) = if_none_match {
                    if items.as_slice().contains(&ac_data.etag) {
                        return self.response_with_304(&ac_data);
                    }
                }
            }
        } else if let Some(if_modified_since) = maybe_if_modified_since {
            if let Some(ac_data) = self.read_from_avatar_cache(&ac_key, &avatar_cache_mutex) {
                if if_modified_since.deref() == &ac_data.last_modified {
                    return self.response_with_304(&ac_data);
                }
            }
        }

        let avatar = match Avatar::fetch(ac_key) {
            Ok(v) => v,
            Err(res) => return Ok(res),
        };

        let res_result = self.response_with_200(&avatar);
        {
            let mut avatar_cache = avatar_cache_mutex.lock().unwrap();

            avatar_cache.insert(avatar.ac_key, avatar.ac_data);
        }
        res_result
    }
}

struct CustomErrorMsg;

impl CustomErrorMsg {
    fn alter_response(&self, res: IronResponse) -> IronResponse {
        if res.status == Some(HyperStatusCode::NotFound) {
            IronResponse::with((HyperStatusCode::NotFound, "HTTP 404: Not Found."))

        } else if res.status == Some(HyperStatusCode::BadRequest) {
            IronResponse::with((HyperStatusCode::BadRequest, "HTTP 400: Bad Request."))

        } else if res.status == Some(HyperStatusCode::BadGateway) {
            IronResponse::with((HyperStatusCode::BadGateway, "HTTP 502: Bad Gateway."))

        } else {
            res

        }
    }
}

impl IronAfterMiddleware for CustomErrorMsg {
    fn after(&self, _: &mut IronRequest, res: IronResponse) -> IronResult<IronResponse> {
        Ok(self.alter_response(res))
    }

    fn catch(&self, _: &mut IronRequest, err: IronError) -> IronResult<IronResponse> {
        let res = err.response;
        let res = self.alter_response(res);

        if let Some(_) = err.error.downcast::<NoRoute>() {
            return Ok(res);
        }

        Err(IronError {
            error: err.error,
            response: res,
        })
    }
}

struct AddXPoweredBy;

impl AddXPoweredBy {
    fn add_header(&self, res: &mut IronResponse) {
        res.headers.set(XPoweredBy("Rust".to_owned()));
    }
}

impl IronAfterMiddleware for AddXPoweredBy {
    fn after(&self, _: &mut IronRequest, mut res: IronResponse) -> IronResult<IronResponse> {
        self.add_header(&mut res);

        Ok(res)
    }

    fn catch(&self, _: &mut IronRequest, err: IronError) -> IronResult<IronResponse> {
        let mut res = err.response;
        let inner_err = err.error;
        self.add_header(&mut res);

        Err(IronError {
            error: inner_err,
            response: res,
        })
    }
}

fn main() {
    let mut router = Router::new();

    router.get("/avatar/:email_md5", MainHandler, "email_md5");

    let mut chain = IronChain::new(router);
    chain.link_after(CustomErrorMsg);
    chain.link_after(AddXPoweredBy);

    chain.link(Write::<AvatarCache>::both(LruCache::new(102400)));

    Iron::new(chain).http("localhost:3000").unwrap();
}
<|endoftext|>


<commit_before>#![allow(dead_code)]

extern crate daggy;
#[macro_use]
extern crate error_chain;
extern crate fs2;
extern crate libc;
extern crate serde_json;
extern crate tempdir;
extern crate walkdir;

use std::hash::{Hash, Hasher};
use std::io::Write;
use std::path::{Path, PathBuf};
use std::process::{Command, ExitStatus};
use std::{env, mem, process};

use parse::Args;
use errors::*;

mod cargo;
mod dag;
mod errors;
mod flock;
mod fs;
mod io;
mod parse;
mod rustc;
mod sysroot;
mod toml;
mod xargo;

fn main() {
    fn show_backtrace() -> bool {
        env::var("RUST_BACKTRACE").as_ref().map(|s| &s[..]) == Ok("1")
    }

    match run() {
        Err(e) => {
            let stderr = io::stderr();
            let mut stderr = stderr.lock();

            writeln!(stderr, "error: {}", e).ok();

            for e in e.iter().skip(1) {
                writeln!(stderr, "caused by: {}", e).ok();
            }

            if show_backtrace() {
                if let Some(backtrace) = e.backtrace() {
                    writeln!(stderr, "{:?}", backtrace).ok();
                }
            } else {
                writeln!(stderr,
                         "note: run with `RUST_BACKTRACE=1` for a backtrace")
                    .ok();
            }

            process::exit(1)
        }
        Ok(status) => {
            if !status.success() {
                process::exit(status.code().unwrap_or(1))
            }
        }
    }
}

fn run() -> Result<ExitStatus> {
    let Args { mut cmd, target, subcommand, verbose } = try!(parse::args());

    let target = if let Some(target) = target.as_ref() {
        Some(try!(Target::from(target)))
    } else if let Some(target) =
        try!(parse::target_in_cargo_config()) {
        Some(try!(Target::from(&target)))
    } else {
        None
    };

    let needs_sysroot = match subcommand.as_ref().map(|s| &s[..]) {
        // we don't need to rebuild the sysroot for these subcommands
        None | Some("clean") | Some("init") | Some("new") |
        Some("update") | Some("search") => None,
        _ => {
            if let Some(target) = target.as_ref() {
                if target.needs_sysroot() {
                    try!(sysroot::update(target, verbose));
                    Some(target)
                } else {
                    None
                }
            } else {
                None
            }
        }
    };

    let locks = if let Some(target) = needs_sysroot {
        // TODO(Filesystem.display()) this could be better ...
        let sysroot = format!("{}", try!(xargo::home()).display());

        if subcommand.as_ref().map(|s| &s[..]) == Some("doc") {
            let mut flags = try!(rustc::flags(target, "rustdocflags"));
            flags.push("--sysroot".to_owned());

            flags.push(sysroot.clone());

            cmd.env("RUSTDOCFLAGS", flags.join(" "));
        }

        let mut flags = try!(rustc::flags(target, "rustflags"));
        flags.push("--sysroot".to_owned());

        // TODO(Filesystem.display()) this could be better ...
        flags.push(sysroot);

        cmd.env("RUSTFLAGS", flags.join(" "));

        // Make sure the sysroot is not blown up while the Cargo command is
        // running
        Some((xargo::lock_ro(&try!(rustc::meta()).host),
              xargo::lock_ro(target.triple())))
    } else {
        None
    };

    let status = try!(cmd.status()
        .chain_err(|| "failed to execute `cargo`. Is it not installed?"));

    mem::drop(locks);

    Ok(status)
}

#[derive(Debug)]
pub enum Target {
    BuiltIn { triple: String },
    Custom { triple: String, json: PathBuf },
    Path { triple: String, json: PathBuf },
}

impl Target {
    fn from(target: &str) -> Result<Target> {
        let target_list = try!(rustc::target_list());

        let target = target.to_owned();
        if target_list.iter().any(|t| t == &target) {
            Ok(Target::BuiltIn { triple: target })
        } else if target.ends_with(".json") {
            if let Some(triple) = Path::new(&target)
                .file_stem()
                .and_then(|f| f.to_str()) {
                Ok(Target::Path {
                    json: PathBuf::from(&target),
                    triple: triple.to_owned(),
                })
            } else {
                try!(Err(format!("error extracting triple from {}", target)))
            }
        } else {
            let json = Path::new(&target).with_extension("json");

            if json.exists() {
                Ok(Target::Custom {
                    json: json,
                    triple: target,
                })
            } else {
                if let Some(target_dir) = env::var_os("RUST_TARGET_PATH") {
                    let json = PathBuf::from(target_dir)
                        .join(&target)
                        .with_extension("json");

                    if json.exists() {
                        return Ok(Target::Custom {
                            json: json,
                            triple: target,
                        });
                    }
                }

                try!(Err(format!("no target specification file found \
                                  for {}",
                                 target)))
            }
        }
    }
}

impl Target {
    fn hash<H>(&self, hasher: &mut H) -> Result<()>
        where H: Hasher
    {
        match *self {
            Target::BuiltIn { .. } => {}
            Target::Custom { ref json, .. } |
            Target::Path { ref json, .. } => try!(io::read(json)).hash(hasher),
        }

        Ok(())
    }

    fn needs_sysroot(&self) -> bool {
        match *self {
            Target::BuiltIn { ref triple } => {
                match &triple[..] {
                    "thumbv6m-none-eabi" |
                    "thumbv7m-none-eabi" |
                    "thumbv7em-none-eabi" |
                    "thumbv7em-none-eabihf" => true,
                    _ => false,
                }
            }
            _ => true,
        }
    }

    fn triple(&self) -> &str {
        match *self {
            Target::BuiltIn { ref triple } |
            Target::Custom { ref triple, .. } |
            Target::Path { ref triple, .. } => triple,
        }
    }
}

trait CommandExt {
    fn run_and_get_status(&mut self) -> Result<ExitStatus>;
    fn run_and_get_stdout(&mut self) -> Result<String>;
    fn run_or_error(&mut self) -> Result<()>;
}

impl CommandExt for Command {
    fn run_and_get_status(&mut self) -> Result<ExitStatus> {
        let cmd = &format!("`{:?}`", self);

        Ok(try!(self.status()
            .chain_err(|| format!("failed to execute {}", cmd))))
    }

    fn run_and_get_stdout(&mut self) -> Result<String> {
        let cmd = &format!("`{:?}`", self);

        let output = try!(self.output()
            .chain_err(|| format!("failed to execute {}", cmd)));

        if !output.status.success() {
            try!(Err(format!("{} failed with exit status: {:?}",
                             cmd,
                             output.status.code())))
        }

        Ok(try!(String::from_utf8(output.stdout)
            .chain_err(|| format!("{} output was not UTF-8 encoded", cmd))))
    }

    fn run_or_error(&mut self) -> Result<()> {
        let cmd = &format!("`{:?}`", self);

        let exit = try!(self.status()
            .chain_err(|| format!("failed to execute {}", cmd)));

        if !exit.success() {
            try!(Err(format!("{} failed with exit status {:?}",
                             cmd,
                             exit.code())))
        }

        Ok(())
    }
}
<commit_msg>report back stderr when a command fails<commit_after>#![allow(dead_code)]

extern crate daggy;
#[macro_use]
extern crate error_chain;
extern crate fs2;
extern crate libc;
extern crate serde_json;
extern crate tempdir;
extern crate walkdir;

use std::hash::{Hash, Hasher};
use std::io::Write;
use std::path::{Path, PathBuf};
use std::process::{Command, ExitStatus};
use std::{env, mem, process};

use parse::Args;
use errors::*;

mod cargo;
mod dag;
mod errors;
mod flock;
mod fs;
mod io;
mod parse;
mod rustc;
mod sysroot;
mod toml;
mod xargo;

fn main() {
    fn show_backtrace() -> bool {
        env::var("RUST_BACKTRACE").as_ref().map(|s| &s[..]) == Ok("1")
    }

    match run() {
        Err(e) => {
            let stderr = io::stderr();
            let mut stderr = stderr.lock();

            writeln!(stderr, "error: {}", e).ok();

            for e in e.iter().skip(1) {
                writeln!(stderr, "caused by: {}", e).ok();
            }

            if show_backtrace() {
                if let Some(backtrace) = e.backtrace() {
                    writeln!(stderr, "{:?}", backtrace).ok();
                }
            } else {
                writeln!(stderr,
                         "note: run with `RUST_BACKTRACE=1` for a backtrace")
                    .ok();
            }

            process::exit(1)
        }
        Ok(status) => {
            if !status.success() {
                process::exit(status.code().unwrap_or(1))
            }
        }
    }
}

fn run() -> Result<ExitStatus> {
    let Args { mut cmd, target, subcommand, verbose } = try!(parse::args());

    let target = if let Some(target) = target.as_ref() {
        Some(try!(Target::from(target)))
    } else if let Some(target) =
        try!(parse::target_in_cargo_config()) {
        Some(try!(Target::from(&target)))
    } else {
        None
    };

    let needs_sysroot = match subcommand.as_ref().map(|s| &s[..]) {
        // we don't need to rebuild the sysroot for these subcommands
        None | Some("clean") | Some("init") | Some("new") |
        Some("update") | Some("search") => None,
        _ => {
            if let Some(target) = target.as_ref() {
                if target.needs_sysroot() {
                    try!(sysroot::update(target, verbose));
                    Some(target)
                } else {
                    None
                }
            } else {
                None
            }
        }
    };

    let locks = if let Some(target) = needs_sysroot {
        // TODO(Filesystem.display()) this could be better ...
        let sysroot = format!("{}", try!(xargo::home()).display());

        if subcommand.as_ref().map(|s| &s[..]) == Some("doc") {
            let mut flags = try!(rustc::flags(target, "rustdocflags"));
            flags.push("--sysroot".to_owned());

            flags.push(sysroot.clone());

            cmd.env("RUSTDOCFLAGS", flags.join(" "));
        }

        let mut flags = try!(rustc::flags(target, "rustflags"));
        flags.push("--sysroot".to_owned());

        // TODO(Filesystem.display()) this could be better ...
        flags.push(sysroot);

        cmd.env("RUSTFLAGS", flags.join(" "));

        // Make sure the sysroot is not blown up while the Cargo command is
        // running
        Some((xargo::lock_ro(&try!(rustc::meta()).host),
              xargo::lock_ro(target.triple())))
    } else {
        None
    };

    let status = try!(cmd.status()
        .chain_err(|| "failed to execute `cargo`. Is it not installed?"));

    mem::drop(locks);

    Ok(status)
}

#[derive(Debug)]
pub enum Target {
    BuiltIn { triple: String },
    Custom { triple: String, json: PathBuf },
    Path { triple: String, json: PathBuf },
}

impl Target {
    fn from(target: &str) -> Result<Target> {
        let target_list = try!(rustc::target_list());

        let target = target.to_owned();
        if target_list.iter().any(|t| t == &target) {
            Ok(Target::BuiltIn { triple: target })
        } else if target.ends_with(".json") {
            if let Some(triple) = Path::new(&target)
                .file_stem()
                .and_then(|f| f.to_str()) {
                Ok(Target::Path {
                    json: PathBuf::from(&target),
                    triple: triple.to_owned(),
                })
            } else {
                try!(Err(format!("error extracting triple from {}", target)))
            }
        } else {
            let json = Path::new(&target).with_extension("json");

            if json.exists() {
                Ok(Target::Custom {
                    json: json,
                    triple: target,
                })
            } else {
                if let Some(target_dir) = env::var_os("RUST_TARGET_PATH") {
                    let json = PathBuf::from(target_dir)
                        .join(&target)
                        .with_extension("json");

                    if json.exists() {
                        return Ok(Target::Custom {
                            json: json,
                            triple: target,
                        });
                    }
                }

                try!(Err(format!("no target specification file found \
                                  for {}",
                                 target)))
            }
        }
    }
}

impl Target {
    fn hash<H>(&self, hasher: &mut H) -> Result<()>
        where H: Hasher
    {
        match *self {
            Target::BuiltIn { .. } => {}
            Target::Custom { ref json, .. } |
            Target::Path { ref json, .. } => try!(io::read(json)).hash(hasher),
        }

        Ok(())
    }

    fn needs_sysroot(&self) -> bool {
        match *self {
            Target::BuiltIn { ref triple } => {
                match &triple[..] {
                    "thumbv6m-none-eabi" |
                    "thumbv7m-none-eabi" |
                    "thumbv7em-none-eabi" |
                    "thumbv7em-none-eabihf" => true,
                    _ => false,
                }
            }
            _ => true,
        }
    }

    fn triple(&self) -> &str {
        match *self {
            Target::BuiltIn { ref triple } |
            Target::Custom { ref triple, .. } |
            Target::Path { ref triple, .. } => triple,
        }
    }
}

trait CommandExt {
    fn run_and_get_status(&mut self) -> Result<ExitStatus>;
    fn run_and_get_stdout(&mut self) -> Result<String>;
    fn run_or_error(&mut self) -> Result<()>;
}

impl CommandExt for Command {
    fn run_and_get_status(&mut self) -> Result<ExitStatus> {
        let cmd = &format!("`{:?}`", self);

        Ok(try!(self.status()
            .chain_err(|| format!("failed to execute {}", cmd))))
    }

    fn run_and_get_stdout(&mut self) -> Result<String> {
        let cmd = &format!("`{:?}`", self);

        let output = try!(self.output()
            .chain_err(|| format!("failed to execute {}", cmd)));

        if !output.status.success() {
            try!(Err(format!("{} failed with exit status: {:?}.\nstderr:\n{}",
                             cmd,
                             output.status.code(),
                             String::from_utf8_lossy(&output.stderr))))
        }

        Ok(try!(String::from_utf8(output.stdout)
            .chain_err(|| format!("{} output was not UTF-8 encoded", cmd))))
    }

    fn run_or_error(&mut self) -> Result<()> {
        let cmd = &format!("`{:?}`", self);

        let exit = try!(self.status()
            .chain_err(|| format!("failed to execute {}", cmd)));

        if !exit.success() {
            try!(Err(format!("{} failed with exit status {:?}",
                             cmd,
                             exit.code())))
        }

        Ok(())
    }
}
<|endoftext|>


<commit_before>// logging
#[macro_use]
extern crate log;
extern crate pretty_env_logger;

// modbus
extern crate libmodbus_rs;

// iron
extern crate iron;
extern crate router;
extern crate staticfile;

// json
extern crate serde;
#[macro_use]
extern crate serde_derive;
extern crate serde_json;

// misc
extern crate clap;
extern crate ctrlc;
extern crate retry;
extern crate hex_slice;

// datatypes
#[macro_use]
extern crate enum_primitive;
#[macro_use]
extern crate bitflags;

use std::fs;
use std::path::Path;
use std::os::unix::fs::FileTypeExt;
use std::sync::{Arc, Mutex};
use std::sync::atomic::{AtomicBool, Ordering};

use clap::{App, Arg};

use iron::Iron;
use iron::prelude::*;
use iron::status;
use iron::middleware::Handler;
use iron::headers::{AccessControlAllowMethods, AccessControlAllowOrigin};
use iron::method::Method;
use iron::mime::Mime;
use router::Router;
use staticfile::Static;

mod sunsaver_connection;
use sunsaver_connection::{SunSaverConnection, FileSunSaverConnection, ModbusSunSaverConnection};
mod sunsaver;
use sunsaver::{ChargeState, ArrayFault, LoggedResponseDay};
mod api;
use api::*;

#[derive(Clone)]
struct ApiHandler {
    connection: Arc<Mutex<Box<SunSaverConnection>>>,
}

impl ApiHandler {
    fn new(connection: Box<SunSaverConnection>) -> ApiHandler {
        ApiHandler { connection: Arc::new(Mutex::new(connection)) }
    }
}

unsafe impl Send for ApiHandler {}
unsafe impl Sync for ApiHandler {}

impl Handler for ApiHandler {
    fn handle(&self, req: &mut Request) -> IronResult<Response> {
        debug!("{:?}", req.url);

        let mut response = Response::new();
        response.headers.set(
            AccessControlAllowMethods(vec![Method::Get]),
        );
        response.headers.set(AccessControlAllowOrigin::Any);
        let mime: Mime = "application/json".parse().unwrap();
        response = response.set((mime));

        let path = req.url.path();
        let last_path = path.clone().pop().unwrap();
        match last_path {
            "status" => {
                let connection = self.connection.clone();
                let mut unlocked_connection = connection.lock().unwrap();
                let a = ApiStatusResponse::from(unlocked_connection.read_status());
                let b = serde_json::to_string_pretty(&a).unwrap();
                response = response.set((status::Ok, b));
            }
            "logged" => {
                let connection = self.connection.clone();
                let mut unlocked_connection = connection.lock().unwrap();
                let a = ApiLoggedResponse::from(unlocked_connection.read_logged());
                let b = serde_json::to_string_pretty(&a).unwrap();
                response = response.set((status::Ok, b));
            }
            _ => {
                response = response.set((status::NotFound, String::new()));
            }
        };

        Ok(response)
    }
}

fn is_rtu_modbus_device(path: &Path) -> bool {
    let metadata = fs::metadata(path).unwrap();
    let file_type = metadata.file_type();
    debug!("is_socket for {:?} had metadata {:?}", path, metadata);
    debug!(
        "is_socket for {:?} is_block_device: {}, is_char_device: {}, is_fifo: {}, is_socket: {}",
        path,
        file_type.is_block_device(),
        file_type.is_char_device(),
        file_type.is_fifo(),
        file_type.is_socket()
    );
    metadata.file_type().is_char_device()
}

static CLI_ARG_DEVICE: &'static str = "DEVICE";
static CLI_ARG_PORT: &'static str = "PORT";

fn is_port_number(v: String) -> Result<(), String> {
    v.parse::<u16>().map(|_| ()).map_err(|_| {
        format!("Invalid port number: {}", v)
    })
}

fn main() {
    assert!(pretty_env_logger::init().is_ok());

    let matches = App::new("simple-client")
        .version(env!("CARGO_PKG_VERSION"))
        .about("HTTP RESTful server for SunSaver MPPT ModBus data")
        .author("Guy Taylor <thebiggerguy.co.uk@gmail.com>")
        .arg(
            Arg::with_name(CLI_ARG_DEVICE)
                .help("Serial device e.g. /dev/ttyUSB0")
                .long("device")
                .short("d")
                .takes_value(true)
                .empty_values(false)
                .required(true),
        )
        .arg(
            Arg::with_name(CLI_ARG_PORT)
                .help("HTTP server port")
                .long("port")
                .short("p")
                .takes_value(true)
                .empty_values(false)
                .required(false)
                .default_value("8080")
                .validator(is_port_number),
        )
        .get_matches();

    let serial_interface = Path::new(matches.value_of(CLI_ARG_DEVICE).unwrap());
    let port_number = matches
        .value_of(CLI_ARG_PORT)
        .unwrap()
        .parse::<u16>()
        .unwrap();

    if !serial_interface.exists() {
        panic!("Device does not exists: {:?}", serial_interface);
    }

    let connection: Box<SunSaverConnection> = if is_rtu_modbus_device(serial_interface) {
        info!("Device is a socket. Using Modbus");
        Box::new(ModbusSunSaverConnection::open(serial_interface))
    } else {
        info!("Device is not a socket. Using File");
        Box::new(FileSunSaverConnection::open(serial_interface))
    };

    let api_handler = ApiHandler::new(connection);
    let static_handler = Static::new(Path::new("web"));

    let mut router = Router::new();
    router.get("/", static_handler.clone(), "index");
    router.get("/:filepath(*)", static_handler.clone(), "static");
    router.get("/api/v1/status", api_handler.clone(), "api_v1_status");
    router.get("/api/v1/logged", api_handler.clone(), "api_v2_logger");

    let running = Arc::new(AtomicBool::new(true));
    let r = running.clone();
    ctrlc::set_handler(move || {
        r.store(false, Ordering::SeqCst);
        info!("Cought Ctrl-C");
    }).expect("Error setting Ctrl-C handler");

    info!("Starting server ...");
    let bind_address = format!("0.0.0.0:{}", port_number);
    let mut listening = Iron::new(router).http(&bind_address).unwrap();
    info!("Started server {}", bind_address);

    info!("Use Ctrl-C to stop");
    while running.load(Ordering::SeqCst) {}
    listening.close().unwrap();
}

#[cfg(test)]
mod test {
    extern crate tempdir;

    use std::fs::OpenOptions;

    use self::tempdir::TempDir;

    use super::*;

    #[test]
    fn is_rtu_modbus_device_test() {
        assert_eq!(is_rtu_modbus_device(Path::new("/dev/zero")), true); // TODO
        assert_eq!(is_rtu_modbus_device(Path::new("/dev/urandom")), true); // TODO
        assert_eq!(is_rtu_modbus_device(Path::new("/dev/tty")), true);
        let tty_usb = Path::new("/dev/ttyUSB0");
        if tty_usb.exists() {
            assert_eq!(is_rtu_modbus_device(tty_usb), true);
        }

        let temp_dir = TempDir::new(concat!(module_path!(), "is_rtu_modbus_device_test")).unwrap();
        let test_file = temp_dir.path().join("test");
        OpenOptions::new()
            .create(true)
            .write(true)
            .open(&test_file)
            .unwrap();
        assert_eq!(is_rtu_modbus_device(test_file.as_path()), false);
    }
}
<commit_msg>Allow overriding web root<commit_after>// logging
#[macro_use]
extern crate log;
extern crate pretty_env_logger;

// modbus
extern crate libmodbus_rs;

// iron
extern crate iron;
extern crate router;
extern crate staticfile;

// json
extern crate serde;
#[macro_use]
extern crate serde_derive;
extern crate serde_json;

// misc
extern crate clap;
extern crate ctrlc;
extern crate retry;
extern crate hex_slice;

// datatypes
#[macro_use]
extern crate enum_primitive;
#[macro_use]
extern crate bitflags;

use std::fs;
use std::path::Path;
use std::os::unix::fs::FileTypeExt;
use std::sync::{Arc, Mutex};
use std::sync::atomic::{AtomicBool, Ordering};

use clap::{App, Arg};

use iron::Iron;
use iron::prelude::*;
use iron::status;
use iron::middleware::Handler;
use iron::headers::{AccessControlAllowMethods, AccessControlAllowOrigin};
use iron::method::Method;
use iron::mime::Mime;
use router::Router;
use staticfile::Static;

mod sunsaver_connection;
use sunsaver_connection::{SunSaverConnection, FileSunSaverConnection, ModbusSunSaverConnection};
mod sunsaver;
use sunsaver::{ChargeState, ArrayFault, LoggedResponseDay};
mod api;
use api::*;

#[derive(Clone)]
struct ApiHandler {
    connection: Arc<Mutex<Box<SunSaverConnection>>>,
}

impl ApiHandler {
    fn new(connection: Box<SunSaverConnection>) -> ApiHandler {
        ApiHandler { connection: Arc::new(Mutex::new(connection)) }
    }
}

unsafe impl Send for ApiHandler {}
unsafe impl Sync for ApiHandler {}

impl Handler for ApiHandler {
    fn handle(&self, req: &mut Request) -> IronResult<Response> {
        debug!("{:?}", req.url);

        let mut response = Response::new();
        response.headers.set(
            AccessControlAllowMethods(vec![Method::Get]),
        );
        response.headers.set(AccessControlAllowOrigin::Any);
        let mime: Mime = "application/json".parse().unwrap();
        response = response.set((mime));

        let path = req.url.path();
        let last_path = path.clone().pop().unwrap();
        match last_path {
            "status" => {
                let connection = self.connection.clone();
                let mut unlocked_connection = connection.lock().unwrap();
                let a = ApiStatusResponse::from(unlocked_connection.read_status());
                let b = serde_json::to_string_pretty(&a).unwrap();
                response = response.set((status::Ok, b));
            }
            "logged" => {
                let connection = self.connection.clone();
                let mut unlocked_connection = connection.lock().unwrap();
                let a = ApiLoggedResponse::from(unlocked_connection.read_logged());
                let b = serde_json::to_string_pretty(&a).unwrap();
                response = response.set((status::Ok, b));
            }
            _ => {
                response = response.set((status::NotFound, String::new()));
            }
        };

        Ok(response)
    }
}

fn is_rtu_modbus_device(path: &Path) -> bool {
    let metadata = fs::metadata(path).unwrap();
    let file_type = metadata.file_type();
    debug!("is_socket for {:?} had metadata {:?}", path, metadata);
    debug!(
        "is_socket for {:?} is_block_device: {}, is_char_device: {}, is_fifo: {}, is_socket: {}",
        path,
        file_type.is_block_device(),
        file_type.is_char_device(),
        file_type.is_fifo(),
        file_type.is_socket()
    );
    metadata.file_type().is_char_device()
}

static CLI_ARG_DEVICE: &'static str = "DEVICE";
static CLI_ARG_PORT: &'static str = "PORT";
static CLI_ARG_WEB_ROOT: &'static str = "WEB_ROOT";

fn is_port_number(v: String) -> Result<(), String> {
    v.parse::<u16>().map(|_| ()).map_err(|_| {
        format!("Invalid port number: {}", v)
    })
}

fn main() {
    assert!(pretty_env_logger::init().is_ok());

    let matches = App::new("simple-client")
        .version(env!("CARGO_PKG_VERSION"))
        .about("HTTP RESTful server for SunSaver MPPT ModBus data")
        .author("Guy Taylor <thebiggerguy.co.uk@gmail.com>")
        .arg(
            Arg::with_name(CLI_ARG_DEVICE)
                .help("Serial device e.g. /dev/ttyUSB0")
                .long("device")
                .short("d")
                .takes_value(true)
                .empty_values(false)
                .required(true),
        )
        .arg(
            Arg::with_name(CLI_ARG_PORT)
                .help("HTTP server port")
                .long("port")
                .short("p")
                .takes_value(true)
                .empty_values(false)
                .required(false)
                .default_value("8080")
                .validator(is_port_number),
        )
        .arg(
            Arg::with_name(CLI_ARG_WEB_ROOT)
                .help("HTTP server root folder")
                .long("webroot")
                .takes_value(true)
                .empty_values(false)
                .required(false)
                .default_value("web"),
        )
        .get_matches();

    let serial_interface = Path::new(matches.value_of(CLI_ARG_DEVICE).unwrap());
    let port_number = matches
        .value_of(CLI_ARG_PORT)
        .unwrap()
        .parse::<u16>()
        .unwrap();
    let web_root = Path::new(matches.value_of(CLI_ARG_WEB_ROOT).unwrap());

    if !serial_interface.exists() {
        panic!("Device does not exists: {:?}", serial_interface);
    }

    let connection: Box<SunSaverConnection> = if is_rtu_modbus_device(serial_interface) {
        info!("Device is a socket. Using Modbus");
        Box::new(ModbusSunSaverConnection::open(serial_interface))
    } else {
        info!("Device is not a socket. Using File");
        Box::new(FileSunSaverConnection::open(serial_interface))
    };

    let api_handler = ApiHandler::new(connection);
    let static_handler = Static::new(web_root);

    let mut router = Router::new();
    router.get("/", static_handler.clone(), "index");
    router.get("/:filepath(*)", static_handler.clone(), "static");
    router.get("/api/v1/status", api_handler.clone(), "api_v1_status");
    router.get("/api/v1/logged", api_handler.clone(), "api_v2_logger");

    let running = Arc::new(AtomicBool::new(true));
    let r = running.clone();
    ctrlc::set_handler(move || {
        r.store(false, Ordering::SeqCst);
        info!("Cought Ctrl-C");
    }).expect("Error setting Ctrl-C handler");

    info!("Starting server ...");
    let bind_address = format!("0.0.0.0:{}", port_number);
    let mut listening = Iron::new(router).http(&bind_address).unwrap();
    info!("Started server {}", bind_address);

    info!("Use Ctrl-C to stop");
    while running.load(Ordering::SeqCst) {}
    listening.close().unwrap();
}

#[cfg(test)]
mod test {
    extern crate tempdir;

    use std::fs::OpenOptions;

    use self::tempdir::TempDir;

    use super::*;

    #[test]
    fn is_rtu_modbus_device_test() {
        assert_eq!(is_rtu_modbus_device(Path::new("/dev/zero")), true); // TODO
        assert_eq!(is_rtu_modbus_device(Path::new("/dev/urandom")), true); // TODO
        assert_eq!(is_rtu_modbus_device(Path::new("/dev/tty")), true);
        let tty_usb = Path::new("/dev/ttyUSB0");
        if tty_usb.exists() {
            assert_eq!(is_rtu_modbus_device(tty_usb), true);
        }

        let temp_dir = TempDir::new(concat!(module_path!(), "is_rtu_modbus_device_test")).unwrap();
        let test_file = temp_dir.path().join("test");
        OpenOptions::new()
            .create(true)
            .write(true)
            .open(&test_file)
            .unwrap();
        assert_eq!(is_rtu_modbus_device(test_file.as_path()), false);
    }
}
<|endoftext|>


<commit_before>use std::env;
use std::io;
use std::io::prelude::*;
use std::fs::File;
use std::str::Chars;

enum Builtin {
    dot,
    plus,
    minus,
    times,
    divide,
}

#[derive(Clone)]
enum Word {
    custom(Rc<Vec<String>>),
    builtin(Builtin),
    colon,
    semicolon,
    _if,
    then,
    _else,
    dotquote,
    int(i32),
    float(f32),
}

enum Branch {
    custom(Rc<Vec<String>>)
    builtin(Builtin),
    ifelse(Vec<String>, Vec<String>),
    dotquote(<String>)
}

struct Dictionary {
    items: HashMap<String, Word>
}

impl Dictionary {
    fn get(&self, name: &str) -> Option<Word> {
        if let Some(word) = self.get(name) {
            Some(word)
        } else if let Ok(num) = name.parse::<i32>() {
            Some(Word::int(num))
        } else if let Ok(num) = name.parse::<f32>() {
            Some(Word::float(num))
        } else {
            None
        }
    }
    fn set(&self, name: &str, value: Word) {
        if self.items.contains_key(name) {
            // XXX display debug message another way
            println!("Redefined {}", name);
        }
    }
}

impl Default for Dictionary {
    fn default() -> Dictionary {
        dict = HashMap::new();
        dict.insert(".\"", Word::dotquote);
        dict.insert(".", Word::builtin(Builtin::dot));
        dict.insert("+", Word::builtin(Builtin::plus));
        dict.insert("-", Word::builtin(Buitlin::minus));
        dict.insert("*", Word::builtin(Buitlin::times));
        dict.insert("/", Word::builtin(Buitlin::divide));
        dict.insert(":", Word::colon);
        dict.insert(";", Word::semicolon);
        dict.insert("if", Word::_if);
        dict.insert("then", Word::_then);
        dict.insert("else", Word::_else);
        Dictionary{items: dict}
    }
}

fn parse_next_word(code: &Chars, dict: &Dictionary) -> Some(Branch) {
    let mut word_str = String::new();
    let mut branch = None;
    while let Some(c) = chars.next() {
        if c == ' ' || c == '\n' {
            if let Some(word) = dict.get(word_str) {
                match word {
                    //stop => { return (words, true); },
                    Word::custom(_) | Word::builtin(_) | Word::int(_) | Word::float(_) => {
                        branch = Some(Branch::word(word.clone()))
                    }
                    Word::dotquote => {
                        let text = chars.take_while(|x| x != '"').collect();
                        branch = Branch::dotquote(text);
                    }
                    Word::colon => {
                        let mut name = String::new();
                        while let Some(c) = chars.next() {
                            if c == ' ' || c == '\n' {
                                break;
                            }
                            name.push(c);
                        }

                    }
                }
            break;
            }
        } else {
            word_str.push(c);
        }
    }

    branch
}

fn parse(code: &Chars) -> Vec<Branch> {
    let mut branches: Vec<Branch> = Vec::new();
    let mut word_str = String::new();
    let dict = Dictionary::default();
    while let Some(branch) = parse_next_word(code, &Dictionary) {
        branches.push(branch);
    }

    branches
}

fn main() {
    let path = env::args().nth(1).unwrap();
    let mut file = File::open(&path).unwrap();
    let mut code = String::new();
    file.read_to_string(&mut code).unwrap();

    println!("{:#?}", parse(&code.iter(), None).0);
}
<commit_msg>Add call() method to Builtin<commit_after>use std::env;
use std::io;
use std::io::prelude::*;
use std::fs::File;
use std::str::Chars;

enum Builtin {
    dot,
    plus,
    minus,
    times,
    divide,
}

impl Builtin {
    fn call(&self, stack: &Vec<i32>) {
        match *self {
            Builtin::dot => print!("{}", stack.pop()),
            Bulltin::plus => {
                n2 = stack.pop();
                n1 = stack.pop();
                stack.push(n1 + n2);
            },
            Bulltin::minus => {
                n2 = stack.pop();
                n1 = stack.pop();
                stack.push(n1 - n2);
            },
            Bulltin::times => {
                n2 = stack.pop();
                n1 = stack.pop();
                stack.push(n1 * n2);
            },
            Bulltin::divide => {
                n2 = stack.pop();
                n1 = stack.pop();
                stack.push(n1 / n2);
            },
        }
    }
}

#[derive(Clone)]
enum Word {
    custom(Rc<Vec<String>>),
    builtin(Builtin),
    colon,
    semicolon,
    _if,
    then,
    _else,
    dotquote,
    int(i32),
    float(f32),
}

enum Branch {
    custom(Rc<Vec<String>>)
    builtin(Builtin),
    ifelse(Vec<String>, Vec<String>),
    dotquote(<String>)
}

struct Dictionary {
    items: HashMap<String, Word>
}

impl Dictionary {
    fn get(&self, name: &str) -> Option<Word> {
        if let Some(word) = self.get(name) {
            Some(word)
        } else if let Ok(num) = name.parse::<i32>() {
            Some(Word::int(num))
        } else if let Ok(num) = name.parse::<f32>() {
            Some(Word::float(num))
        } else {
            None
        }
    }
    fn set(&self, name: &str, value: Word) {
        if self.items.contains_key(name) {
            // XXX display debug message another way
            println!("Redefined {}", name);
        }
    }
}

impl Default for Dictionary {
    fn default() -> Dictionary {
        dict = HashMap::new();
        dict.insert(".\"", Word::dotquote);
        dict.insert(".", Word::builtin(Builtin::dot));
        dict.insert("+", Word::builtin(Builtin::plus));
        dict.insert("-", Word::builtin(Buitlin::minus));
        dict.insert("*", Word::builtin(Buitlin::times));
        dict.insert("/", Word::builtin(Buitlin::divide));
        dict.insert(":", Word::colon);
        dict.insert(";", Word::semicolon);
        dict.insert("if", Word::_if);
        dict.insert("then", Word::_then);
        dict.insert("else", Word::_else);
        Dictionary{items: dict}
    }
}

fn parse_next_word(code: &Chars, dict: &Dictionary) -> Some(Branch) {
    let mut word_str = String::new();
    let mut branch = None;
    while let Some(c) = chars.next() {
        if c == ' ' || c == '\n' {
            if let Some(word) = dict.get(word_str) {
                match word {
                    //stop => { return (words, true); },
                    Word::custom(_) | Word::builtin(_) | Word::int(_) | Word::float(_) => {
                        branch = Some(Branch::word(word.clone()))
                    }
                    Word::dotquote => {
                        let text = chars.take_while(|x| x != '"').collect();
                        branch = Branch::dotquote(text);
                    }
                    Word::colon => {
                        let mut name = String::new();
                        while let Some(c) = chars.next() {
                            if c == ' ' || c == '\n' {
                                break;
                            }
                            name.push(c);
                        }

                    }
                }
            break;
            }
        } else {
            word_str.push(c);
        }
    }

    branch
}

fn parse(code: &Chars) -> Vec<Branch> {
    let mut branches: Vec<Branch> = Vec::new();
    let mut word_str = String::new();
    let dict = Dictionary::default();
    while let Some(branch) = parse_next_word(code, &Dictionary) {
        branches.push(branch);
    }

    branches
}

fn main() {
    let path = env::args().nth(1).unwrap();
    let mut file = File::open(&path).unwrap();
    let mut code = String::new();
    file.read_to_string(&mut code).unwrap();

    println!("{:#?}", parse(&code.iter(), None).0);
}
<|endoftext|>


<commit_before>extern crate combine;

use std::io::prelude::*;

use combine::{between, many1, digit, letter, spaces, string, try, choice, parser, Parser, ParserExt, ParseError};
use combine::char as combine_char;
use combine::primitives::{Stream, State, ParseResult};

#[derive(Debug, PartialEq)]
enum LispVal {
    Atom(String),
    Bool(bool),
    Number(i32),
    Character(char),
    String(String)
}

fn parse_symbol<I>(input: State<I>) -> ParseResult<String, I>
where I: Stream<Item=char>
{
    let lex_char = |c| combine_char(c).skip(spaces());

    let symbols = choice([lex_char('!'), lex_char('$'), lex_char('%'), lex_char('$'), lex_char('&'),
                    lex_char('|'), lex_char('*'), lex_char('+'), lex_char('-'), lex_char(':'),
                    lex_char('<'), lex_char('='), lex_char('>'), lex_char('?'), lex_char('@'),
                    lex_char('^'), lex_char('_'), lex_char('~')]);

    symbols.map(|c: char| {
            let mut s = String::new();
            s.push(c);
            return s;
        })
        .parse_state(input)
}

fn parse_bool<I>(input: State<I>) -> ParseResult<bool, I>
where I: Stream<Item=char>
{
    let bewl = try(string("#t").map(|_| return true)).or(
            string("#f").map(|_| return false)
        )
       .parse_state(input);

    bewl
}

fn parse_atom<I>(input: State<I>) -> ParseResult<String, I>
where I: Stream<Item=char>
{
    let atom = (parser(parse_symbol::<I>))
        .parse_state(input);

    atom
}

fn parse_string<I>(input: State<I>) -> ParseResult<String, I>
where I: Stream<Item=char>
{
    let string = between(combine_char('"'), combine_char('"'), many1(letter()))
        .parse_state(input);
    string
}

fn parse_char<I>(input: State<I>) -> ParseResult<char, I>
where I: Stream<Item=char>
{
    between(combine_char('\''), combine_char('\''), letter())
        .parse_state(input)
}

fn parse_number<I>(input: State<I>) -> ParseResult<i32, I>
where I: Stream<Item=char>
{
    let number = many1(digit())
        .map(|string: String| string.parse::<i32>().unwrap())
        .parse_state(input);
    number
}

fn rusty_lisp<I>(input: State<I>) -> ParseResult<LispVal, I>
where I: Stream<Item=char>
{
    let lispval = parser(parse_atom::<I>)
                   .map(LispVal::Atom)
        .or(parser(parse_bool::<I>)
            .map(LispVal::Bool))
        .or(parser(parse_number::<I>)
            .map(LispVal::Number))
        .or(parser(parse_char::<I>)
            .map(LispVal::Character))
        .or(parser(parse_string::<I>)
            .map(LispVal::String))
        .parse_state(input);

    lispval
}

fn parse(program: String) {
    let program = program.as_ref();

    let result: Result<(LispVal, &str), ParseError<&str>> = parser(rusty_lisp).parse(program);
    match result {
        Ok((value, _remaining_input)) => println!("{:?}", value),
        Err(err) => println!("{}", err)
    }
}

fn main() {
    println!("rusty-lisp v0.1.0");
    println!("Press 'Ctrl+C' to exit");
    println!("");

    let mut stdin = std::io::stdin();
    let mut input = String::new();

    stdin.read_to_string(&mut input).unwrap();
    parse(input);

    println!("Thanks for using!");
}
<commit_msg>tdd, baby<commit_after>extern crate combine;

use std::io::prelude::*;

use combine::{between, many1, digit, letter, spaces, string, try, choice, parser, Parser, ParserExt, ParseError};
use combine::char as combine_char;
use combine::primitives::{Stream, State, ParseResult};

#[derive(Debug, PartialEq)]
enum LispVal {
    Atom(String),
    Bool(bool),
    Number(i32),
    Character(char),
    String(String)
}

fn parse_symbol<I>(input: State<I>) -> ParseResult<String, I>
where I: Stream<Item=char>
{
    let lex_char = |c| combine_char(c).skip(spaces());

    let symbols = choice([lex_char('!'), lex_char('$'), lex_char('%'), lex_char('$'), lex_char('&'),
                    lex_char('|'), lex_char('*'), lex_char('+'), lex_char('-'), lex_char(':'),
                    lex_char('<'), lex_char('='), lex_char('>'), lex_char('?'), lex_char('@'),
                    lex_char('^'), lex_char('_'), lex_char('~')]);

    symbols.map(|c: char| {
            let mut s = String::new();
            s.push(c);
            return s;
        })
        .parse_state(input)
}

fn parse_bool<I>(input: State<I>) -> ParseResult<bool, I>
where I: Stream<Item=char>
{
    let bewl = try(string("#t").map(|_| return true)).or(
            string("#f").map(|_| return false)
        )
       .parse_state(input);

    bewl
}

fn parse_atom<I>(input: State<I>) -> ParseResult<String, I>
where I: Stream<Item=char>
{
    let atom = (parser(parse_symbol::<I>))
        .parse_state(input);

    atom
}

fn parse_string<I>(input: State<I>) -> ParseResult<String, I>
where I: Stream<Item=char>
{
    let string = between(combine_char('"'), combine_char('"'), many1(letter()))
        .parse_state(input);
    string
}

fn parse_char<I>(input: State<I>) -> ParseResult<char, I>
where I: Stream<Item=char>
{
    between(combine_char('\''), combine_char('\''), letter())
        .parse_state(input)
}

fn parse_number<I>(input: State<I>) -> ParseResult<i32, I>
where I: Stream<Item=char>
{
    let number = many1(digit())
        .map(|string: String| string.parse::<i32>().unwrap())
        .parse_state(input);
    number
}

fn rusty_lisp<I>(input: State<I>) -> ParseResult<LispVal, I>
where I: Stream<Item=char>
{
    let lispval = parser(parse_atom::<I>)
                   .map(LispVal::Atom)
        .or(parser(parse_bool::<I>)
            .map(LispVal::Bool))
        .or(parser(parse_number::<I>)
            .map(LispVal::Number))
        .or(parser(parse_char::<I>)
            .map(LispVal::Character))
        .or(parser(parse_string::<I>)
            .map(LispVal::String))
        .parse_state(input);

    lispval
}

fn parse(program: String) {
    let program = program.as_ref();

    let result: Result<(LispVal, &str), ParseError<&str>> = parser(rusty_lisp).parse(program);
    match result {
        Ok((value, _remaining_input)) => println!("{:?}", value),
        Err(err) => println!("{}", err)
    }
}

fn main() {
    println!("rusty-lisp v0.1.0");
    println!("Press 'Ctrl+C' to exit");
    println!("");

    let mut stdin = std::io::stdin();
    let mut input = String::new();

    stdin.read_to_string(&mut input).unwrap();
    parse(input);

    println!("Thanks for using!");
}

#[cfg(test)]
mod tests {
    use combine::{parser, Parser, ParseError};
    use super::{parse_symbol, parse_bool, parse_char, parse_string, parse_number};

    #[test]
    fn test_parse_symbol() {
        let input = "!";
        let result: Result<(String, &str), ParseError<&str>> = parser(parse_symbol).parse(input);
        assert_eq!(result, Ok(("!".to_string(), "")))
    }

    #[test]
    fn test_parse_bool() {
        let input = "#t";
        let result: Result<(bool, &str), ParseError<&str>> = parser(parse_bool).parse(input);
        assert_eq!(result, Ok((true, "")));

        let input = "#f";
        let result: Result<(bool, &str), ParseError<&str>> = parser(parse_bool).parse(input);
        assert_eq!(result, Ok((false, "")))
    }

    #[test]
    fn test_parse_string() {
        let input = "\"Foo\"";
        let result: Result<(String, &str), ParseError<&str>> = parser(parse_string).parse(input);
        assert_eq!(result, Ok(("Foo".to_string(), "")))
    }

    #[test]
    fn test_parse_char() {
        let input = "'c'";
        let result: Result<(char, &str), ParseError<&str>> = parser(parse_char).parse(input);
        assert_eq!(result, Ok(('c', "")))
    }

    #[test]
    fn test_parse_number() {
        let input = "42";
        let result: Result<(i32, &str), ParseError<&str>> = parser(parse_number).parse(input);
        assert_eq!(result, Ok((42, "")))
    }

}
<|endoftext|>


<commit_before>use common::*;
use gl;
use interval_timer::IntervalTimer;
use process_event::process_event;
use render::render;
use sdl2;
use sdl2::event::Event;
use state::App;
use std::mem;
use std::time::duration::Duration;
use std::io::timer;
use stopwatch::TimerSet;
use time;
use update::update;
use yaglw::gl_context::{GLContext, GLContextExistence};

pub const FRAMES_PER_SECOND: u64 = 30;
pub const UPDATES_PER_SECOND: u64 = 30;

pub fn main() {
  debug!("starting");

  with_sdl2_gl_loaded(|window, gl, gl_context| {
    let timers = TimerSet::new();
    let mut app = App::new(gl, gl_context, &timers);

    let mut render_timer;
    let mut update_timer;
    {
      let now = time::precise_time_ns();
      let nanoseconds_per_second = 1000000000;
      render_timer = IntervalTimer::new(nanoseconds_per_second / FRAMES_PER_SECOND, now);
      update_timer = IntervalTimer::new(nanoseconds_per_second / UPDATES_PER_SECOND, now);
    }

    loop {
      let updates = update_timer.update(time::precise_time_ns());
      if updates > 0 {
        update(&mut app);
      }

      let renders = render_timer.update(time::precise_time_ns());
      if renders > 0 {
        render(&mut app);
        // swap buffers
        window.gl_swap_window();
      }

      loop {
        match sdl2::event::poll_event() {
          Event::None => {
            break;
          },
          Event::Quit(_) => {
            return;
          }
          Event::AppTerminating(_) => {
            return;
          }
          event => {
            process_event(&mut app, window, event);
          },
        }
      }
      timer::sleep(Duration::microseconds(10));
    }
  });

  debug!("finished");
}

#[allow(unused_variables)]
fn with_sdl2_gl_loaded(act: |&mut sdl2::video::Window, &GLContextExistence, &mut GLContext|) {
  sdl2::init(sdl2::INIT_EVERYTHING);

  sdl2::video::gl_set_attribute(sdl2::video::GLAttr::GLContextMajorVersion, 3);
  sdl2::video::gl_set_attribute(sdl2::video::GLAttr::GLContextMinorVersion, 3);
  sdl2::video::gl_set_attribute(
    sdl2::video::GLAttr::GLContextProfileMask,
    sdl2::video::GLProfile::GLCoreProfile as int
  );

  let mut window = sdl2::video::Window::new(
    "playform",
    sdl2::video::WindowPos::PosCentered,
    sdl2::video::WindowPos::PosCentered,
    WINDOW_WIDTH as int,
    WINDOW_HEIGHT as int,
    sdl2::video::OPENGL,
  ).unwrap();

  // Send text input events.
  sdl2::keyboard::start_text_input();

  let sdl_gl_context = window.gl_create_context().unwrap();

  // Load the OpenGL function pointers.
  gl::load_with(|s| unsafe {
    mem::transmute(sdl2::video::gl_get_proc_address(s))
  });

  let (gl, mut gl_context) = unsafe {
    GLContext::new()
  };

  act(&mut window, &gl, &mut gl_context);
}
<commit_msg>cleanups in main<commit_after>use common::*;
use gl;
use interval_timer::IntervalTimer;
use process_event::process_event;
use render::render;
use sdl2;
use sdl2::event::Event;
use state::App;
use std::mem;
use std::time::duration::Duration;
use std::io::timer;
use stopwatch::TimerSet;
use time;
use update::update;
use yaglw::gl_context::GLContext;

pub const FRAMES_PER_SECOND: u64 = 30;
pub const UPDATES_PER_SECOND: u64 = 30;

pub fn main() {
  debug!("starting");

  sdl2::init(sdl2::INIT_EVERYTHING);

  sdl2::video::gl_set_attribute(sdl2::video::GLAttr::GLContextMajorVersion, 3);
  sdl2::video::gl_set_attribute(sdl2::video::GLAttr::GLContextMinorVersion, 3);
  sdl2::video::gl_set_attribute(
    sdl2::video::GLAttr::GLContextProfileMask,
    sdl2::video::GLProfile::GLCoreProfile as int
  );

  let mut window =
    sdl2::video::Window::new(
      "playform",
      sdl2::video::WindowPos::PosCentered,
      sdl2::video::WindowPos::PosCentered,
      WINDOW_WIDTH as int,
      WINDOW_HEIGHT as int,
      sdl2::video::OPENGL,
    ).unwrap();

  // Send text input events.
  sdl2::keyboard::start_text_input();

  {
    let _sdl_gl_context = window.gl_create_context().unwrap();

    // Load the OpenGL function pointers.
    gl::load_with(|s| unsafe {
      mem::transmute(sdl2::video::gl_get_proc_address(s))
    });

    let (gl, mut gl_context) = unsafe {
      GLContext::new()
    };

    let timers = TimerSet::new();
    let mut app = App::new(&gl, &mut gl_context, &timers);

    let mut render_timer;
    let mut update_timer;
    {
      let now = time::precise_time_ns();
      let nanoseconds_per_second = 1000000000;
      render_timer = IntervalTimer::new(nanoseconds_per_second / FRAMES_PER_SECOND, now);
      update_timer = IntervalTimer::new(nanoseconds_per_second / UPDATES_PER_SECOND, now);
    }

    loop {
      let updates = update_timer.update(time::precise_time_ns());
      if updates > 0 {
        update(&mut app);
      }

      let renders = render_timer.update(time::precise_time_ns());
      if renders > 0 {
        render(&mut app);
        // swap buffers
        window.gl_swap_window();
      }

      loop {
        match sdl2::event::poll_event() {
          Event::None => {
            break;
          },
          Event::Quit(_) => {
            return;
          }
          Event::AppTerminating(_) => {
            return;
          }
          event => {
            process_event(&mut app, &mut window, event);
          },
        }
      }
      timer::sleep(Duration::microseconds(10));
    }
  }

  debug!("finished");
}
<|endoftext|>