<commit_before>//! xdg-rs is a utility library to make conforming to the [XDG specification](http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html) easier

#![feature(if_let)]

use std::io;
use std::io::fs::PathExtensions;
use std::os;

/// Run some sanity checks for the XDG spec
///
/// Returns ```Ok(true)``` if $XDG_RUNTIME_DIR is founc and valid, ```Ok(false)``` if not found
/// and ```Err(msg)``` if and error occured
pub fn xdg_init() -> Result<bool, String> {
    match getenv_path("XDG_RUNTIME_DIR") {
        Some(path) => {
            match path.stat() {
                Ok(stat_buf) => {
                    if stat_buf.perm.intersects(io::GROUP_RWX | io::OTHER_RWX) {
                        Err("Incorrect permissions for $XDG_RUNTIME_DIR".to_string())
                    } else {
                        Ok(true)
                    }
                },

                Err(_) => Ok(false)
            }
        },

        None => Ok(false),
    }
}

/// Get the data home directory
///
/// If $XDG_DATA_HOME is not set, it returns $HOME/.local/share
pub fn get_data_home() -> Path {
    getenv_path("XDG_DATA_HOME").unwrap_or(
        os::homedir().unwrap().join("/.local/share")
    )
}

/// Get the config home directory
///
/// If $XDG_CONFIG_HOME is not set, it returns $HOME/.config
pub fn get_config_home() -> Path {
    getenv_path("XDG_CONFIG_HOME").unwrap_or(
        os::homedir().unwrap().join("/.config")
    )
}

/// Get the cache home directory
///
/// If $XDG_CACHE_HOME is not set, it returns $HOME/.cache
pub fn get_cache_home() -> Path {
    getenv_path("XDG_CACHE_HOME").unwrap_or(
        os::homedir().unwrap().join("/.cache")
    )
}

/// Get $XDG_RUNTIME_DIR if found and valid
///
/// This directory must have permissions set to 0700
pub fn get_runtime_dir() -> Option<Path> {
    getenv_path("XDG_RUNTIME_DIR")
}

/// Get an environment variable's value as a Path
fn getenv_path(env_var: &str) -> Option<Path> {
    let path = os::getenv(env_var);
    match path {
        Some(path) => Some(Path::new(path)),
        None => None
    }
}
<commit_msg>update doc text to include reference to rust-xdg<commit_after>//! xdg-rs is a utility library to make conforming to the [XDG specification](http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html) easier
//!
//! Some code borrowed from [rust-xdg](https://github.com/o11c/rust-xdg). ```rust-xdg``` is
//! currently a more complete implementation of the specification. The APIs provided by
//! ```rust-xdg``` and ```xdg-rs``` are different.

#![feature(if_let)]

use std::io;
use std::io::fs::PathExtensions;
use std::os;

/// Run some sanity checks for the XDG spec
///
/// Returns ```Ok(true)``` if $XDG_RUNTIME_DIR is founc and valid, ```Ok(false)``` if not found
/// and ```Err(msg)``` if and error occured
pub fn xdg_init() -> Result<bool, String> {
    match getenv_path("XDG_RUNTIME_DIR") {
        Some(path) => {
            match path.stat() {
                Ok(stat) => {
                    if stat.perm.intersects(io::GROUP_RWX | io::OTHER_RWX) {
                        Err("Incorrect permissions for $XDG_RUNTIME_DIR".to_string())
                    } else {
                        Ok(true)
                    }
                },

                Err(_) => Ok(false)
            }
        },

        None => Ok(false),
    }
}

/// Get the data home directory
///
/// If $XDG_DATA_HOME is not set, it returns $HOME/.local/share
pub fn get_data_home() -> Path {
    getenv_path("XDG_DATA_HOME").unwrap_or(
        os::homedir().unwrap().join("/.local/share")
    )
}

/// Get the config home directory
///
/// If $XDG_CONFIG_HOME is not set, it returns $HOME/.config
pub fn get_config_home() -> Path {
    getenv_path("XDG_CONFIG_HOME").unwrap_or(
        os::homedir().unwrap().join("/.config")
    )
}

/// Get the cache home directory
///
/// If $XDG_CACHE_HOME is not set, it returns $HOME/.cache
pub fn get_cache_home() -> Path {
    getenv_path("XDG_CACHE_HOME").unwrap_or(
        os::homedir().unwrap().join("/.cache")
    )
}

/// Get $XDG_RUNTIME_DIR if found and valid
///
/// This directory must have permissions set to 0700
pub fn get_runtime_dir() -> Option<Path> {
    getenv_path("XDG_RUNTIME_DIR")
}

/// Get an environment variable's value as a Path
fn getenv_path(env_var: &str) -> Option<Path> {
    let path = os::getenv(env_var);
    match path {
        Some(path) => Some(Path::new(path)),
        None => None
    }
}
<|endoftext|>


<commit_before>extern crate libc;
#[macro_use]
extern crate log;
extern crate env_logger as logger;
use log::LogLevel;
use std::io::prelude::*;
use std::str;
use std::char;
use std::ptr;
use std::path::Path;
use std::fs;
use std::fs::File;
use std::ffi::CStr;
use std::ffi::CString;

enum spsps_loc_ {}
enum spsps_errno {}

/*
#[repr(C)]
struct Loc{
	name: *const libc::c_char,
	line: u32,
	column: u32,
}
*/
//This is for the opaque C pointer
enum spsps_parser_ {}
//this will be the safe abstraction
#[derive(Debug)]
struct Parser{
	_ptr: *mut spsps_parser_,
	_fd: *mut libc::FILE
}

#[derive(Debug)]
struct Loc{
	ptr: *mut spsps_loc_
}

#[link(name = "spsps")]
extern{
	//This returns an owned Parser
	fn spsps_new(name: *const libc::c_char, stream: *mut libc::FILE) -> *mut spsps_parser_;
	fn spsps_free(parser: *mut spsps_parser_);
	//This returns an owned C string.
	fn spsps_loc_to_string(loc: *mut spsps_loc_) -> *mut libc::c_char;
	//This returns a borrowed string
	fn spsps_printchar(xch: libc::c_char) -> *mut libc::c_char;
	fn spsps_consume(parser: *mut spsps_parser_) -> libc::c_char;
	fn spsps_consume_n(parser: *mut spsps_parser_, n: libc::size_t);
	fn spsps_consume_whitespace(parser: *mut spsps_parser_);
	//Not sure what return type to use for bool
	fn spsps_eof(parser: *mut spsps_parser_) -> libc::c_int;
	//This returns an owned Loc
	fn spsps_loc(parser: *mut spsps_parser_) -> *mut spsps_loc_;
	fn spsps_peek(parser: *mut spsps_parser_) -> libc::c_char;
	fn spsps_peek_n(parser: *mut spsps_parser_, n: libc::size_t) -> *mut libc::c_char;
	fn spsps_peek_str(parser: *mut spsps_parser_, next: *const libc::c_char) -> libc::c_int;
	fn spsps_peek_and_consume(parser: *mut spsps_parser_, next: *const libc::c_char) -> libc::c_int;
}

impl Parser{
	fn from_file(name: Option<&str>, path: &Path) -> Parser {
		unsafe{
			let c_path = CString::new(path.to_str().unwrap()).unwrap().as_ptr();
			debug!("from_file path: {:?}", CString::new(path.to_str().unwrap()).unwrap());
			let fd = libc::funcs::c95::stdio::fopen(c_path,  CString::new("r").unwrap().as_ptr());
			if fd.is_null() {
				panic!("Unable to open file to parse.");
			}
			match name{
			    Some(n) => Parser{
				    	_ptr: spsps_new(CString::new(n).unwrap().as_ptr(), fd),
				    	_fd: fd
			    	},
			    None => Parser{
				    	_ptr: spsps_new(ptr::null(), fd),
				    	_fd: fd
			    	}
			}
		 }
	}

	fn consume(&self) -> String {
		unsafe {
			char::from_u32(spsps_consume(self._ptr) as u32).unwrap().to_string()
		}
	}

	fn consume_n(&self, n: usize){
		unsafe { spsps_consume_n(self._ptr, n as libc::size_t); }
	}

	fn consume_whitespace(&self){
		unsafe { spsps_consume_whitespace(self._ptr); }
	}

	fn eof(&self) -> bool{
		unsafe { spsps_eof(self._ptr) as usize != 0 }
	}

	fn get_loc(&self) -> Loc {
		unsafe{
			Loc { ptr: spsps_loc(self._ptr) }
		}
	}

	fn peek(&self) -> String{
		let c_char = unsafe { spsps_peek(self._ptr) };
		char::from_u32(c_char as u32).unwrap().to_string()
	}

	fn peek_n(&self, n: usize) -> String{
		let cstr = unsafe{ spsps_peek_n(self._ptr, n as libc::size_t) };
		let safe_cstr = unsafe { CStr::from_ptr(cstr) };
		let retval = str::from_utf8(safe_cstr.to_bytes()).unwrap_or("").to_owned();
		unsafe { libc::free(safe_cstr.as_ptr() as *mut libc::c_void); }
		return retval;
	}

	fn peek_str(&self, needle: &str) -> bool {
		unsafe { spsps_peek_str(self._ptr, CString::new(needle).unwrap().as_ptr()) != 0 }
	}

	fn peek_str_and_consume(&self, needle: &str) -> bool {
		unsafe { spsps_peek_and_consume(self._ptr, CString::new(needle).unwrap().as_ptr()) != 0 }
	}
}

impl Drop for Parser{
	fn drop(&mut self) {
		unsafe{
			libc::fclose(self._fd);
			spsps_free(self._ptr);
		}
	}
}

impl Loc{
	fn to_string(&self) -> String{
		let tmp = unsafe { CStr::from_ptr(spsps_loc_to_string(self.ptr)) };
		let retval = str::from_utf8(tmp.to_bytes()).unwrap_or("").to_owned();
		unsafe {libc::free(tmp.as_ptr() as *mut libc::c_void);}
		retval
	}
}

impl Drop for Loc{
	fn drop(&mut self) {
		unsafe{ libc::free(self.ptr as *mut libc::c_void); }
	}
}

fn test_parser() -> Parser {
	let mut f = File::create("./foo.txt").unwrap();
	f.write_all(b"This is a test.\n");
	f.sync_data();
	let test_path = Path::new("./foo.txt");
	Parser::from_file(Some("test"), test_path)
}

#[test]
fn test_from_file(){
	logger::init().unwrap();
	let p = test_parser();
	//fs::remove_file("foo.txt").unwrap();
	println!("{:?}", p);
}

#[test]
fn test_peek_n(){
	let p = test_parser();
	let first_chars = p.peek_n(3);
	println!("{:?}", first_chars);
	assert_eq!(first_chars, "Thi");
}

#[test]
fn test_peek(){
	let p = test_parser();
	let first_char = p.peek();
	println!("{:?}", first_char);
	assert_eq!(first_char, "T");
}

#[test]
fn test_consume(){
	let p = test_parser();
	let first_char = p.consume();
	println!("{:?}", first_char);
	assert_eq!(first_char, "T");
}

#[test]
fn test_loc_to_string(){
	let p = test_parser();
	let location = p.get_loc();
	let loc_string = location.to_string();
	println!("{}",loc_string);
	assert_eq!(loc_string, "test:1:1");
}

#[test]
fn test_peek_consume(){
	let parser = test_parser();
	let p = parser.peek();
	let c = parser.consume();
	assert_eq!(p, c);
}

#[test]
fn test_zzz_cleanup()
{
	fs::remove_file("foo.txt").unwrap();
}<commit_msg>Added a constructor for an unnamed parser on stdin, new()<commit_after>extern crate libc;
#[macro_use]
extern crate log;
extern crate env_logger as logger;
use log::LogLevel;
use std::io::prelude::*;
use std::str;
use std::char;
use std::ptr;
use std::path::Path;
use std::fs;
use std::fs::File;
use std::ffi::CStr;
use std::ffi::CString;

enum spsps_loc_ {}
enum spsps_errno {}

/*
#[repr(C)]
struct Loc{
	name: *const libc::c_char,
	line: u32,
	column: u32,
}
*/
//This is for the opaque C pointer
enum spsps_parser_ {}
//this will be the safe abstraction
#[derive(Debug)]
struct Parser{
	_ptr: *mut spsps_parser_,
	_fd: *mut libc::FILE
}

#[derive(Debug)]
struct Loc{
	ptr: *mut spsps_loc_
}

#[link(name = "spsps")]
extern{
	//This returns an owned Parser
	fn spsps_new(name: *const libc::c_char, stream: *mut libc::FILE) -> *mut spsps_parser_;
	fn spsps_free(parser: *mut spsps_parser_);
	//This returns an owned C string.
	fn spsps_loc_to_string(loc: *mut spsps_loc_) -> *mut libc::c_char;
	//This returns a borrowed string
	fn spsps_printchar(xch: libc::c_char) -> *mut libc::c_char;
	fn spsps_consume(parser: *mut spsps_parser_) -> libc::c_char;
	fn spsps_consume_n(parser: *mut spsps_parser_, n: libc::size_t);
	fn spsps_consume_whitespace(parser: *mut spsps_parser_);
	//Not sure what return type to use for bool
	fn spsps_eof(parser: *mut spsps_parser_) -> libc::c_int;
	//This returns an owned Loc
	fn spsps_loc(parser: *mut spsps_parser_) -> *mut spsps_loc_;
	fn spsps_peek(parser: *mut spsps_parser_) -> libc::c_char;
	fn spsps_peek_n(parser: *mut spsps_parser_, n: libc::size_t) -> *mut libc::c_char;
	fn spsps_peek_str(parser: *mut spsps_parser_, next: *const libc::c_char) -> libc::c_int;
	fn spsps_peek_and_consume(parser: *mut spsps_parser_, next: *const libc::c_char) -> libc::c_int;
}

impl Parser{
	fn new() -> Parser {
		unsafe{
			Parser{
		    	_ptr: spsps_new(ptr::null(), ptr::null_mut()),
		    	_fd: ptr::null_mut()
			}
		 }
	}
	fn from_file(name: Option<&str>, path: &Path) -> Parser {
		unsafe{
			let c_path = CString::new(path.to_str().unwrap()).unwrap().as_ptr();
			debug!("from_file path: {:?}", CString::new(path.to_str().unwrap()).unwrap());
			let fd = libc::funcs::c95::stdio::fopen(c_path,  CString::new("r").unwrap().as_ptr());
			if fd.is_null() {
				panic!("Unable to open file to parse.");
			}
			match name{
			    Some(n) => Parser{
				    	_ptr: spsps_new(CString::new(n).unwrap().as_ptr(), fd),
				    	_fd: fd
			    	},
			    None => Parser{
				    	_ptr: spsps_new(ptr::null(), fd),
				    	_fd: fd
			    	}
			}
		 }
	}

	fn consume(&self) -> String {
		unsafe {
			char::from_u32(spsps_consume(self._ptr) as u32).unwrap().to_string()
		}
	}

	fn consume_n(&self, n: usize){
		unsafe { spsps_consume_n(self._ptr, n as libc::size_t); }
	}

	fn consume_whitespace(&self){
		unsafe { spsps_consume_whitespace(self._ptr); }
	}

	fn eof(&self) -> bool{
		unsafe { spsps_eof(self._ptr) as usize != 0 }
	}

	fn get_loc(&self) -> Loc {
		unsafe{
			Loc { ptr: spsps_loc(self._ptr) }
		}
	}

	fn peek(&self) -> String{
		let c_char = unsafe { spsps_peek(self._ptr) };
		char::from_u32(c_char as u32).unwrap().to_string()
	}

	fn peek_n(&self, n: usize) -> String{
		let cstr = unsafe{ spsps_peek_n(self._ptr, n as libc::size_t) };
		let safe_cstr = unsafe { CStr::from_ptr(cstr) };
		let retval = str::from_utf8(safe_cstr.to_bytes()).unwrap_or("").to_owned();
		unsafe { libc::free(safe_cstr.as_ptr() as *mut libc::c_void); }
		return retval;
	}

	fn peek_str(&self, needle: &str) -> bool {
		unsafe { spsps_peek_str(self._ptr, CString::new(needle).unwrap().as_ptr()) != 0 }
	}

	fn peek_str_and_consume(&self, needle: &str) -> bool {
		unsafe { spsps_peek_and_consume(self._ptr, CString::new(needle).unwrap().as_ptr()) != 0 }
	}
}

impl Drop for Parser{
	fn drop(&mut self) {
		unsafe{
			if !self._ptr.is_null() { spsps_free(self._ptr); }
			if !self._fd.is_null() { libc::fclose(self._fd); }
		}
	}
}

impl Loc{
	fn to_string(&self) -> String{
		let tmp = unsafe { CStr::from_ptr(spsps_loc_to_string(self.ptr)) };
		let retval = str::from_utf8(tmp.to_bytes()).unwrap_or("").to_owned();
		unsafe {libc::free(tmp.as_ptr() as *mut libc::c_void);}
		retval
	}
}

impl Drop for Loc{
	fn drop(&mut self) {
		unsafe{ libc::free(self.ptr as *mut libc::c_void); }
	}
}

fn test_parser() -> Parser {
	let mut f = File::create("./foo.txt").unwrap();
	f.write_all(b"This is a test.\n");
	f.sync_data();
	let test_path = Path::new("./foo.txt");
	Parser::from_file(Some("test"), test_path)
}

#[test]
fn test_from_file(){
	logger::init().unwrap();
	let p = test_parser();
	//fs::remove_file("foo.txt").unwrap();
	println!("{:?}", p);
}

#[test]
fn test_peek_n(){
	let p = test_parser();
	let first_chars = p.peek_n(3);
	println!("{:?}", first_chars);
	assert_eq!(first_chars, "Thi");
}

#[test]
fn test_peek(){
	let p = test_parser();
	let first_char = p.peek();
	println!("{:?}", first_char);
	assert_eq!(first_char, "T");
}

#[test]
fn test_consume(){
	let p = test_parser();
	let first_char = p.consume();
	println!("{:?}", first_char);
	assert_eq!(first_char, "T");
}

#[test]
fn test_loc_to_string(){
	let p = test_parser();
	let location = p.get_loc();
	let loc_string = location.to_string();
	println!("{}",loc_string);
	assert_eq!(loc_string, "test:1:1");
}

#[test]
fn test_peek_consume(){
	let parser = test_parser();
	let p = parser.peek();
	let c = parser.consume();
	assert_eq!(p, c);
}

#[test]
fn test_zzz_cleanup()
{
	fs::remove_file("foo.txt").unwrap();
}<|endoftext|>


<commit_before>#[macro_use]
extern crate serde_json;
extern crate hyper;
extern crate hyper_openssl;
extern crate backtrace;

use std::thread;
use std::io::Read;
use std::fmt::Debug;
use std::panic::PanicInfo;
use std::borrow::ToOwned;
use std::sync::Arc;
use backtrace::Backtrace;

#[macro_export]
macro_rules! report_error {
    ($client:ident, $err:ident) => {
        let backtrace = backtrace::Backtrace::new();
        $client.build_report()
            .with_backtrace(&backtrace)
            .with_line_number(line!())
            .with_file_name(file!())
            .from_error(&$err)
            .send();
    }
}

#[macro_export]
macro_rules! report_panics {
    ($client:ident) => {
        std::panic::set_hook(Box::new(move |panic_info| {
            let backtrace = backtrace::Backtrace::new();
            $client.build_report()
                .with_backtrace(&backtrace)
                .from_panic(panic_info)
                .send();
        }));
    }
}

#[macro_export]
macro_rules! report_message {
    ($client:ident, $message:expr) => {
        $client.build_report()
            .with_level(rollbar::Level::INFO)
            .from_message($message)
            .send();
    }
}

#[derive(Clone)]
pub enum Level {
    CRITICAL,
    ERROR,
    WARNING,
    INFO,
    DEBUG
}

impl<'a> From<&'a str> for Level {
    fn from(s: &'a str) -> Level {
        match s {
            "critical" => Level::CRITICAL,
            "warning"  => Level::WARNING,
            "info"     => Level::INFO,
            "debug"    => Level::DEBUG,
            _          => Level::ERROR
        }
    }
}

impl ToString for Level {
    fn to_string(&self) -> String {
        match self {
            &Level::CRITICAL => "critical".to_string(),
            &Level::ERROR    => "error".to_string(),
            &Level::WARNING  => "warning".to_string(),
            &Level::INFO     => "info".to_string(),
            &Level::DEBUG    => "debug".to_string()
        }
    }
}

// https://rollbar.com/docs/api/items_post/
const URL: &'static str = "https://api.rollbar.com/api/1/item/";

pub struct ReportBuilder<'a> {
    client: &'a Client,
    send_strategy: Option<Box<Fn(Arc<hyper::Client>, String)>>,

    level: Option<Level>,
    backtrace: Option<&'a Backtrace>,
    line_number: Option<u32>,
    filename: Option<&'static str>
}

pub struct ReportPanicBuilder<'a> {
    report_builder: &'a ReportBuilder<'a>,
    panic_info: &'a PanicInfo<'a>
}

impl<'a> ReportPanicBuilder<'a> {
    pub fn send(&mut self) {
        let client = self.report_builder.client;

        match self.report_builder.send_strategy {
            Some(ref send_strategy) => {
                let http_client = client.http_client.to_owned();
                send_strategy(http_client, self.to_string());
            },
            None => { client.send(self.to_string()); }
        };
    }
}

impl<'a> ToString for ReportPanicBuilder<'a> {
    fn to_string(&self) -> String {
        let report_builder = self.report_builder;
        let client = report_builder.client;

        let payload = self.panic_info.payload();
        let error_message = match payload.downcast_ref::<&str>() {
            Some(s) => *s,
            None => match payload.downcast_ref::<String>() {
                Some(s) => &**s,
                None => "Box<Any>"
            }
        };

        let frame = match self.panic_info.location() {
            Some(location) => {
                json!({
                    "filename": location.file().to_owned(),
                    "lineno": location.line().to_owned()
                })
            },
            None => {
                json!({
                    "filename": report_builder.filename.unwrap_or(""),
                    "lineno": report_builder.line_number.unwrap_or(0),
                })
            }
        };

        json!({
            "access_token": client.access_token,
            "data": {
                "environment": client.environment,
                "body": {
                    "trace": {
                        "frames": [frame],
                        "exception": {
                            "class": thread::current().name().unwrap_or("unnamed"),
                            "message": error_message,
                            "description": match report_builder.backtrace {
                                Some(backtrace) => format!("{:?}", backtrace),
                                None => error_message.to_owned()
                            }
                        }
                    }
                },
                "level": report_builder.level
                    .to_owned()
                    .unwrap_or(Level::ERROR)
                    .to_string(),
                "language": "rust"
            }
        }).to_string()
    }
}

pub struct ReportErrorBuilder<'a, T: 'a + Debug> {
    report_builder: &'a ReportBuilder<'a>,
    error: &'a T
}

impl<'a, T: Debug> ReportErrorBuilder<'a, T> {
    pub fn send(&mut self) {
        let client = self.report_builder.client;

        match self.report_builder.send_strategy {
            Some(ref send_strategy) => {
                let http_client = client.http_client.to_owned();
                send_strategy(http_client, self.to_string());
            },
            None => { client.send(self.to_string()); }
        };
    }
}

impl<'a, T: Debug> ToString for ReportErrorBuilder<'a, T> {
    fn to_string(&self) -> String {
        let report_builder = self.report_builder;
        let client = report_builder.client;
        let error_message = format!("{:?}", self.error);

        json!({
            "access_token": client.access_token,
            "data": {
                "environment": client.environment,
                "body": {
                    "trace": {
                        "frames": [{
                            "filename": report_builder.filename.unwrap_or(""),
                            "lineno": report_builder.line_number.unwrap_or(0)
                        }],
                        "exception": {
                            "class": thread::current().name().unwrap_or("unnamed"),
                            "message": error_message,
                            "description": match report_builder.backtrace {
                                Some(backtrace) => format!("{:?}", backtrace),
                                None => error_message.to_owned()
                            }
                        }
                    }
                },
                "level": report_builder.level
                    .to_owned()
                    .unwrap_or(Level::ERROR)
                    .to_string(),
                "language": "rust"
            }
        }).to_string()
    }
}

pub struct ReportMessageBuilder<'a> {
    report_builder: &'a ReportBuilder<'a>,
    message: &'a str
}

impl<'a> ReportMessageBuilder<'a> {
    pub fn send(&mut self) {
        let client = self.report_builder.client;

        match self.report_builder.send_strategy {
            Some(ref send_strategy) => {
                let http_client = client.http_client.to_owned();
                send_strategy(http_client, self.to_string());
            },
            None => { client.send(self.to_string()); }
        };
    }
}

impl<'a> ToString for ReportMessageBuilder<'a> {
    fn to_string(&self) -> String {
        let report_builder = self.report_builder;
        let client = report_builder.client;

        json!({
            "access_token": client.access_token,
            "data": {
                "body": {
                    "environment": client.environment,
                    "message": {
                        "body": self.message
                    }
                },
                "level": report_builder.level
                    .to_owned()
                    .unwrap_or(Level::INFO)
                    .to_string()
            }
        }).to_string()
    }
}

impl<'a> ReportBuilder<'a> {
    pub fn from_panic(&'a mut self, panic_info: &'a PanicInfo) -> ReportPanicBuilder<'a> {
        ReportPanicBuilder {
            report_builder: self,
            panic_info: panic_info
        }
    }

    pub fn from_error<T: Debug>(&'a mut self, error: &'a T) -> ReportErrorBuilder<'a, T> {
        ReportErrorBuilder {
            report_builder: self,
            error: error
        }
    }

    pub fn from_message(&'a mut self, message: &'a str) -> ReportMessageBuilder<'a> {
        ReportMessageBuilder {
            report_builder: self,
            message: message
        }
    }

    pub fn with_backtrace(&mut self, backtrace: &'a Backtrace) -> &mut Self {
        self.backtrace = Some(backtrace);
        self
    }

    pub fn with_line_number(&mut self, line_number: u32) -> &mut Self {
        self.line_number = Some(line_number);
        self
    }

    pub fn with_file_name(&mut self, filename: &'static str) -> &mut Self {
        self.filename = Some(filename);
        self
    }

    pub fn with_level<T>(&'a mut self, level: T) -> &'a mut Self where T: Into<Level> {
        self.level = Some(level.into());
        self
    }

    pub fn with_send_strategy(&'a mut self, send_strategy: Box<Fn(Arc<hyper::Client>, String)>) -> &'a mut Self {
        self.send_strategy = Some(send_strategy);
        self
    }
}

pub struct Client {
    http_client: Arc<hyper::Client>,
    access_token: String,
    environment: String
}

impl Client {
    pub fn new<T: Into<String>>(access_token: T, environment: T) -> Client {
        let ssl = hyper_openssl::OpensslClient::new().unwrap();
        let connector = hyper::net::HttpsConnector::new(ssl);

        Client {
            http_client: Arc::new(hyper::Client::with_connector(connector)),
            access_token: access_token.into(),
            environment: environment.into()
        }
    }

    pub fn build_report(&self) -> ReportBuilder {
        ReportBuilder {
            client: self,
            send_strategy: None,
            level: None,
            backtrace: None,
            line_number: None,
            filename: None
        }
    }

    pub fn send(&self, payload: String) {
        let http_client = self.http_client.to_owned();

        let _ = thread::spawn(move || {
            let res = http_client.post(URL).body(&*payload).send();

            match res {
                Ok(mut res) => {
                    let mut body = String::new();
                    res.read_to_string(&mut body).unwrap();

                    println!("- Error while sending a report to Rollbar.");
                    println!("\n- The error that Rollbar raised was:\n{:?}", res);
                    println!("\n- The message that Rollbar returned was:\n{}", body);
                    println!("\n- The error that your application raised was:\n{}", payload);
                },
                Err(e) => {
                    println!("- Error while sending a report to Rollbar.");
                    println!("\n- The error that Rollbar raised was:\n{:?}", e);
                    println!("\n- The error that your application raised was:\n{}", payload);
                }
            }
        }).join();
    }
}

#[cfg(test)]
mod tests {
    extern crate serde_json;
    extern crate hyper;
    extern crate backtrace;

    use std::panic;
    use super::{Client, Level};
    use std::sync::{Arc, Mutex};
    use std::sync::mpsc::channel;
    use backtrace::Backtrace;
    use serde_json::Value;

    // TODO: rewrite this shit
    #[test]
    fn test_build_payload_from_panic() {
        let (tx, rx) = channel();

        {
            let tx = Arc::new(Mutex::new(tx));

            let client = Client::new("ACCESS_TOKEN", "ENVIRONMENT");
            panic::set_hook(Box::new(move |panic_info| {
                let backtrace = Backtrace::new();
                let payload = client.build_report()
                    .with_backtrace(&backtrace)
                    .with_level("info")
                    .from_panic(panic_info)
                    .to_string();
                let payload = Arc::new(Mutex::new(payload));
                tx.lock().unwrap().send(payload).unwrap();
            }));

            let result = panic::catch_unwind(|| {
                // just to trick the linter
                let zero = "0".parse::<i32>().unwrap();
                let _ = 1/zero;
            });
            assert!(result.is_err());
        }

        // remove the hook to avoid double panics
        let _ = panic::take_hook();

        let lock = rx.recv().unwrap();
        let error = match lock.lock() {
            Ok(guard) => guard,
            Err(poisoned) => poisoned.into_inner(),
        };

        let mut payload:          serde_json::Value = serde_json::from_str(&*error).unwrap();
        let mut expected_payload: serde_json::Value = serde_json::from_str(
            r#"{"access_token":"ACCESS_TOKEN","data":{"body":{"trace":{"exception":{"class":"tests::test_build_payload_from_panic","description":"attempt to divide by zero","message":"attempt to divide by zero"},"frames":[{"filename":"src/lib.rs","lineno":268}]}},"environment":"ENVIRONMENT","level":"info","language":"rust"}}"#
        ).unwrap();

        *expected_payload.get_mut("data").unwrap()
            .get_mut("body").unwrap()
            .get_mut("trace").unwrap()
            .get_mut("frames").unwrap() = payload.get("data").unwrap()
                                            .get("body").unwrap()
                                            .get("trace").unwrap()
                                            .get("frames").unwrap()
                                            .clone();

        // we're gonna ignore ignore the backtrace
        *payload.get_mut("data").unwrap()
            .get_mut("body").unwrap()
            .get_mut("trace").unwrap()
            .get_mut("exception").unwrap()
            .get_mut("description").unwrap() = Value::String("attempt to divide by zero".into());

        assert_eq!(expected_payload.to_string(), payload.to_string());
    }

    #[test]
    fn test_report_error() {
        let client = Client::new("ACCESS_TOKEN", "ENVIRONMENT");

        match "笑".parse::<i32>() {
            Ok(_) => { println!("lolnope"); },
            Err(e) => {
                let backtrace = Backtrace::new();

                client.build_report()
                    .with_level(Level::ERROR)
                    .with_send_strategy(Box::new(|_, payload| {
                        // we're gonna ignore the backtrace
                        let mut payload: serde_json::Value = serde_json::from_str(&*payload).unwrap();
                        let     expected_payload: serde_json::Value = serde_json::from_str(
                            r#"{"access_token":"ACCESS_TOKEN","data":{"body":{"trace":{"exception":{"class":"tests::test_report_error","description":"ParseIntError { kind: InvalidDigit }","message":"ParseIntError { kind: InvalidDigit }"},"frames":[{"filename":"","lineno":0}]}},"environment":"ENVIRONMENT","language":"rust","level":"error"}}"#
                        ).unwrap();
                        *payload.get_mut("data").unwrap()
                            .get_mut("body").unwrap()
                            .get_mut("trace").unwrap()
                            .get_mut("exception").unwrap()
                            .get_mut("description").unwrap() = Value::String("ParseIntError { kind: InvalidDigit }".into());

                        assert_eq!(expected_payload.to_string(), payload.to_string());
                    }))
                    .with_backtrace(&backtrace)
                    .from_error(&e)
                    .send();
            }
        }
    }

    #[test]
    fn test_payload_string() {
        let client = Client::new("ACCESS_TOKEN", "ENVIRONMENT");
        let payload = client.build_report()
            .with_level("info")
            .from_message("hai")
            .to_string();

        assert_eq!(
            payload,
            r#"{"access_token":"ACCESS_TOKEN","data":{"body":{"environment":"ENVIRONMENT","message":{"body":"hai"}},"level":"info"}}"#
        );
    }
}
<commit_msg>lib: fix message environment and improve tests<commit_after>#[macro_use]
extern crate serde_json;
extern crate hyper;
extern crate hyper_openssl;
extern crate backtrace;

use std::io::Read;
use std::thread;
use std::fmt::Debug;
use std::panic::PanicInfo;
use std::borrow::ToOwned;
use std::sync::Arc;
use backtrace::Backtrace;

#[macro_export]
macro_rules! report_error {
    ($client:ident, $err:ident) => {
        let backtrace = backtrace::Backtrace::new();
        let line = line!() - 2;

        $client.build_report()
            .with_backtrace(&backtrace)
            .with_line_number(line)
            .with_file_name(file!())
            .from_error(&$err)
            .send();
    }
}

#[macro_export]
macro_rules! report_panics {
    ($client:ident) => {
        std::panic::set_hook(Box::new(move |panic_info| {
            let backtrace = backtrace::Backtrace::new();
            $client.build_report()
                .with_backtrace(&backtrace)
                .from_panic(panic_info)
                .send();
        }));
    }
}

#[macro_export]
macro_rules! report_message {
    ($client:ident, $message:expr) => {
        $client.build_report()
            .with_level(rollbar::Level::INFO)
            .from_message($message)
            .send();
    }
}

#[derive(Clone)]
pub enum Level {
    CRITICAL,
    ERROR,
    WARNING,
    INFO,
    DEBUG
}

impl<'a> From<&'a str> for Level {
    fn from(s: &'a str) -> Level {
        match s {
            "critical" => Level::CRITICAL,
            "warning"  => Level::WARNING,
            "info"     => Level::INFO,
            "debug"    => Level::DEBUG,
            _          => Level::ERROR
        }
    }
}

impl ToString for Level {
    fn to_string(&self) -> String {
        match self {
            &Level::CRITICAL => "critical".to_string(),
            &Level::ERROR    => "error".to_string(),
            &Level::WARNING  => "warning".to_string(),
            &Level::INFO     => "info".to_string(),
            &Level::DEBUG    => "debug".to_string()
        }
    }
}

// https://rollbar.com/docs/api/items_post/
const URL: &'static str = "https://api.rollbar.com/api/1/item/";

pub struct ReportBuilder<'a> {
    client: &'a Client,
    send_strategy: Option<Box<Fn(Arc<hyper::Client>, String)>>,

    level: Option<Level>,
    backtrace: Option<&'a Backtrace>,
    line_number: Option<u32>,
    filename: Option<&'static str>
}

pub struct ReportPanicBuilder<'a> {
    report_builder: &'a ReportBuilder<'a>,
    panic_info: &'a PanicInfo<'a>
}

impl<'a> ReportPanicBuilder<'a> {
    pub fn send(&mut self) {
        let client = self.report_builder.client;

        match self.report_builder.send_strategy {
            Some(ref send_strategy) => {
                let http_client = client.http_client.to_owned();
                send_strategy(http_client, self.to_string());
            },
            None => { client.send(self.to_string()); }
        };
    }
}

impl<'a> ToString for ReportPanicBuilder<'a> {
    fn to_string(&self) -> String {
        let report_builder = self.report_builder;
        let client = report_builder.client;

        let payload = self.panic_info.payload();
        let error_message = match payload.downcast_ref::<&str>() {
            Some(s) => *s,
            None => match payload.downcast_ref::<String>() {
                Some(s) => &**s,
                None => "Box<Any>"
            }
        };

        let frame = match self.panic_info.location() {
            Some(location) => {
                json!({
                    "filename": location.file().to_owned(),
                    "lineno": location.line().to_owned()
                })
            },
            None => {
                json!({
                    "filename": report_builder.filename.unwrap_or(""),
                    "lineno": report_builder.line_number.unwrap_or(0),
                })
            }
        };

        json!({
            "access_token": client.access_token,
            "data": {
                "environment": client.environment,
                "body": {
                    "trace": {
                        "frames": [frame],
                        "exception": {
                            "class": thread::current().name().unwrap_or("unnamed"),
                            "message": error_message,
                            "description": match report_builder.backtrace {
                                Some(backtrace) => format!("{:?}", backtrace),
                                None => error_message.to_owned()
                            }
                        }
                    }
                },
                "level": report_builder.level
                    .to_owned()
                    .unwrap_or(Level::ERROR)
                    .to_string(),
                "language": "rust"
            }
        }).to_string()
    }
}

pub struct ReportErrorBuilder<'a, T: 'a + Debug> {
    report_builder: &'a ReportBuilder<'a>,
    error: &'a T
}

impl<'a, T: Debug> ReportErrorBuilder<'a, T> {
    pub fn send(&mut self) {
        let client = self.report_builder.client;

        match self.report_builder.send_strategy {
            Some(ref send_strategy) => {
                let http_client = client.http_client.to_owned();
                send_strategy(http_client, self.to_string());
            },
            None => { client.send(self.to_string()); }
        };
    }
}

impl<'a, T: Debug> ToString for ReportErrorBuilder<'a, T> {
    fn to_string(&self) -> String {
        let report_builder = self.report_builder;
        let client = report_builder.client;
        let error_message = format!("{:?}", self.error);

        json!({
            "access_token": client.access_token,
            "data": {
                "environment": client.environment,
                "body": {
                    "trace": {
                        "frames": [{
                            "filename": report_builder.filename.unwrap_or(""),
                            "lineno": report_builder.line_number.unwrap_or(0)
                        }],
                        "exception": {
                            "class": thread::current().name().unwrap_or("unnamed"),
                            "message": error_message,
                            "description": match report_builder.backtrace {
                                Some(backtrace) => format!("{:?}", backtrace),
                                None => error_message.to_owned()
                            }
                        }
                    }
                },
                "level": report_builder.level
                    .to_owned()
                    .unwrap_or(Level::ERROR)
                    .to_string(),
                "language": "rust"
            }
        }).to_string()
    }
}

pub struct ReportMessageBuilder<'a> {
    report_builder: &'a ReportBuilder<'a>,
    message: &'a str
}

impl<'a> ReportMessageBuilder<'a> {
    pub fn send(&mut self) {
        let client = self.report_builder.client;

        match self.report_builder.send_strategy {
            Some(ref send_strategy) => {
                let http_client = client.http_client.to_owned();
                send_strategy(http_client, self.to_string());
            },
            None => { client.send(self.to_string()); }
        };
    }
}

impl<'a> ToString for ReportMessageBuilder<'a> {
    fn to_string(&self) -> String {
        let report_builder = self.report_builder;
        let client = report_builder.client;

        json!({
            "access_token": client.access_token,
            "data": {
                "environment": client.environment,
                "body": {
                    "message": {
                        "body": self.message
                    }
                },
                "level": report_builder.level
                    .to_owned()
                    .unwrap_or(Level::INFO)
                    .to_string()
            }
        }).to_string()
    }
}

impl<'a> ReportBuilder<'a> {
    pub fn from_panic(&'a mut self, panic_info: &'a PanicInfo) -> ReportPanicBuilder<'a> {
        ReportPanicBuilder {
            report_builder: self,
            panic_info: panic_info
        }
    }

    pub fn from_error<T: Debug>(&'a mut self, error: &'a T) -> ReportErrorBuilder<'a, T> {
        ReportErrorBuilder {
            report_builder: self,
            error: error
        }
    }

    pub fn from_message(&'a mut self, message: &'a str) -> ReportMessageBuilder<'a> {
        ReportMessageBuilder {
            report_builder: self,
            message: message
        }
    }

    pub fn with_backtrace(&mut self, backtrace: &'a Backtrace) -> &mut Self {
        self.backtrace = Some(backtrace);
        self
    }

    pub fn with_line_number(&mut self, line_number: u32) -> &mut Self {
        self.line_number = Some(line_number);
        self
    }

    pub fn with_file_name(&mut self, filename: &'static str) -> &mut Self {
        self.filename = Some(filename);
        self
    }

    pub fn with_level<T>(&'a mut self, level: T) -> &'a mut Self where T: Into<Level> {
        self.level = Some(level.into());
        self
    }

    pub fn with_send_strategy(&'a mut self, send_strategy: Box<Fn(Arc<hyper::Client>, String)>) -> &'a mut Self {
        self.send_strategy = Some(send_strategy);
        self
    }
}

pub struct Client {
    http_client: Arc<hyper::Client>,
    access_token: String,
    environment: String
}

impl Client {
    pub fn new<T: Into<String>>(access_token: T, environment: T) -> Client {
        let ssl = hyper_openssl::OpensslClient::new().unwrap();
        let connector = hyper::net::HttpsConnector::new(ssl);

        Client {
            http_client: Arc::new(hyper::Client::with_connector(connector)),
            access_token: access_token.into(),
            environment: environment.into()
        }
    }

    pub fn build_report(&self) -> ReportBuilder {
        ReportBuilder {
            client: self,
            send_strategy: None,
            level: None,
            backtrace: None,
            line_number: None,
            filename: None
        }
    }

    pub fn send(&self, payload: String) {
        let http_client = self.http_client.to_owned();

        let _ = thread::spawn(move || {
            let res = http_client.post(URL).body(&*payload).send();

            match res {
                Ok(mut res) => {
                    let mut body = String::new();
                    res.read_to_string(&mut body).unwrap();

                    println!("- Error while sending a report to Rollbar.");
                    println!("\n- The error that Rollbar raised was:\n{:?}", res);
                    println!("\n- The message that Rollbar returned was:\n{}", body);
                    println!("\n- The error that your application raised was:\n{}", payload);
                },
                Err(e) => {
                    println!("- Error while sending a report to Rollbar.");
                    println!("\n- The error that Rollbar raised was:\n{:?}", e);
                    println!("\n- The error that your application raised was:\n{}", payload);
                }
            }
        }).join();
    }
}

#[cfg(test)]
mod tests {
    extern crate serde_json;
    extern crate hyper;
    extern crate backtrace;

    use std::panic;
    use super::{Client, Level};
    use std::sync::{Arc, Mutex};
    use std::sync::mpsc::channel;
    use backtrace::Backtrace;
    use serde_json::Value;

    #[test]
    fn test_report_panics() {
        let (tx, rx) = channel();

        {
            let tx = Arc::new(Mutex::new(tx));

            let client = Client::new("ACCESS_TOKEN", "ENVIRONMENT");
            panic::set_hook(Box::new(move |panic_info| {
                let backtrace = Backtrace::new();
                let payload = client.build_report()
                    .with_backtrace(&backtrace)
                    .with_level("info")
                    .from_panic(panic_info)
                    .to_string();
                let payload = Arc::new(Mutex::new(payload));
                tx.lock().unwrap().send(payload).unwrap();
            }));

            let result = panic::catch_unwind(|| {
                // just to trick the linter
                let zero = "0".parse::<i32>().unwrap();
                let _ = 1/zero;
            });
            assert!(result.is_err());
        }

        // remove the hook to avoid double panics
        let _ = panic::take_hook();

        let lock = rx.recv().unwrap();
        let payload = match lock.lock() {
            Ok(guard) => guard,
            Err(poisoned) => poisoned.into_inner(),
        };

        let payload: Value = serde_json::from_str(&*payload).unwrap();
        let mut expected_payload = json!({
            "access_token": "ACCESS_TOKEN",
            "data": {
                "environment": "ENVIRONMENT",
                "body": {
                    "trace": {
                        "frames": [{
                            "filename": "src/lib.rs",
                            "lineno": 268
                        }],
                        "exception": {
                            "class": "tests::test_report_panics",
                            "message": "attempt to divide by zero",
                            "description": "attempt to divide by zero"
                        }
                    }
                },
                "level": "info",
                "language": "rust"
            }
        });

        // copy the frames from the payload
        *expected_payload.get_mut("data").unwrap()
            .get_mut("body").unwrap()
            .get_mut("trace").unwrap()
            .get_mut("frames").unwrap() = payload.get("data").unwrap()
                                            .get("body").unwrap()
                                            .get("trace").unwrap()
                                            .get("frames").unwrap()
                                            .to_owned();

        // copy the backtrace from the payload
        *expected_payload.get_mut("data").unwrap()
            .get_mut("body").unwrap()
            .get_mut("trace").unwrap()
            .get_mut("exception").unwrap()
            .get_mut("description").unwrap() = payload.get("data").unwrap()
                                                .get("body").unwrap()
                                                .get("trace").unwrap()
                                                .get("exception").unwrap()
                                                .get("description").unwrap()
                                                .to_owned();

        assert_eq!(expected_payload.to_string(), payload.to_string());
    }

    #[test]
    fn test_report_error() {
        let client = Client::new("ACCESS_TOKEN", "ENVIRONMENT");

        match "笑".parse::<i32>() {
            Ok(_) => { assert!(false); },
            Err(e) => {
                let payload = client.build_report()
                    .with_level(Level::ERROR)
                    .from_error(&e)
                    .to_string();

                let expected_payload = json!({
                    "access_token": "ACCESS_TOKEN",
                    "data": {
                        "environment": "ENVIRONMENT",
                        "body": {
                            "trace": {
                                "frames": [{
                                    "filename": "",
                                    "lineno": 0
                                }],
                                "exception": {
                                    "class": "tests::test_report_error",
                                    "message": "ParseIntError { kind: InvalidDigit }",
                                    "description": "ParseIntError { kind: InvalidDigit }"
                                }
                            }
                        },
                        "level": "error",
                        "language": "rust"
                    }
                }).to_string();

                assert_eq!(payload, expected_payload);
            }
        }
    }

    #[test]
    fn test_report_message() {
        let client = Client::new("ACCESS_TOKEN", "ENVIRONMENT");

        let payload = client.build_report()
            .with_level("info")
            .from_message("hai")
            .to_string();

        let expected_payload = json!({
            "access_token": "ACCESS_TOKEN",
            "data": {
                "environment": "ENVIRONMENT",
                "body": {
                    "message": {
                        "body": "hai"
                    }
                },
                "level": "info"
            }
        }).to_string();

        assert_eq!(payload, expected_payload);
    }
}
<|endoftext|>


<commit_before>//! jsonrpc http server.
//!
//! ```no_run
//! extern crate jsonrpc_core;
//! extern crate jsonrpc_http_server;
//!
//! use std::sync::Arc;
//! use jsonrpc_core::*;
//! use jsonrpc_http_server::*;
//!
//! struct SayHello;
//! impl MethodCommand for SayHello {
//! 	fn execute(&self, _params: Params) -> Result<Value, Error> {
//! 		Ok(Value::String("hello".to_string()))
//! 	}
//! }
//!
//! fn main() {
//! 	let io = IoHandler::new();
//! 	io.add_method("say_hello", SayHello);
//! 	let _server = Server::start(&"127.0.0.1:3030".parse().unwrap(), Arc::new(io), AccessControlAllowOrigin::Null);
//! }
//! ```

extern crate hyper;
extern crate unicase;
extern crate jsonrpc_core as jsonrpc;

use std::sync::Arc;
use std::io::{Read, Write};
use std::net::SocketAddr;
use hyper::header::{Headers, Allow, ContentType, AccessControlAllowHeaders};
use hyper::method::Method;
use hyper::server::{Request, Response};
use hyper::{Next, Encoder, Decoder};
use hyper::net::HttpStream;
use unicase::UniCase;
use self::jsonrpc::{IoHandler};

pub use hyper::header::AccessControlAllowOrigin;

pub type ServerResult = Result<Server, RpcServerError>;

/// RPC Server startup error
#[derive(Debug)]
pub enum RpcServerError {
	IoError(std::io::Error),
	Other(hyper::error::Error),
}

impl From<hyper::error::Error> for RpcServerError {
	fn from(err: hyper::error::Error) -> Self {
		match err {
			hyper::error::Error::Io(e) => RpcServerError::IoError(e),
			e => RpcServerError::Other(e)
		}
	}
}
/// jsonrpc http request handler.
pub struct ServerHandler {
	jsonrpc_handler: Arc<IoHandler>,
	cors_domain: AccessControlAllowOrigin,
	request: String,
	response: Option<String>,
}

impl ServerHandler {
	/// Create new request handler.
	pub fn new(jsonrpc_handler: Arc<IoHandler>, cors_domain: AccessControlAllowOrigin) -> Self {
		ServerHandler {
			jsonrpc_handler: jsonrpc_handler,
			cors_domain: cors_domain,
			request: String::new(),
			response: None,
		}
	}

	fn response_headers(&self) -> Headers {
		let mut headers = Headers::new();
		headers.set(
			Allow(vec![
				Method::Options, Method::Post
			])
		);
		headers.set(ContentType::json());
		headers.set(
			AccessControlAllowHeaders(vec![
				UniCase("origin".to_owned()),
				UniCase("content-type".to_owned()),
				UniCase("accept".to_owned()),
			])
		);
		headers.set(self.cors_domain.clone());
		headers
	}
}

impl hyper::server::Handler<HttpStream> for ServerHandler {
    fn on_request(&mut self, request: Request) -> Next {
		match *request.method() {
			Method::Options => {
				self.response = Some(String::new());
				Next::write()
			},
			Method::Post => Next::read(),
			_ => Next::write(),
		}
	}

    /// This event occurs each time the `Request` is ready to be read from.
    fn on_request_readable(&mut self, decoder: &mut Decoder<HttpStream>) -> Next {
		match decoder.read_to_string(&mut self.request) {
			Ok(0) => {
				self.response = self.jsonrpc_handler.handle_request(&self.request);
				Next::write()
			}
			Ok(_) => {
				Next::read()
			}
			Err(e) => match e.kind() {
				::std::io::ErrorKind::WouldBlock => Next::read(),
				_ => {
					//trace!("Read error: {}", e);
					Next::end()
				}
			}
		}
	}

    /// This event occurs after the first time this handled signals `Next::write()`.
    fn on_response(&mut self, response: &mut Response) -> Next {
		*response.headers_mut() = self.response_headers();
		if self.response.is_none() {
			response.set_status(hyper::status::StatusCode::MethodNotAllowed);
		}
		Next::write()
	}

    /// This event occurs each time the `Response` is ready to be written to.
    fn on_response_writable(&mut self, encoder: &mut Encoder<HttpStream>) -> Next {
		if let Some(ref response) = self.response {
			encoder.write(response.as_bytes()).unwrap();
      encoder.write(b"\n").unwrap();
		}
		Next::end()
	}
}

/// jsonrpc http server.
///
/// ```no_run
/// extern crate jsonrpc_core;
/// extern crate jsonrpc_http_server;
///
/// use std::sync::Arc;
/// use jsonrpc_core::*;
/// use jsonrpc_http_server::*;
///
/// struct SayHello;
/// impl MethodCommand for SayHello {
/// 	fn execute(&self, _params: Params) -> Result<Value, Error> {
/// 		Ok(Value::String("hello".to_string()))
/// 	}
/// }
///
/// fn main() {
/// 	let io = IoHandler::new();
/// 	io.add_method("say_hello", SayHello);
/// 	let _server = Server::start(&"127.0.0.1:3030".parse().unwrap(), Arc::new(io), AccessControlAllowOrigin::Null);
/// }
/// ```
pub struct Server {
	server: Option<hyper::server::Listening>,
}

impl Server {
	pub fn start(addr: &SocketAddr, jsonrpc_handler: Arc<IoHandler>, cors_domain: AccessControlAllowOrigin) -> ServerResult {
		let srv = try!(try!(hyper::Server::http(addr)).handle(move |_| ServerHandler::new(jsonrpc_handler.clone(), cors_domain.clone())));
		Ok(Server {
			server: Some(srv),
		})
	}
}

impl Drop for Server {
	fn drop(&mut self) {
		self.server.take().unwrap().close()
	}
}
<commit_msg>Formatting<commit_after>//! jsonrpc http server.
//!
//! ```no_run
//! extern crate jsonrpc_core;
//! extern crate jsonrpc_http_server;
//!
//! use std::sync::Arc;
//! use jsonrpc_core::*;
//! use jsonrpc_http_server::*;
//!
//! struct SayHello;
//! impl MethodCommand for SayHello {
//! 	fn execute(&self, _params: Params) -> Result<Value, Error> {
//! 		Ok(Value::String("hello".to_string()))
//! 	}
//! }
//!
//! fn main() {
//! 	let io = IoHandler::new();
//! 	io.add_method("say_hello", SayHello);
//! 	let _server = Server::start(&"127.0.0.1:3030".parse().unwrap(), Arc::new(io), AccessControlAllowOrigin::Null);
//! }
//! ```

extern crate hyper;
extern crate unicase;
extern crate jsonrpc_core as jsonrpc;

use std::sync::Arc;
use std::io::{Read, Write};
use std::net::SocketAddr;
use hyper::header::{Headers, Allow, ContentType, AccessControlAllowHeaders};
use hyper::method::Method;
use hyper::server::{Request, Response};
use hyper::{Next, Encoder, Decoder};
use hyper::net::HttpStream;
use unicase::UniCase;
use self::jsonrpc::{IoHandler};

pub use hyper::header::AccessControlAllowOrigin;

pub type ServerResult = Result<Server, RpcServerError>;

/// RPC Server startup error
#[derive(Debug)]
pub enum RpcServerError {
	IoError(std::io::Error),
	Other(hyper::error::Error),
}

impl From<hyper::error::Error> for RpcServerError {
	fn from(err: hyper::error::Error) -> Self {
		match err {
			hyper::error::Error::Io(e) => RpcServerError::IoError(e),
			e => RpcServerError::Other(e)
		}
	}
}
/// jsonrpc http request handler.
pub struct ServerHandler {
	jsonrpc_handler: Arc<IoHandler>,
	cors_domain: AccessControlAllowOrigin,
	request: String,
	response: Option<String>,
}

impl ServerHandler {
	/// Create new request handler.
	pub fn new(jsonrpc_handler: Arc<IoHandler>, cors_domain: AccessControlAllowOrigin) -> Self {
		ServerHandler {
			jsonrpc_handler: jsonrpc_handler,
			cors_domain: cors_domain,
			request: String::new(),
			response: None,
		}
	}

	fn response_headers(&self) -> Headers {
		let mut headers = Headers::new();
		headers.set(
			Allow(vec![
				Method::Options, Method::Post
			])
		);
		headers.set(ContentType::json());
		headers.set(
			AccessControlAllowHeaders(vec![
				UniCase("origin".to_owned()),
				UniCase("content-type".to_owned()),
				UniCase("accept".to_owned()),
			])
		);
		headers.set(self.cors_domain.clone());
		headers
	}
}

impl hyper::server::Handler<HttpStream> for ServerHandler {
	fn on_request(&mut self, request: Request) -> Next {
		match *request.method() {
			Method::Options => {
				self.response = Some(String::new());
				Next::write()
			},
			Method::Post => Next::read(),
			_ => Next::write(),
		}
	}

	/// This event occurs each time the `Request` is ready to be read from.
	fn on_request_readable(&mut self, decoder: &mut Decoder<HttpStream>) -> Next {
		match decoder.read_to_string(&mut self.request) {
			Ok(0) => {
				self.response = self.jsonrpc_handler.handle_request(&self.request);
				Next::write()
			}
			Ok(_) => {
				Next::read()
			}
			Err(e) => match e.kind() {
				::std::io::ErrorKind::WouldBlock => Next::read(),
				_ => {
					//trace!("Read error: {}", e);
					Next::end()
				}
			}
		}
	}

		/// This event occurs after the first time this handled signals `Next::write()`.
	fn on_response(&mut self, response: &mut Response) -> Next {
		*response.headers_mut() = self.response_headers();
		if self.response.is_none() {
			response.set_status(hyper::status::StatusCode::MethodNotAllowed);
		}
		Next::write()
	}

		/// This event occurs each time the `Response` is ready to be written to.
		fn on_response_writable(&mut self, encoder: &mut Encoder<HttpStream>) -> Next {
		if let Some(ref response) = self.response {
			encoder.write(response.as_bytes()).unwrap();
			encoder.write(b"\n").unwrap();
		}
		Next::end()
	}
}

/// jsonrpc http server.
///
/// ```no_run
/// extern crate jsonrpc_core;
/// extern crate jsonrpc_http_server;
///
/// use std::sync::Arc;
/// use jsonrpc_core::*;
/// use jsonrpc_http_server::*;
///
/// struct SayHello;
/// impl MethodCommand for SayHello {
/// 	fn execute(&self, _params: Params) -> Result<Value, Error> {
/// 		Ok(Value::String("hello".to_string()))
/// 	}
/// }
///
/// fn main() {
/// 	let io = IoHandler::new();
/// 	io.add_method("say_hello", SayHello);
/// 	let _server = Server::start(&"127.0.0.1:3030".parse().unwrap(), Arc::new(io), AccessControlAllowOrigin::Null);
/// }
/// ```
pub struct Server {
	server: Option<hyper::server::Listening>,
}

impl Server {
	pub fn start(addr: &SocketAddr, jsonrpc_handler: Arc<IoHandler>, cors_domain: AccessControlAllowOrigin) -> ServerResult {
		let srv = try!(try!(hyper::Server::http(addr)).handle(move |_| ServerHandler::new(jsonrpc_handler.clone(), cors_domain.clone())));
		Ok(Server {
			server: Some(srv),
		})
	}
}

impl Drop for Server {
	fn drop(&mut self) {
		self.server.take().unwrap().close()
	}
}
<|endoftext|>


<commit_before>//! Rustic bindings to [libnotify](https://developer.gnome.org/libnotify/)
//!
//! ```rust
//!extern crate libnotify;
//!
//!fn main() {
//!    let notify = libnotify::Context::new("hello").unwrap_or_else(|e| {
//!        panic!("{}", e);
//!    });
//!    let body_text = Some("This is the optional body text.");
//!    let n = notify.new_notification("This is the summary.", body_text, None)
//!                  .unwrap_or_else(|e| panic!("{}", e));
//!    n.show().unwrap_or_else(|e| panic!("{}", e));
//!}
//!
//! ```

#![warn(missing_docs)]

extern crate libnotify_sys as sys;
extern crate glib_2_0_sys as glib;
extern crate gtypes;

use std::ffi::{CStr, CString};
use std::marker::PhantomData;
use std::fmt;
use std::error::Error;

use gtypes::{TRUE, FALSE};

/// Error that can happen on context creation
#[derive(Debug)]
pub enum ContextCreationError {
    /// Context already exists.
    AlreadyExists,
    /// Failed to initialize libnotify.
    InitError,
    NulError,
}

impl fmt::Display for ContextCreationError {
    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        use ContextCreationError::*;
        match *self {
            AlreadyExists => write!(f, "A Libnotify context already exists."),
            InitError => write!(f, "Failed to initialize libnotify."),
            NulError => write!(f, "Argument contains a nul character."),
        }
    }
}

#[derive(Debug)]
pub enum NotificationCreationError {
    NulError,
    Unknown,
}

impl fmt::Display for NotificationCreationError {
    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        use NotificationCreationError::*;
        match *self {
            NulError => write!(f, "Argument contains a nul character."),
            Unknown => write!(f, "Unknown error"),
        }
    }
}

/// The context which within libnotify operates.
///
/// Only one context can exist at a time.
pub struct Context;

impl Context {
    /// Create a new context
    ///
    /// Arguments:
    ///
    /// - app_name: The name of the application using the context
    pub fn new(app_name: &str) -> Result<Context, ContextCreationError> {
        unsafe {
            if sys::notify_is_initted() == TRUE {
                return Err(ContextCreationError::AlreadyExists);
            }
            let app_name = match CString::new(app_name) {
                Ok(name) => name,
                Err(_) => return Err(ContextCreationError::NulError),
            };
            if sys::notify_init(app_name.as_ptr()) == FALSE {
                return Err(ContextCreationError::InitError);
            }
        }
        Ok(Context)
    }
    /// Creates a new Notification.
    ///
    /// Arguments:
    ///
    /// - summary: Required summary text
    /// - body: Optional body text
    /// - icon: Optional icon theme icon name or filename
    pub fn new_notification(&self,
                            summary: &str,
                            body: Option<&str>,
                            icon: Option<&str>)
                            -> Result<Notification, NotificationCreationError> {
        let summary = match CString::new(summary) {
            Ok(cstr) => cstr,
            Err(_) => return Err(NotificationCreationError::NulError),
        };
        let body = match body {
            Some(body) => match CString::new(body) {
                Ok(cstr) => Some(cstr),
                Err(_) => return Err(NotificationCreationError::NulError),
            },
            None => None,
        };
        let body_ptr = match body {
            Some(body) => body.as_ptr(),
            None => std::ptr::null(),
        };
        let icon = match icon {
            Some(icon) => match CString::new(icon) {
                Ok(cstr) => Some(cstr),
                Err(_) => return Err(NotificationCreationError::NulError),
            },
            None => None,
        };
        let icon_ptr = match icon {
            Some(icon) => icon.as_ptr(),
            None => std::ptr::null(),
        };
        unsafe {
            let n = sys::notify_notification_new(summary.as_ptr(), body_ptr, icon_ptr);
            if n.is_null() {
                return Err(NotificationCreationError::Unknown);
            }

            Ok(Notification {
                handle: n,
                _phantom: PhantomData,
            })
        }
    }
}

impl Drop for Context {
    fn drop(&mut self) {
        unsafe {
            sys::notify_uninit();
        }
    }
}

/// A passive pop-up notification
pub struct Notification<'a> {
    handle: *mut sys::NotifyNotification,
    _phantom: PhantomData<&'a Context>,
}

impl<'a> Notification<'a> {
    /// Tells the notification server to display the notification
    /// on the screen.
    pub fn show(&'a self) -> Result<(), NotificationShowError> {
        unsafe {
            let mut err: *mut glib::GError = std::ptr::null_mut();
            sys::notify_notification_show(self.handle, &mut err);
            if !err.is_null() {
                let result = Err(NotificationShowError {
                    message: CStr::from_ptr((*err).message).to_string_lossy().into_owned(),
                });
                glib::g_error_free(err);
                return result;
            }
            return Ok(());
        }
    }
}

#[derive(Debug)]
pub struct NotificationShowError {
    message: String,
}

impl fmt::Display for NotificationShowError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "Error showing notification: {}", self.message)
    }
}

impl Error for NotificationShowError {
    fn description(&self) -> &str {
        "Notification show error"
    }
}
<commit_msg>Use fmt::Result<commit_after>//! Rustic bindings to [libnotify](https://developer.gnome.org/libnotify/)
//!
//! ```rust
//!extern crate libnotify;
//!
//!fn main() {
//!    let notify = libnotify::Context::new("hello").unwrap_or_else(|e| {
//!        panic!("{}", e);
//!    });
//!    let body_text = Some("This is the optional body text.");
//!    let n = notify.new_notification("This is the summary.", body_text, None)
//!                  .unwrap_or_else(|e| panic!("{}", e));
//!    n.show().unwrap_or_else(|e| panic!("{}", e));
//!}
//!
//! ```

#![warn(missing_docs)]

extern crate libnotify_sys as sys;
extern crate glib_2_0_sys as glib;
extern crate gtypes;

use std::ffi::{CStr, CString};
use std::marker::PhantomData;
use std::fmt;
use std::error::Error;

use gtypes::{TRUE, FALSE};

/// Error that can happen on context creation
#[derive(Debug)]
pub enum ContextCreationError {
    /// Context already exists.
    AlreadyExists,
    /// Failed to initialize libnotify.
    InitError,
    NulError,
}

impl fmt::Display for ContextCreationError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        use ContextCreationError::*;
        match *self {
            AlreadyExists => write!(f, "A Libnotify context already exists."),
            InitError => write!(f, "Failed to initialize libnotify."),
            NulError => write!(f, "Argument contains a nul character."),
        }
    }
}

#[derive(Debug)]
pub enum NotificationCreationError {
    NulError,
    Unknown,
}

impl fmt::Display for NotificationCreationError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        use NotificationCreationError::*;
        match *self {
            NulError => write!(f, "Argument contains a nul character."),
            Unknown => write!(f, "Unknown error"),
        }
    }
}

/// The context which within libnotify operates.
///
/// Only one context can exist at a time.
pub struct Context;

impl Context {
    /// Create a new context
    ///
    /// Arguments:
    ///
    /// - app_name: The name of the application using the context
    pub fn new(app_name: &str) -> Result<Context, ContextCreationError> {
        unsafe {
            if sys::notify_is_initted() == TRUE {
                return Err(ContextCreationError::AlreadyExists);
            }
            let app_name = match CString::new(app_name) {
                Ok(name) => name,
                Err(_) => return Err(ContextCreationError::NulError),
            };
            if sys::notify_init(app_name.as_ptr()) == FALSE {
                return Err(ContextCreationError::InitError);
            }
        }
        Ok(Context)
    }
    /// Creates a new Notification.
    ///
    /// Arguments:
    ///
    /// - summary: Required summary text
    /// - body: Optional body text
    /// - icon: Optional icon theme icon name or filename
    pub fn new_notification(&self,
                            summary: &str,
                            body: Option<&str>,
                            icon: Option<&str>)
                            -> Result<Notification, NotificationCreationError> {
        let summary = match CString::new(summary) {
            Ok(cstr) => cstr,
            Err(_) => return Err(NotificationCreationError::NulError),
        };
        let body = match body {
            Some(body) => match CString::new(body) {
                Ok(cstr) => Some(cstr),
                Err(_) => return Err(NotificationCreationError::NulError),
            },
            None => None,
        };
        let body_ptr = match body {
            Some(body) => body.as_ptr(),
            None => std::ptr::null(),
        };
        let icon = match icon {
            Some(icon) => match CString::new(icon) {
                Ok(cstr) => Some(cstr),
                Err(_) => return Err(NotificationCreationError::NulError),
            },
            None => None,
        };
        let icon_ptr = match icon {
            Some(icon) => icon.as_ptr(),
            None => std::ptr::null(),
        };
        unsafe {
            let n = sys::notify_notification_new(summary.as_ptr(), body_ptr, icon_ptr);
            if n.is_null() {
                return Err(NotificationCreationError::Unknown);
            }

            Ok(Notification {
                handle: n,
                _phantom: PhantomData,
            })
        }
    }
}

impl Drop for Context {
    fn drop(&mut self) {
        unsafe {
            sys::notify_uninit();
        }
    }
}

/// A passive pop-up notification
pub struct Notification<'a> {
    handle: *mut sys::NotifyNotification,
    _phantom: PhantomData<&'a Context>,
}

impl<'a> Notification<'a> {
    /// Tells the notification server to display the notification
    /// on the screen.
    pub fn show(&'a self) -> Result<(), NotificationShowError> {
        unsafe {
            let mut err: *mut glib::GError = std::ptr::null_mut();
            sys::notify_notification_show(self.handle, &mut err);
            if !err.is_null() {
                let result = Err(NotificationShowError {
                    message: CStr::from_ptr((*err).message).to_string_lossy().into_owned(),
                });
                glib::g_error_free(err);
                return result;
            }
            return Ok(());
        }
    }
}

#[derive(Debug)]
pub struct NotificationShowError {
    message: String,
}

impl fmt::Display for NotificationShowError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "Error showing notification: {}", self.message)
    }
}

impl Error for NotificationShowError {
    fn description(&self) -> &str {
        "Notification show error"
    }
}
<|endoftext|>


<commit_before>extern crate itertools;

use itertools::Itertools;

use std::collections::HashMap;
use std::fmt;
use std::str::FromStr;
use std::vec;

mod prim;

#[derive(Debug, Clone)]
pub enum EvalError {
    StackUnderflow,
    NotFound(String),
    TypeMismatch,
}

impl fmt::Display for EvalError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            EvalError::StackUnderflow => write!(f, "stack underflow"),
            EvalError::NotFound(ref name) => write!(f, "could not find `{}`", name),
            EvalError::TypeMismatch => write!(f, "type mismatch"),
        }
    }
}

pub type EvalResult<T> = Result<T, EvalError>;

#[derive(Copy, Clone, Debug, PartialEq)]
pub enum Value {
    Bool(bool),
    Number(i32),
}

impl fmt::Display for Value {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            Value::Bool(x) => write!(f, "{}", x),
            Value::Number(x) => write!(f, "{}", x),
        }
    }
}

// The type `fn(&mut T, &mut V) -> V` does not implement `Debug`, `Clone`, or
// `PartialEq`, so to enable `#[derive(..)]` to work for `Term`, we implement
// these for a wrapper struct.
#[derive(Copy)]
pub struct Prim(pub fn(&mut Stack, &Words) -> EvalResult<()>);

impl fmt::Debug for Prim {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "Prim(..)")
    }
}

impl Clone for Prim {
    fn clone(&self) -> Prim { Prim(self.0) }
}

impl PartialEq for Prim {
    fn eq(&self, other: &Prim) -> bool {
        &self.0 as *const _ == &other.0 as *const _
    }
}

#[derive(Clone, Debug, PartialEq)]
pub enum Term {
    Push(Value),
    Quote(Stack),
    Call(String),
    Prim(Prim),
}

impl Term {
    pub fn prim(f: fn(&mut Stack, &Words) -> EvalResult<()>) -> Term {
        Term::Prim(Prim(f))
    }
}

impl fmt::Display for Term {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            Term::Push(value) => write!(f, "{}", value),
            Term::Quote(ref stack) => write!(f, "[ {} ]", stack),
            Term::Call(ref name) => write!(f, "{}", name),
            Term::Prim(_) => write!(f, "<prim>"),
        }
    }
}

#[derive(Clone, Debug)]
pub struct Words {
    defs: HashMap<String, Term>,
}

impl Words {
    pub fn empty() -> Words {
        Words {
            defs: HashMap::new(),
        }
    }

    pub fn standard() -> Words {
        let mut words = Words::empty();

        words.define("words", Term::prim(prim::words)); // (A ~> A)

        words.define("dup", Term::prim(prim::dup)); // (A b -> A b b)
        words.define("pop", Term::prim(prim::pop)); // (A b -> A)
        words.define("swap", Term::prim(prim::swap)); // (A b c -> A c b)
        words.define("apply", Term::prim(prim::apply)); // (A (A -> B) -> B)
        words.define("quote", Term::prim(prim::quote)); // (A b -> A (C -> C b))
        words.define("compose", Term::prim(prim::compose)); // (A (B -> C) (C -> D) -> A (B -> D)))

        words.define("if", Term::prim(prim::if_)); // (A bool (A -> B) (A -> B) -> B)

        words.define("true", Term::Push(Value::Bool(true))); // (A -> A bool)
        words.define("false", Term::Push(Value::Bool(false))); // (A -> A bool)

        words.define("eq", Term::prim(prim::eq)); // (A num num -> A bool)
        words.define("and", Term::prim(prim::and)); // (A bool bool -> A bool)
        words.define("or", Term::prim(prim::or)); // (A bool bool -> A bool)
        words.define("not", Term::prim(prim::not)); // (A bool -> A bool)

        words.define("+", Term::prim(prim::add)); // (A num num -> A num)
        words.define("-", Term::prim(prim::sub)); // (A num num -> A num)
        words.define("*", Term::prim(prim::mul)); // (A num num -> A num)
        words.define("/", Term::prim(prim::div)); // (A num num -> A num)
        words.define("%", Term::prim(prim::rem)); // (A num num -> A num)

        words
    }

    pub fn define<S: Into<String>>(&mut self, name: S, x: Term) {
        self.defs.insert(name.into(), x);
    }

    pub fn lookup(&self, name: &str) -> Option<&Term> {
        self.defs.get(name)
    }
}

#[derive(Clone, Debug, PartialEq)]
pub struct Stack {
    terms: Vec<Term>,
}

impl Stack {
    pub fn new(terms: Vec<Term>) -> Stack {
        Stack { terms: terms }
    }

    fn push(&mut self, term: Term) {
        self.terms.push(term);
    }

    fn pop(&mut self) -> EvalResult<Term> {
        match self.terms.pop() {
            Some(term) => Ok(term),
            None => Err(EvalError::StackUnderflow),
        }
    }

    fn pop_bool(&mut self) -> EvalResult<bool> {
        match try!(self.pop()) {
            Term::Push(Value::Bool(x)) => Ok(x),
            _ => Err(EvalError::TypeMismatch),
        }
    }

    fn pop_number(&mut self) -> EvalResult<i32> {
        match try!(self.pop()) {
            Term::Push(Value::Number(x)) => Ok(x),
            _ => Err(EvalError::TypeMismatch),
        }
    }

    fn pop_quote(&mut self) -> EvalResult<Stack> {
        match try!(self.pop()) {
            Term::Quote(stack) => Ok(stack),
            _ => Err(EvalError::TypeMismatch),
        }
    }

    fn peek(&mut self) -> EvalResult<&Term> {
        match self.terms.last() {
            Some(term) => Ok(term),
            None => Err(EvalError::StackUnderflow),
        }
    }

    fn eval_name(&mut self, words: &Words, name: String) -> EvalResult<()> {
        match words.lookup(&name) {
            Some(term) => self.eval_term(words, term.clone()),
            None => Err(EvalError::NotFound(name)),
        }
    }

    fn eval_term(&mut self, words: &Words, term: Term) -> EvalResult<()> {
        match term {
            Term::Push(value) => { self.push(Term::Push(value)); Ok(()) },
            Term::Quote(stack) => { self.push(Term::Quote(stack)); Ok(()) },
            Term::Call(name) => self.eval_name(words, name),
            Term::Prim(Prim(f)) => f(self, words),
        }
    }

    fn apply(&mut self, words: &Words, quote: Stack) -> EvalResult<()> {
        let mut quote = quote.into_iter();
        while let Some(term) = quote.next() {
            try!(self.eval_term(words, term))
        }
        Ok(())
    }
}

pub fn eval(stack: Stack, words: &Words) -> EvalResult<Stack> {
    let mut result = Stack::new(vec![]);
    try!(result.apply(words, stack));
    Ok(result)
}

impl FromStr for Stack {
    type Err = String;

    fn from_str(src: &str) -> Result<Stack, String> {
        fn parse_tokens<'a, I>(tokens: &mut I, in_quote: bool) -> Result<Stack, String> where
            I: Iterator<Item = &'a str>,
        {
            let mut terms = vec![];

            while let Some(token) = tokens.next() {
                match token {
                    "[" => {
                        terms.push(Term::Quote(
                            try!(parse_tokens(tokens, true))
                        ))
                    },
                    "]" => {
                        if in_quote {
                            return Ok(Stack::new(terms));
                        } else {
                            return Err("found unexpected `]`".to_string());
                        }
                    },
                    token => match token.parse() {
                        Ok(x) => terms.push(Term::Push(Value::Number(x))),
                        Err(_) => terms.push(Term::Call(token.to_string())),
                    },
                }
            }
            if in_quote {
                Err("expected closing `]`".to_string())
            } else {
                Ok(Stack::new(terms))
            }
        }

        parse_tokens(&mut src.split_whitespace(), false)
    }
}

pub type Terms = vec::IntoIter<Term>;

impl IntoIterator for Stack {
    type Item = Term;
    type IntoIter = Terms;

    fn into_iter(self) -> Terms {
        self.terms.into_iter()
    }
}

impl fmt::Display for Stack {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", self.terms.iter().format(" ", |t, f| f(t)))
    }
}

#[cfg(test)]
mod tests {
    mod prim {
        mod eq {
            use Prim;
            use prim;

            #[test]
            fn test_same() {
                assert_eq!(Prim(prim::eq), Prim(prim::eq));
                assert_eq!(Prim(prim::add), Prim(prim::add));
            }

            #[test]
            fn test_different() {
                assert!(Prim(prim::eq) != Prim(prim::add));
                assert!(Prim(prim::add) != Prim(prim::eq));
            }
        }
    }

    mod stack {
        mod parse {
            use Stack;
            use Term::*;
            use Value::*;

            #[test]
            fn test_number() {
                assert_eq!("123".parse(), Ok(Stack::new(vec![Push(Number(123))])));
                assert_eq!(" 34 ".parse(), Ok(Stack::new(vec![Push(Number(34))])));
            }

            #[test]
            fn test_name() {
                assert_eq!("foo".parse(), Ok(Stack::new(vec![Call("foo".to_string())])));
                assert_eq!(" * ".parse(), Ok(Stack::new(vec![Call("*".to_string())])));
            }

            #[test]
            fn test_quote() {
                assert_eq!("[ foo ]".parse(),
                    Ok(Stack::new(vec![
                        Quote(Stack::new(vec![Call("foo".to_string())]))
                    ])));

                assert_eq!("[ 1 2 * + ]".parse(),
                    Ok(Stack::new(vec![
                        Quote(Stack::new(vec![
                            Push(Number(1)),
                            Push(Number(2)),
                            Call("*".to_string()),
                            Call("+".to_string()),
                        ])),
                    ])));
            }

            #[test]
            fn test_compose() {
                assert_eq!(" 1 2 [ foo ]  * +".parse(),
                    Ok(Stack::new(vec![
                        Push(Number(1)),
                        Push(Number(2)),
                        Quote(Stack::new(vec![Call("foo".to_string())])),
                        Call("*".to_string()),
                        Call("+".to_string()),
                    ])));
                assert_eq!(" 1 2 [ foo [ -23 bar ] ]  * +".parse(),
                    Ok(Stack::new(vec![
                        Push(Number(1)),
                        Push(Number(2)),
                        Quote(Stack::new(vec![
                            Call("foo".to_string()),
                            Quote(Stack::new(vec![
                                Push(Number(-23)),
                                Call("bar".to_string())
                            ]))
                        ])),
                        Call("*".to_string()),
                        Call("+".to_string()),
                    ])));
            }
        }
    }
}
<commit_msg>Remove IntoIter implementation<commit_after>extern crate itertools;

use itertools::Itertools;

use std::collections::HashMap;
use std::fmt;
use std::str::FromStr;

mod prim;

#[derive(Debug, Clone)]
pub enum EvalError {
    StackUnderflow,
    NotFound(String),
    TypeMismatch,
}

impl fmt::Display for EvalError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            EvalError::StackUnderflow => write!(f, "stack underflow"),
            EvalError::NotFound(ref name) => write!(f, "could not find `{}`", name),
            EvalError::TypeMismatch => write!(f, "type mismatch"),
        }
    }
}

pub type EvalResult<T> = Result<T, EvalError>;

#[derive(Copy, Clone, Debug, PartialEq)]
pub enum Value {
    Bool(bool),
    Number(i32),
}

impl fmt::Display for Value {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            Value::Bool(x) => write!(f, "{}", x),
            Value::Number(x) => write!(f, "{}", x),
        }
    }
}

// The type `fn(&mut T, &mut V) -> V` does not implement `Debug`, `Clone`, or
// `PartialEq`, so to enable `#[derive(..)]` to work for `Term`, we implement
// these for a wrapper struct.
#[derive(Copy)]
pub struct Prim(pub fn(&mut Stack, &Words) -> EvalResult<()>);

impl fmt::Debug for Prim {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "Prim(..)")
    }
}

impl Clone for Prim {
    fn clone(&self) -> Prim { Prim(self.0) }
}

impl PartialEq for Prim {
    fn eq(&self, other: &Prim) -> bool {
        &self.0 as *const _ == &other.0 as *const _
    }
}

#[derive(Clone, Debug, PartialEq)]
pub enum Term {
    Push(Value),
    Quote(Stack),
    Call(String),
    Prim(Prim),
}

impl Term {
    pub fn prim(f: fn(&mut Stack, &Words) -> EvalResult<()>) -> Term {
        Term::Prim(Prim(f))
    }
}

impl fmt::Display for Term {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            Term::Push(value) => write!(f, "{}", value),
            Term::Quote(ref stack) => write!(f, "[ {} ]", stack),
            Term::Call(ref name) => write!(f, "{}", name),
            Term::Prim(_) => write!(f, "<prim>"),
        }
    }
}

#[derive(Clone, Debug)]
pub struct Words {
    defs: HashMap<String, Term>,
}

impl Words {
    pub fn empty() -> Words {
        Words {
            defs: HashMap::new(),
        }
    }

    pub fn standard() -> Words {
        let mut words = Words::empty();

        words.define("words", Term::prim(prim::words)); // (A ~> A)

        words.define("dup", Term::prim(prim::dup)); // (A b -> A b b)
        words.define("pop", Term::prim(prim::pop)); // (A b -> A)
        words.define("swap", Term::prim(prim::swap)); // (A b c -> A c b)
        words.define("apply", Term::prim(prim::apply)); // (A (A -> B) -> B)
        words.define("quote", Term::prim(prim::quote)); // (A b -> A (C -> C b))
        words.define("compose", Term::prim(prim::compose)); // (A (B -> C) (C -> D) -> A (B -> D)))

        words.define("if", Term::prim(prim::if_)); // (A bool (A -> B) (A -> B) -> B)

        words.define("true", Term::Push(Value::Bool(true))); // (A -> A bool)
        words.define("false", Term::Push(Value::Bool(false))); // (A -> A bool)

        words.define("eq", Term::prim(prim::eq)); // (A num num -> A bool)
        words.define("and", Term::prim(prim::and)); // (A bool bool -> A bool)
        words.define("or", Term::prim(prim::or)); // (A bool bool -> A bool)
        words.define("not", Term::prim(prim::not)); // (A bool -> A bool)

        words.define("+", Term::prim(prim::add)); // (A num num -> A num)
        words.define("-", Term::prim(prim::sub)); // (A num num -> A num)
        words.define("*", Term::prim(prim::mul)); // (A num num -> A num)
        words.define("/", Term::prim(prim::div)); // (A num num -> A num)
        words.define("%", Term::prim(prim::rem)); // (A num num -> A num)

        words
    }

    pub fn define<S: Into<String>>(&mut self, name: S, x: Term) {
        self.defs.insert(name.into(), x);
    }

    pub fn lookup(&self, name: &str) -> Option<&Term> {
        self.defs.get(name)
    }
}

#[derive(Clone, Debug, PartialEq)]
pub struct Stack {
    terms: Vec<Term>,
}

impl Stack {
    pub fn new(terms: Vec<Term>) -> Stack {
        Stack { terms: terms }
    }

    fn push(&mut self, term: Term) {
        self.terms.push(term);
    }

    fn pop(&mut self) -> EvalResult<Term> {
        match self.terms.pop() {
            Some(term) => Ok(term),
            None => Err(EvalError::StackUnderflow),
        }
    }

    fn pop_bool(&mut self) -> EvalResult<bool> {
        match try!(self.pop()) {
            Term::Push(Value::Bool(x)) => Ok(x),
            _ => Err(EvalError::TypeMismatch),
        }
    }

    fn pop_number(&mut self) -> EvalResult<i32> {
        match try!(self.pop()) {
            Term::Push(Value::Number(x)) => Ok(x),
            _ => Err(EvalError::TypeMismatch),
        }
    }

    fn pop_quote(&mut self) -> EvalResult<Stack> {
        match try!(self.pop()) {
            Term::Quote(stack) => Ok(stack),
            _ => Err(EvalError::TypeMismatch),
        }
    }

    fn peek(&mut self) -> EvalResult<&Term> {
        match self.terms.last() {
            Some(term) => Ok(term),
            None => Err(EvalError::StackUnderflow),
        }
    }

    fn eval_name(&mut self, words: &Words, name: String) -> EvalResult<()> {
        match words.lookup(&name) {
            Some(term) => self.eval_term(words, term.clone()),
            None => Err(EvalError::NotFound(name)),
        }
    }

    fn eval_term(&mut self, words: &Words, term: Term) -> EvalResult<()> {
        match term {
            Term::Push(value) => { self.push(Term::Push(value)); Ok(()) },
            Term::Quote(stack) => { self.push(Term::Quote(stack)); Ok(()) },
            Term::Call(name) => self.eval_name(words, name),
            Term::Prim(Prim(f)) => f(self, words),
        }
    }

    fn apply(&mut self, words: &Words, quote: Stack) -> EvalResult<()> {
        let mut terms = quote.terms.into_iter();
        while let Some(term) = terms.next() {
            try!(self.eval_term(words, term))
        }
        Ok(())
    }
}

pub fn eval(stack: Stack, words: &Words) -> EvalResult<Stack> {
    let mut result = Stack::new(vec![]);
    try!(result.apply(words, stack));
    Ok(result)
}

impl FromStr for Stack {
    type Err = String;

    fn from_str(src: &str) -> Result<Stack, String> {
        fn parse_tokens<'a, I>(tokens: &mut I, in_quote: bool) -> Result<Stack, String> where
            I: Iterator<Item = &'a str>,
        {
            let mut terms = vec![];

            while let Some(token) = tokens.next() {
                match token {
                    "[" => {
                        terms.push(Term::Quote(
                            try!(parse_tokens(tokens, true))
                        ))
                    },
                    "]" => {
                        if in_quote {
                            return Ok(Stack::new(terms));
                        } else {
                            return Err("found unexpected `]`".to_string());
                        }
                    },
                    token => match token.parse() {
                        Ok(x) => terms.push(Term::Push(Value::Number(x))),
                        Err(_) => terms.push(Term::Call(token.to_string())),
                    },
                }
            }

            if in_quote {
                Err("expected closing `]`".to_string())
            } else {
                Ok(Stack::new(terms))
            }
        }

        parse_tokens(&mut src.split_whitespace(), false)
    }
}

impl fmt::Display for Stack {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", self.terms.iter().format(" ", |t, f| f(t)))
    }
}

#[cfg(test)]
mod tests {
    mod prim {
        mod eq {
            use Prim;
            use prim;

            #[test]
            fn test_same() {
                assert_eq!(Prim(prim::eq), Prim(prim::eq));
                assert_eq!(Prim(prim::add), Prim(prim::add));
            }

            #[test]
            fn test_different() {
                assert!(Prim(prim::eq) != Prim(prim::add));
                assert!(Prim(prim::add) != Prim(prim::eq));
            }
        }
    }

    mod stack {
        mod parse {
            use Stack;
            use Term::*;
            use Value::*;

            #[test]
            fn test_number() {
                assert_eq!("123".parse(), Ok(Stack::new(vec![Push(Number(123))])));
                assert_eq!(" 34 ".parse(), Ok(Stack::new(vec![Push(Number(34))])));
            }

            #[test]
            fn test_name() {
                assert_eq!("foo".parse(), Ok(Stack::new(vec![Call("foo".to_string())])));
                assert_eq!(" * ".parse(), Ok(Stack::new(vec![Call("*".to_string())])));
            }

            #[test]
            fn test_quote() {
                assert_eq!("[ foo ]".parse(),
                    Ok(Stack::new(vec![
                        Quote(Stack::new(vec![Call("foo".to_string())]))
                    ])));

                assert_eq!("[ 1 2 * + ]".parse(),
                    Ok(Stack::new(vec![
                        Quote(Stack::new(vec![
                            Push(Number(1)),
                            Push(Number(2)),
                            Call("*".to_string()),
                            Call("+".to_string()),
                        ])),
                    ])));
            }

            #[test]
            fn test_compose() {
                assert_eq!(" 1 2 [ foo ]  * +".parse(),
                    Ok(Stack::new(vec![
                        Push(Number(1)),
                        Push(Number(2)),
                        Quote(Stack::new(vec![Call("foo".to_string())])),
                        Call("*".to_string()),
                        Call("+".to_string()),
                    ])));
                assert_eq!(" 1 2 [ foo [ -23 bar ] ]  * +".parse(),
                    Ok(Stack::new(vec![
                        Push(Number(1)),
                        Push(Number(2)),
                        Quote(Stack::new(vec![
                            Call("foo".to_string()),
                            Quote(Stack::new(vec![
                                Push(Number(-23)),
                                Call("bar".to_string())
                            ]))
                        ])),
                        Call("*".to_string()),
                        Call("+".to_string()),
                    ])));
            }
        }
    }
}
<|endoftext|>