<commit_before>extern crate libc;

use libc::{c_char, c_uint, c_int};
use std::sync::Arc;
use ffi::{QrsEngine, QObject};
use std::path::Path;
use std::convert::AsRef;

/* Re-exports */

pub use variant::{Variant, FromQVariant, ToQVariant, unpack_varlist};
pub use ffi::QVariant as OpaqueQVariant;

/* Submodules */

#[allow(dead_code)]
pub mod ffi;
mod macros;
pub mod variant;

struct EngineInternal {
    p: *mut QrsEngine,
}

/* Hack to get invoke working. Need to figure out better way for invokes anyway.. */
unsafe impl Send for EngineInternal { }
unsafe impl Sync for EngineInternal { }

impl Drop for EngineInternal {
    fn drop(&mut self) {
        unsafe { ffi::qmlrs_destroy_engine(self.p); }
    }
}

pub struct Engine {
    i: Arc<EngineInternal>,
}

impl Engine {
    pub fn new() -> Engine {
        let p = unsafe { ffi::qmlrs_create_engine() };
        assert!(!p.is_null());

        let i = Arc::new(EngineInternal {
            p: p,
        });

        Engine {
            i: i
        }
    }

    pub fn new_headless() -> Engine {
        let p = unsafe { ffi::qmlrs_create_engine_headless() };
        assert!(!p.is_null());

        let i = Arc::new(EngineInternal {
            p: p,
        });

        Engine {
            i: i
        }
    }

    pub fn load_url(&mut self, path: &str) {
        unsafe {
            ffi::qmlrs_engine_load_url(self.i.p, path.as_ptr() as *const c_char,
                                       path.len() as c_uint);
        }
    }

    pub fn load_data(&mut self, data: &str) {
        unsafe {
            ffi::qmlrs_engine_load_from_data(self.i.p, data.as_ptr() as *const c_char,
                                             data.len() as c_uint);
        }
    }

    pub fn load_local_file(&mut self, path: &str) {
        unsafe {
            ffi::qmlrs_engine_load_file(self.i.p, path.as_ptr() as *const c_char,
                                       path.len() as c_uint);
        }
    }

    pub fn exec(self) {
        unsafe { ffi::qmlrs_app_exec(); }
    }

    pub fn set_property(&mut self, name: &str, obj: &Object) {
        unsafe {
            ffi::qmlrs_engine_set_property(self.i.p, name.as_ptr() as *const c_char,
                                           name.len() as c_uint, obj.p);
        }
    }
}

#[allow(missing_copy_implementations)]
pub struct MetaObject {
    p: *mut ffi::QrsMetaObject
}

impl MetaObject {
    pub fn new(name: &str, fun: ffi::SlotFunction) -> MetaObject {
        let p = unsafe { ffi::qmlrs_metaobject_create(name.as_ptr() as *const c_char,name.len() as c_uint,fun) };
        assert!(!p.is_null());

        MetaObject { p: p }
    }

    pub fn add_signal(&mut self, sig: &str) -> isize {
        unsafe { ffi::qmlrs_metaobject_add_signal(self.p,sig.as_ptr() as *const c_char,sig.len() as c_uint) as isize }
    }

    pub fn add_property(&mut self, name: &str, ty: &str, signal: Option<&str>) {
        unsafe {
            if let Some(sig) = signal {
                ffi::qmlrs_metaobject_add_property(self.p,name.as_ptr() as *const c_char,name.len() as c_uint,
                                                   ty.as_ptr() as *const c_char, ty.len() as c_uint,
                                                   sig.as_ptr() as *const c_char, sig.len() as c_uint);
            } else {
                let s = "";
                ffi::qmlrs_metaobject_add_property(self.p,name.as_ptr() as *const c_char,name.len() as c_uint,
                                                   ty.as_ptr() as *const c_char,ty.len() as c_uint,
                                                   s.as_ptr() as *const c_char, 0);
            }
        }
    }

    pub fn add_slot(&mut self, sig: &str) -> isize {
        unsafe { ffi::qmlrs_metaobject_add_slot(self.p,sig.as_ptr() as *const c_char,sig.len() as c_uint) as isize }
    }

    pub fn add_method(&mut self, sig: &str, ret: &str) -> isize {
        unsafe {
            ffi::qmlrs_metaobject_add_method(self.p,sig.as_ptr() as *const c_char,sig.len() as c_uint,
                                             ret.as_ptr() as *const c_char,ret.len() as c_uint) as isize
        }
    }

    pub fn instantiate(&mut self) -> Object {
        Object{ p: unsafe {
            ffi::qmlrs_metaobject_instantiate(self.p)
        } }
    }
}

pub struct Object {
    p: *mut ffi::QObject
}

unsafe impl Send for Object { }
unsafe impl Sync for Object { }

impl Object {
    pub fn from_ptr(p: *mut ffi::QObject) -> Object {
        Object{ p: p }
    }

    pub fn set_property(&mut self, name: &str, value: Variant) {
        unsafe {
            let var = ffi::qmlrs_variant_create();
            value.to_qvariant(var);

            ffi::qmlrs_object_set_property(self.p,name.as_ptr() as *const c_char,name.len() as c_uint,var);
            ffi::qmlrs_variant_destroy(var);
        }
    }

    pub fn get_property(&self, name: &str) -> Variant {
        unsafe {
            let var = ffi::qmlrs_variant_create();

            ffi::qmlrs_object_get_property(self.p,name.as_ptr() as *const c_char,name.len() as c_uint,var);
            let ret = Variant::from_qvariant(var);

            ffi::qmlrs_variant_destroy(var);
            ret.unwrap()
        }
    }

    pub fn call(&self, id: isize, args: &[Variant]) -> Option<Variant> {
        unsafe {
            let vl = ffi::qmlrs_varlist_create();
            let ret = ffi::qmlrs_variant_create();

            for v in args {
                let var = ffi::qmlrs_varlist_push(vl);
                v.to_qvariant(var);
            }

            ffi::qmlrs_object_call(self.p, id as c_int, vl, ret);
            ffi::qmlrs_varlist_destroy(vl);

            let r = Variant::from_qvariant(ret);
            ffi::qmlrs_variant_destroy(ret);

            r
        }
    }

    pub fn emit(&self, id: isize, args: &[Variant]) {
        unsafe {
            let vl = ffi::qmlrs_varlist_create();

            for v in args {
                let var = ffi::qmlrs_varlist_push(vl);
                v.to_qvariant(var);
            }

            ffi::qmlrs_object_emit(self.p, id as c_int, vl);
            ffi::qmlrs_varlist_destroy(vl);
        }
    }

    pub fn delete_later(&mut self) {
        unsafe { ffi::qmlrs_object_delete_later(self.p) };
    }

    pub fn as_ptr(&mut self) -> *mut QObject {
        self.p
    }
}

pub fn register_singleton_type(module: &str, major: isize, minor: isize, typenam: &str, fun: ffi::SingletonFunction) {
    unsafe {
        ffi::qmlrs_register_singleton_type(module.as_ptr() as *const c_char,module.len() as c_uint,
                                           major as c_int,minor as c_int,
                                           typenam.as_ptr() as *const c_char,typenam.len() as c_uint,
                                           fun);
    }
}

pub fn clear_type_registrations() {
    unsafe { ffi::clear_type_registrations() }
}

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

    #[test]
    fn test_create_engine() {
        Engine::new_headless();
    }
}
<commit_msg>typos<commit_after>extern crate libc;

use libc::{c_char, c_uint, c_int};
use std::sync::Arc;
use ffi::{QrsEngine, QObject};

/* Re-exports */

pub use variant::{Variant, FromQVariant, ToQVariant, unpack_varlist};
pub use ffi::QVariant as OpaqueQVariant;

/* Submodules */

#[allow(dead_code)]
pub mod ffi;
mod macros;
pub mod variant;

struct EngineInternal {
    p: *mut QrsEngine,
}

/* Hack to get invoke working. Need to figure out better way for invokes anyway.. */
unsafe impl Send for EngineInternal { }
unsafe impl Sync for EngineInternal { }

impl Drop for EngineInternal {
    fn drop(&mut self) {
        unsafe { ffi::qmlrs_destroy_engine(self.p); }
    }
}

pub struct Engine {
    i: Arc<EngineInternal>,
}

impl Engine {
    pub fn new() -> Engine {
        let p = unsafe { ffi::qmlrs_create_engine() };
        assert!(!p.is_null());

        let i = Arc::new(EngineInternal {
            p: p,
        });

        Engine {
            i: i
        }
    }

    pub fn new_headless() -> Engine {
        let p = unsafe { ffi::qmlrs_create_engine_headless() };
        assert!(!p.is_null());

        let i = Arc::new(EngineInternal {
            p: p,
        });

        Engine {
            i: i
        }
    }

    pub fn load_url(&mut self, path: &str) {
        unsafe {
            ffi::qmlrs_engine_load_url(self.i.p, path.as_ptr() as *const c_char,
                                       path.len() as c_uint);
        }
    }

    pub fn load_data(&mut self, data: &str) {
        unsafe {
            ffi::qmlrs_engine_load_from_data(self.i.p, data.as_ptr() as *const c_char,
                                             data.len() as c_uint);
        }
    }

    pub fn load_local_file(&mut self, path: &str) {
        unsafe {
            ffi::qmlrs_engine_load_file(self.i.p, path.as_ptr() as *const c_char,
                                       path.len() as c_uint);
        }
    }

    pub fn exec(self) {
        unsafe { ffi::qmlrs_app_exec(); }
    }

    pub fn set_property(&mut self, name: &str, obj: &Object) {
        unsafe {
            ffi::qmlrs_engine_set_property(self.i.p, name.as_ptr() as *const c_char,
                                           name.len() as c_uint, obj.p);
        }
    }
}

#[allow(missing_copy_implementations)]
pub struct MetaObject {
    p: *mut ffi::QrsMetaObject
}

impl MetaObject {
    pub fn new(name: &str, fun: ffi::SlotFunction) -> MetaObject {
        let p = unsafe { ffi::qmlrs_metaobject_create(name.as_ptr() as *const c_char,name.len() as c_uint,fun) };
        assert!(!p.is_null());

        MetaObject { p: p }
    }

    pub fn add_signal(&mut self, sig: &str) -> isize {
        unsafe { ffi::qmlrs_metaobject_add_signal(self.p,sig.as_ptr() as *const c_char,sig.len() as c_uint) as isize }
    }

    pub fn add_property(&mut self, name: &str, ty: &str, signal: Option<&str>) {
        unsafe {
            if let Some(sig) = signal {
                ffi::qmlrs_metaobject_add_property(self.p,name.as_ptr() as *const c_char,name.len() as c_uint,
                                                   ty.as_ptr() as *const c_char, ty.len() as c_uint,
                                                   sig.as_ptr() as *const c_char, sig.len() as c_uint);
            } else {
                let s = "";
                ffi::qmlrs_metaobject_add_property(self.p,name.as_ptr() as *const c_char,name.len() as c_uint,
                                                   ty.as_ptr() as *const c_char,ty.len() as c_uint,
                                                   s.as_ptr() as *const c_char, 0);
            }
        }
    }

    pub fn add_slot(&mut self, sig: &str) -> isize {
        unsafe { ffi::qmlrs_metaobject_add_slot(self.p,sig.as_ptr() as *const c_char,sig.len() as c_uint) as isize }
    }

    pub fn add_method(&mut self, sig: &str, ret: &str) -> isize {
        unsafe {
            ffi::qmlrs_metaobject_add_method(self.p,sig.as_ptr() as *const c_char,sig.len() as c_uint,
                                             ret.as_ptr() as *const c_char,ret.len() as c_uint) as isize
        }
    }

    pub fn instantiate(&mut self) -> Object {
        Object{ p: unsafe {
            ffi::qmlrs_metaobject_instantiate(self.p)
        } }
    }
}

pub struct Object {
    p: *mut ffi::QObject
}

unsafe impl Send for Object { }
unsafe impl Sync for Object { }

impl Object {
    pub fn from_ptr(p: *mut ffi::QObject) -> Object {
        Object{ p: p }
    }

    pub fn set_property(&mut self, name: &str, value: Variant) {
        unsafe {
            let var = ffi::qmlrs_variant_create();
            value.to_qvariant(var);

            ffi::qmlrs_object_set_property(self.p,name.as_ptr() as *const c_char,name.len() as c_uint,var);
            ffi::qmlrs_variant_destroy(var);
        }
    }

    pub fn get_property(&self, name: &str) -> Variant {
        unsafe {
            let var = ffi::qmlrs_variant_create();

            ffi::qmlrs_object_get_property(self.p,name.as_ptr() as *const c_char,name.len() as c_uint,var);
            let ret = Variant::from_qvariant(var);

            ffi::qmlrs_variant_destroy(var);
            ret.unwrap()
        }
    }

    pub fn call(&self, id: isize, args: &[Variant]) -> Option<Variant> {
        unsafe {
            let vl = ffi::qmlrs_varlist_create();
            let ret = ffi::qmlrs_variant_create();

            for v in args {
                let var = ffi::qmlrs_varlist_push(vl);
                v.to_qvariant(var);
            }

            ffi::qmlrs_object_call(self.p, id as c_int, vl, ret);
            ffi::qmlrs_varlist_destroy(vl);

            let r = Variant::from_qvariant(ret);
            ffi::qmlrs_variant_destroy(ret);

            r
        }
    }

    pub fn emit(&self, id: isize, args: &[Variant]) {
        unsafe {
            let vl = ffi::qmlrs_varlist_create();

            for v in args {
                let var = ffi::qmlrs_varlist_push(vl);
                v.to_qvariant(var);
            }

            ffi::qmlrs_object_emit(self.p, id as c_int, vl);
            ffi::qmlrs_varlist_destroy(vl);
        }
    }

    pub fn delete_later(&mut self) {
        unsafe { ffi::qmlrs_object_delete_later(self.p) };
    }

    pub fn as_ptr(&mut self) -> *mut QObject {
        self.p
    }
}

pub fn register_singleton_type(module: &str, major: isize, minor: isize, typenam: &str, fun: ffi::SingletonFunction) {
    unsafe {
        ffi::qmlrs_register_singleton_type(module.as_ptr() as *const c_char,module.len() as c_uint,
                                           major as c_int,minor as c_int,
                                           typenam.as_ptr() as *const c_char,typenam.len() as c_uint,
                                           fun);
    }
}

pub fn clear_type_registrations() {
    unsafe { ffi::qmlrs_clear_type_registrations() }
}

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

    #[test]
    fn test_create_engine() {
        Engine::new_headless();
    }
}
<|endoftext|>


<commit_before>#![cfg_attr(test, deny(warnings))]

extern crate time;
extern crate conduit;
extern crate conduit_middleware as middleware;

use conduit::{Request, Method};
use middleware::Middleware;
use std::borrow::Cow;
use std::error::Error;
use std::io;
use time::{Tm, ParseError};

pub type Response = Result<conduit::Response, Box<Error+Send>>;

#[allow(missing_copy_implementations)]
pub struct ConditionalGet;

impl Middleware for ConditionalGet {
    fn after(&self, req: &mut Request, res: Response) -> Response {
        let mut res = try!(res);

        match req.method() {
            Method::Get | Method::Head => {
                if is_ok(&res) && is_fresh(req, &res) {
                    res.status = (304, "Not Modified");
                    res.headers.remove("Content-Type");
                    res.headers.remove("Content-Length");
                    res.body = Box::new(io::empty());
                }
            },
            _ => ()
        }

        Ok(res)
    }
}

fn is_ok(response: &conduit::Response) -> bool {
    match *response {
        conduit::Response { status: (200, _), .. } => true,
        _ => false
    }
}

fn is_fresh(req: &Request, res: &conduit::Response) -> bool {
    let modified_since = req.headers().find("If-Modified-Since").map(header_val);
    let none_match     = req.headers().find("If-None-Match").map(header_val);

    if modified_since.as_ref().or_else(|| none_match.as_ref()).is_none() {
        return false;
    }

    let mut success = true;

    modified_since.and_then(|modified_since| {
        parse_http_date(&modified_since).map_err(|_| { success = false; }).ok()
    }).map(|parsed| {
        success = success && is_modified_since(parsed, res);
    });

    none_match.map(|none_match| {
        success = success && etag_matches(&none_match, res);
    });

    success
}

fn etag_matches(none_match: &str, res: &conduit::Response) -> bool {
    res.headers.get("ETag").map(|etag| {
        res_header_val(etag) == none_match
    }).unwrap_or(false)
}

fn is_modified_since(modified_since: Tm, res: &conduit::Response) -> bool {
    res.headers.get("Last-Modified").and_then(|last_modified| {
        parse_http_date(&res_header_val(last_modified)).ok()
    }).map(|last_modified| {
        modified_since.to_timespec() >= last_modified.to_timespec()
    }).unwrap_or(false)
}

fn header_val<'a>(header: Vec<&'a str>) -> Cow<'a, str> {
    if header.len() == 1 {
        Cow::Borrowed(header[0])
    } else {
        Cow::Owned(header.concat())
    }
}

fn res_header_val<'a>(header: &'a Vec<String>) -> Cow<'a, str> {
    if header.len() == 1 {
        Cow::Borrowed(&header[0])
    } else {
        Cow::Owned(header.concat())
    }
}

fn parse_http_date(string: &str) -> Result<Tm, ()> {
    parse_rfc1123(string)
        .or_else(|_| parse_rfc850(string))
        .or_else(|_| parse_asctime(string))
        .map_err(|_| ())
}

fn parse_rfc1123(string: &str) -> Result<Tm, ParseError> {
    time::strptime(string, "%a, %d %b %Y %T GMT")
}

fn parse_rfc850(string: &str) -> Result<Tm, ParseError> {
    time::strptime(string, "%a, %d-%m-%y %T GMT")
}

fn parse_asctime(string: &str) -> Result<Tm, ParseError> {
    time::strptime(string, "%a %m%t%d %T %Y")
}

#[cfg(test)]
mod tests {
    extern crate conduit_test as test;

    use std::collections::HashMap;
    use std::error::Error;
    use std::io::Cursor;
    use time;
    use time::Tm;
    use conduit::{Request, Response, Handler, Method};
    use middleware::MiddlewareBuilder;

    use super::ConditionalGet;

    macro_rules! returning {
        ($code:expr, $($header:expr => $value:expr),+) => ({
            let mut headers = HashMap::new();
            $(headers.insert($header.to_string(), vec!($value.to_string()));)+
            let handler = SimpleHandler::new(headers, $code, "hello");
            let mut stack = MiddlewareBuilder::new(handler);
            stack.add(ConditionalGet);
            stack
        });
        ($($header:expr => $value:expr),+) => ({
            returning!((200, "OK"), $($header => $value),+)
        })
    }

    macro_rules! request {
        ($($header:expr => $value:expr),+) => ({
            let mut req = test::MockRequest::new(Method::Get, "/");
            $(req.header($header, &$value.to_string());)+
            req
        })
    }

    #[test]
    fn test_sends_304() {
        let handler = returning!("Last-Modified" => httpdate(time::now()));
        expect_304(handler.call(&mut request!(
            "If-Modified-Since" => httpdate(time::now())
        )));
    }

    #[test]
    fn test_sends_304_if_older_than_now() {
        let handler = returning!("Last-Modified" => before_now());
        expect_304(handler.call(&mut request!(
            "If-Modified-Since" => httpdate(time::now())
        )));
    }

    #[test]
    fn test_sends_304_with_etag() {
        let handler = returning!("ETag" => "1234");
        expect_304(handler.call(&mut request!(
            "If-None-Match" => "1234"
        )));
    }

    #[test]
    fn test_sends_200_with_fresh_time_but_not_etag() {
        let handler = returning!("Last-Modified" => before_now(), "ETag" => "1234");
        expect_200(handler.call(&mut request!(
            "If-Modified-Since" => now(),
            "If-None-Match" => "4321"
        )));
    }

    #[test]
    fn test_sends_200_with_fresh_etag_but_not_time() {
        let handler = returning!("Last-Modified" => now(), "ETag" => "1234");
        expect_200(handler.call(&mut request!(
            "If-Modified-Since" => before_now(),
            "If-None-Match" => "1234"
        )));
    }

    #[test]
    fn test_sends_200_with_fresh_etag() {
        let handler = returning!("ETag" => "1234");
        expect_200(handler.call(&mut request!(
            "If-None-Match" => "4321"
        )));
    }

    #[test]
    fn test_sends_200_with_fresh_time() {
        let handler = returning!("Last-Modified" => now());
        expect_200(handler.call(&mut request!(
            "If-Modified-Since" => before_now()
        )));
    }

    #[test]
    fn test_sends_304_with_fresh_time_and_etag() {
        let handler = returning!("Last-Modified" => before_now(), "ETag" => "1234");
        expect_304(handler.call(&mut request!(
            "If-Modified-Since" => now(),
            "If-None-Match" => "1234"
        )));
    }

    #[test]
    fn test_does_not_affect_non_200() {
        let code = (302, "Found");
        let handler = returning!(code, "Last-Modified" => before_now(), "ETag" => "1234");
        expect(code, handler.call(&mut request!(
            "If-Modified-Since" => now(),
            "If-None-Match" => "1234"
        )));
    }

    #[test]
    fn test_does_not_affect_malformed_timestamp() {
        let bad_stamp = time::now().strftime("%Y-%m-%d %H:%M:%S %z").unwrap().to_string();
        let handler = returning!("Last-Modified" => before_now());
        expect_200(handler.call(&mut request!(
            "If-Modified-Since" => bad_stamp
        )));
    }

    fn expect_304(response: Result<Response, Box<Error+Send>>) {
        let mut response = response.ok().expect("No response");
        let mut body = String::new();
        response.body.read_to_string(&mut body).ok().expect("No body");

        assert_eq!(response.status, (304, "Not Modified"));
        assert_eq!(body, "");
    }

    fn expect_200(response: Result<Response, Box<Error+Send>>) {
        expect((200, "OK"), response);
    }

    fn expect(status: (u32, &'static str), response: Result<Response, Box<Error+Send>>) {
        let mut response = response.ok().expect("No response");
        let mut body = String::new();
        response.body.read_to_string(&mut body).ok().expect("No body");

        assert_eq!(response.status, status);
        assert_eq!(body, "hello");
    }

    struct SimpleHandler {
        map: HashMap<String, Vec<String>>,
        status: Status,
        body: &'static str
    }

    type Status = (u32, &'static str);

    impl SimpleHandler {
        fn new(map: HashMap<String, Vec<String>>, status: Status, body: &'static str) -> SimpleHandler {
            SimpleHandler { map: map, status: status, body: body }
        }
    }

    impl Handler for SimpleHandler {
        fn call(&self, _: &mut Request) -> Result<Response, Box<Error+Send>> {
            Ok(Response {
                status: self.status,
                headers: self.map.clone(),
                body: Box::new(Cursor::new(self.body.to_string().into_bytes())),
            })
        }
    }

    fn before_now() -> String {
        let mut now = time::now();
        now.tm_year -= 1;
        httpdate(now)
    }

    fn now() -> String {
        httpdate(time::now())
    }

    fn httpdate(time: Tm) -> String {
        time.strftime("%a, %d-%m-%y %T GMT").unwrap().to_string()
    }
}
<commit_msg>Update tests<commit_after>#![cfg_attr(test, deny(warnings))]

extern crate time;
extern crate conduit;
extern crate conduit_middleware as middleware;

use conduit::{Request, Method};
use middleware::Middleware;
use std::borrow::Cow;
use std::error::Error;
use std::io;
use time::{Tm, ParseError};

pub type Response = Result<conduit::Response, Box<Error+Send>>;

#[allow(missing_copy_implementations)]
pub struct ConditionalGet;

impl Middleware for ConditionalGet {
    fn after(&self, req: &mut Request, res: Response) -> Response {
        let mut res = try!(res);

        match req.method() {
            Method::Get | Method::Head => {
                if is_ok(&res) && is_fresh(req, &res) {
                    res.status = (304, "Not Modified");
                    res.headers.remove("Content-Type");
                    res.headers.remove("Content-Length");
                    res.body = Box::new(io::empty());
                }
            },
            _ => ()
        }

        Ok(res)
    }
}

fn is_ok(response: &conduit::Response) -> bool {
    match *response {
        conduit::Response { status: (200, _), .. } => true,
        _ => false
    }
}

fn is_fresh(req: &Request, res: &conduit::Response) -> bool {
    let modified_since = req.headers().find("If-Modified-Since").map(header_val);
    let none_match     = req.headers().find("If-None-Match").map(header_val);

    if modified_since.as_ref().or_else(|| none_match.as_ref()).is_none() {
        return false;
    }

    let mut success = true;

    modified_since.and_then(|modified_since| {
        parse_http_date(&modified_since).map_err(|_| { success = false; }).ok()
    }).map(|parsed| {
        success = success && is_modified_since(parsed, res);
    });

    none_match.map(|none_match| {
        success = success && etag_matches(&none_match, res);
    });

    success
}

fn etag_matches(none_match: &str, res: &conduit::Response) -> bool {
    res.headers.get("ETag").map(|etag| {
        res_header_val(etag) == none_match
    }).unwrap_or(false)
}

fn is_modified_since(modified_since: Tm, res: &conduit::Response) -> bool {
    res.headers.get("Last-Modified").and_then(|last_modified| {
        parse_http_date(&res_header_val(last_modified)).ok()
    }).map(|last_modified| {
        modified_since.to_timespec() >= last_modified.to_timespec()
    }).unwrap_or(false)
}

fn header_val<'a>(header: Vec<&'a str>) -> Cow<'a, str> {
    if header.len() == 1 {
        Cow::Borrowed(header[0])
    } else {
        Cow::Owned(header.concat())
    }
}

fn res_header_val<'a>(header: &'a Vec<String>) -> Cow<'a, str> {
    if header.len() == 1 {
        Cow::Borrowed(&header[0])
    } else {
        Cow::Owned(header.concat())
    }
}

fn parse_http_date(string: &str) -> Result<Tm, ()> {
    parse_rfc1123(string)
        .or_else(|_| parse_rfc850(string))
        .or_else(|_| parse_asctime(string))
        .map_err(|_| ())
}

fn parse_rfc1123(string: &str) -> Result<Tm, ParseError> {
    time::strptime(string, "%a, %d %b %Y %T GMT")
}

fn parse_rfc850(string: &str) -> Result<Tm, ParseError> {
    time::strptime(string, "%a, %d-%m-%y %T GMT")
}

fn parse_asctime(string: &str) -> Result<Tm, ParseError> {
    time::strptime(string, "%a %m%t%d %T %Y")
}

#[cfg(test)]
mod tests {
    extern crate conduit_test as test;

    use std::collections::HashMap;
    use std::error::Error;
    use std::io::Cursor;
    use time;
    use time::Tm;
    use conduit::{Request, Response, Handler, Method};
    use middleware::MiddlewareBuilder;

    use super::ConditionalGet;

    macro_rules! returning {
        ($code:expr, $($header:expr => $value:expr),+) => ({
            let mut headers = HashMap::new();
            $(headers.insert($header.to_string(), vec!($value.to_string()));)+
            let handler = SimpleHandler::new(headers, $code, "hello");
            let mut stack = MiddlewareBuilder::new(handler);
            stack.add(ConditionalGet);
            stack
        });
        ($($header:expr => $value:expr),+) => ({
            returning!((200, "OK"), $($header => $value),+)
        })
    }

    macro_rules! request {
        ($($header:expr => $value:expr),+) => ({
            let mut req = test::MockRequest::new(Method::Get, "/");
            $(req.header($header, &$value.to_string());)+
            req
        })
    }

    #[test]
    fn test_sends_304() {
        let handler = returning!("Last-Modified" => httpdate(time::now()));
        expect_304(handler.call(&mut request!(
            "If-Modified-Since" => httpdate(time::now())
        )));
    }

    #[test]
    fn test_sends_304_if_older_than_now() {
        let handler = returning!("Last-Modified" => before_now());
        expect_304(handler.call(&mut request!(
            "If-Modified-Since" => httpdate(time::now())
        )));
    }

    #[test]
    fn test_sends_304_with_etag() {
        let handler = returning!("ETag" => "1234");
        expect_304(handler.call(&mut request!(
            "If-None-Match" => "1234"
        )));
    }

    #[test]
    fn test_sends_200_with_fresh_time_but_not_etag() {
        let handler = returning!("Last-Modified" => before_now(), "ETag" => "1234");
        expect_200(handler.call(&mut request!(
            "If-Modified-Since" => now(),
            "If-None-Match" => "4321"
        )));
    }

    #[test]
    fn test_sends_200_with_fresh_etag_but_not_time() {
        let handler = returning!("Last-Modified" => now(), "ETag" => "1234");
        expect_200(handler.call(&mut request!(
            "If-Modified-Since" => before_now(),
            "If-None-Match" => "1234"
        )));
    }

    #[test]
    fn test_sends_200_with_fresh_etag() {
        let handler = returning!("ETag" => "1234");
        expect_200(handler.call(&mut request!(
            "If-None-Match" => "4321"
        )));
    }

    #[test]
    fn test_sends_200_with_fresh_time() {
        let handler = returning!("Last-Modified" => now());
        expect_200(handler.call(&mut request!(
            "If-Modified-Since" => before_now()
        )));
    }

    #[test]
    fn test_sends_304_with_fresh_time_and_etag() {
        let handler = returning!("Last-Modified" => before_now(), "ETag" => "1234");
        expect_304(handler.call(&mut request!(
            "If-Modified-Since" => now(),
            "If-None-Match" => "1234"
        )));
    }

    #[test]
    fn test_does_not_affect_non_200() {
        let code = (302, "Found");
        let handler = returning!(code, "Last-Modified" => before_now(), "ETag" => "1234");
        expect(code, handler.call(&mut request!(
            "If-Modified-Since" => now(),
            "If-None-Match" => "1234"
        )));
    }

    #[test]
    fn test_does_not_affect_malformed_timestamp() {
        let bad_stamp = time::now().strftime("%Y-%m-%d %H:%M:%S %z").unwrap().to_string();
        let handler = returning!("Last-Modified" => before_now());
        expect_200(handler.call(&mut request!(
            "If-Modified-Since" => bad_stamp
        )));
    }

    fn expect_304(response: Result<Response, Box<Error+Send>>) {
        let mut response = response.ok().expect("No response");
        let mut body = Vec::new();
        response.body.write_body(&mut body).ok().expect("No body");

        assert_eq!(response.status, (304, "Not Modified"));
        assert_eq!(body, b"");
    }

    fn expect_200(response: Result<Response, Box<Error+Send>>) {
        expect((200, "OK"), response);
    }

    fn expect(status: (u32, &'static str), response: Result<Response, Box<Error+Send>>) {
        let mut response = response.ok().expect("No response");
        let mut body = Vec::new();
        response.body.write_body(&mut body).ok().expect("No body");

        assert_eq!(response.status, status);
        assert_eq!(body, b"hello");
    }

    struct SimpleHandler {
        map: HashMap<String, Vec<String>>,
        status: Status,
        body: &'static str
    }

    type Status = (u32, &'static str);

    impl SimpleHandler {
        fn new(map: HashMap<String, Vec<String>>, status: Status, body: &'static str) -> SimpleHandler {
            SimpleHandler { map: map, status: status, body: body }
        }
    }

    impl Handler for SimpleHandler {
        fn call(&self, _: &mut Request) -> Result<Response, Box<Error+Send>> {
            Ok(Response {
                status: self.status,
                headers: self.map.clone(),
                body: Box::new(Cursor::new(self.body.to_string().into_bytes())),
            })
        }
    }

    fn before_now() -> String {
        let mut now = time::now();
        now.tm_year -= 1;
        httpdate(now)
    }

    fn now() -> String {
        httpdate(time::now())
    }

    fn httpdate(time: Tm) -> String {
        time.strftime("%a, %d-%m-%y %T GMT").unwrap().to_string()
    }
}
<|endoftext|>


<commit_before>// Copyright 2016 Martin Grabmueller. See the LICENSE file at the
// top-level directory of this distribution for license information.
//

#[macro_use]
extern crate log;
extern crate uuid;
extern crate getopts;
extern crate openssl;

use uuid::Uuid;
use getopts::Options;
use openssl::ssl;

//use std::fmt;
use std::io::Write;
use std::io::Read;
use std::net::TcpListener;
use std::net::TcpStream;
use std::thread;

mod error;

/// Print a usage summary to stdout that describes the command syntax.
pub fn print_usage(program: &str, opts: Options) {
    let brief = format!("Usage: {} FILE [options]", program);
    print!("{}", opts.usage(&brief));
}

pub struct NodeState {
    ssl_context: ssl::SslContext
}

impl NodeState {
    pub fn new(config: &Config) -> Result<NodeState, error::Error> {
        let mut ssl_context = try!(ssl::SslContext::new(ssl::SslMethod::Tlsv1));

        let mut ca_path = config.workspace_dir.clone();
        ca_path.push_str("/ca-chain.cert.pem");

        ssl_context.set_CA_file(&ca_path);

        Ok(NodeState {
            ssl_context: ssl_context
        })
    }
}

/// Configuration of a peerington node.
pub struct Config {
    /// UUID of the node.
    pub uuid: Uuid,
    /// The addresses this node will attempt to listen on.
    pub listen_addresses: Vec<String>,
    /// The addresses this node will use to connect to a cluster of
    /// peerington nodes.
    pub seed_addresses: Vec<String>,
    /// Local storage space where some data is stored during
    /// operation.
    pub workspace_dir: String
}

/// Parse allowed options for a peerington node.  Either return a
/// complete `Config' value or an error value.
pub fn parse_opts(args: Vec<String>) -> Result<Config, error::ConfigError> {

    let mut opts = Options::new();
    opts.optopt("u", "uuid", "set node uuid", "UUID");
    opts.optopt("w", "workspace", "set workspace directory", "DIRECTORY");
    opts.optmulti("l", "listen", "set listen address(es)", "ADDRESS:PORT");
    opts.optmulti("s", "seed", "set seed address(es)", "ADDRESS:PORT");
    opts.optflag("h", "help", "print this help menu");

    match opts.parse(&args[1..]) {
        Ok(matches) => {
            if matches.opt_present("h") {
                return Err(error::ConfigError::HelpRequested(opts));
            }

            let addresses = if matches.opt_present("l") {
                matches.opt_strs("l")
            } else {
                return Err(error::ConfigError::NoListenAddress);
            };

            let seeds = matches.opt_strs("s");

            let workspace =
                match matches.opt_str("w") {
                    Some(w) => w,
                    None => {
                        return Err(error::ConfigError::NoWorkspace);
                    }
                };

            let uuid =
                match matches.opt_str("u") {
                    Some(uuid) => {
                        match Uuid::parse_str(uuid.chars().as_str()) {
                            Ok(u) => {
                                u
                            }
                            Err(e) => {
                                return Err(error::ConfigError::InvalidUuid(e));
                            }
                        }
                    },
                    None => {
                        return Err(error::ConfigError::NoUuid);
                    }
                };

            return Ok(Config {
                listen_addresses: addresses,
                seed_addresses: seeds,
                workspace_dir: workspace,
                uuid: uuid
            });
        }
        Err(f) => {
            return Err(error::ConfigError::GetOptError(f));
        }
    };
}

fn handler(stream: &mut TcpStream) {
    if let Ok(peer) = stream.peer_addr() {
        info!("connection from {}", peer);
    } else {
        info!("connection attempt");
    }
    let mut buf = [33; 1024];
    loop {
        match stream.read(&mut buf) {
            Ok(0) =>
                break,
            Ok(n) => {
                match stream.write(&buf[0..n]) {
                    Ok(x) =>
                        if x != n {
                            error!("could not write everything");
                            break;
                        },
                    Err(e) => {
                        error!("error when writing: {}", e);
                    }
                }
            },
            Err(e) => {
                error!("error when reading: {}", e);
                break;
            }
        }
    }
    if let Ok(peer) = stream.peer_addr() {
        info!("closed connection from {}", peer);
    } else {
        info!("closed connection");
    }
}

fn listener(addr: String) {
    match TcpListener::bind(addr.as_str()) {
        Ok(listener) => {
            info!("listening on {}", listener.local_addr().unwrap());
            for stream in listener.incoming() {
                match stream {
                    Ok(mut stream) => {
                        thread::spawn(move || {
                            handler(&mut stream);
                        });
                    },
                    Err(e) =>
                        error!("error when accepting connection to {}: {}",
                                 addr, e)
                }
            }
        },
        Err(e) => {
            error!("error when binding to {}: {}", addr, e);
        }
    }
}

pub fn start_listeners(config: &Config) {
    for addr in config.listen_addresses.clone() {
        let a = addr.clone();
        thread::spawn(move || listener(a));
    }
}

mod test {
    #[allow(unused_imports)]
    use super::*;

    #[test]
    fn no_listen() {
        let ok = match parse_opts(vec!["prog".to_string()]) {
            Err(ConfigError::NoListenAddress) => true,
            _ => false
        };
        assert!(ok);
    }

    #[test]
    fn no_workspace() {
        let ok = match parse_opts(vec!["prog".to_string(),
                                       "--listen=localhost:1234".to_string()]) {
            Err(ConfigError::NoWorkspace) => true,
            _ => false
        };
        assert!(ok);
    }

    #[test]
    fn no_uuid() {
        let ok = match parse_opts(vec!["prog".to_string(),
                                       "--listen=localhost:1234".to_string(),
                                       "--workspace=space".to_string()]) {
            Err(ConfigError::NoUuid) => true,
            _ => false
        };
        assert!(ok);
    }

    #[test]
    fn invalid_uuid() {
        let ok = match parse_opts(vec!["prog".to_string(),
                                       "--listen=localhost:1234".to_string(),
                                       "--workspace=space".to_string(),
                                       "--uuid=abcde".to_string()]) {
            Err(ConfigError::InvalidUuid(_)) => true,
            _ => false
        };
        assert!(ok);
    }

    #[test]
    fn invalid_arg() {
        let ok = match parse_opts(vec!["prog".to_string(), "--hepl".to_string()]) {
            Err(ConfigError::GetOptError(_)) => true,
            _ => false
        };
        assert!(ok);
    }

    #[test]
    fn help_arg() {
        let ok = match parse_opts(vec!["prog".to_string(), "-h".to_string()]) {
            Err(ConfigError::HelpRequested(_)) => true,
            _ => false
        };
        assert!(ok);
    }

    #[test]
    fn long_help_arg() {
        let ok = match parse_opts(vec!["prog".to_string(), "--help".to_string()]) {
            Err(ConfigError::HelpRequested(_)) => true,
            _ => false
        };
        assert!(ok);
    }

    #[test]
    fn all_fine() {
        match parse_opts(vec!["prog".to_string(),
                              "--listen=localhost:1234".to_string(),
                              "--workspace=space".to_string(),
                              "--uuid=33d92212-7bf3-4573-8052-17789f520240".to_string()]) {
            Ok(config) => {
                assert!(config.listen_addresses.len() >= 1);
                for a in config.listen_addresses {
                    assert!(a.len() > 0);
                }
                assert!(config.workspace_dir.len() >= 1);
            }
            _ => {
                assert!(false);
            }
        };
    }

}
<commit_msg>Start TLS implementation.<commit_after>// Copyright 2016 Martin Grabmueller. See the LICENSE file at the
// top-level directory of this distribution for license information.
//

#[macro_use]
extern crate log;
extern crate uuid;
extern crate getopts;
extern crate openssl;

use uuid::Uuid;
use getopts::Options;
use openssl::ssl;
use openssl::x509;

use std::io::Write;
use std::io::Read;
use std::net::TcpListener;
use std::net::TcpStream;
use std::thread;

pub mod error;

/// Print a usage summary to stdout that describes the command syntax.
pub fn print_usage(program: &str, opts: Options) {
    let brief = format!("Usage: {} FILE [options]", program);
    print!("{}", opts.usage(&brief));
}

pub struct NodeState {
    pub ssl_context: ssl::SslContext
}

impl NodeState {
    pub fn new(config: &Config) -> Result<NodeState, error::Error> {
        let mut ssl_context = try!(ssl::SslContext::new(ssl::SslMethod::Tlsv1));

        let mut ca_path = config.workspace_dir.clone();
        ca_path.push_str("/ca-chain.cert.pem");

        trace!("setting ca file");
        try!(ssl_context.set_CA_file(&ca_path));

        let mut cert_path = config.workspace_dir.clone();
        cert_path.push_str("/");
        cert_path.push_str(&config.uuid.to_string());
        cert_path.push_str(".cert.pem");

        trace!("setting certificate file");
        try!(ssl_context.set_certificate_file(&cert_path, x509::X509FileType::PEM));

        let mut priv_key_path = config.workspace_dir.clone();
        priv_key_path.push_str("/");
        priv_key_path.push_str(&config.uuid.to_string());
        priv_key_path.push_str(".key.pem");

        trace!("setting private key file");
        try!(ssl_context.set_private_key_file(&priv_key_path,
                                              x509::X509FileType::PEM));
        try!(ssl_context.check_private_key());

        Ok(NodeState {
            ssl_context: ssl_context
        })
    }
}

/// Configuration of a peerington node.
pub struct Config {
    /// UUID of the node.
    pub uuid: Uuid,
    /// The addresses this node will attempt to listen on.
    pub listen_addresses: Vec<String>,
    /// The addresses this node will use to connect to a cluster of
    /// peerington nodes.
    pub seed_addresses: Vec<String>,
    /// Local storage space where some data is stored during
    /// operation.
    pub workspace_dir: String
}

/// Parse allowed options for a peerington node.  Either return a
/// complete `Config' value or an error value.
pub fn parse_opts(args: Vec<String>) -> Result<Config, error::ConfigError> {

    let mut opts = Options::new();
    opts.optopt("u", "uuid", "set node uuid", "UUID");
    opts.optopt("w", "workspace", "set workspace directory", "DIRECTORY");
    opts.optmulti("l", "listen", "set listen address(es)", "ADDRESS:PORT");
    opts.optmulti("s", "seed", "set seed address(es)", "ADDRESS:PORT");
    opts.optflag("h", "help", "print this help menu");

    match opts.parse(&args[1..]) {
        Ok(matches) => {
            if matches.opt_present("h") {
                return Err(error::ConfigError::HelpRequested(opts));
            }

            let addresses = if matches.opt_present("l") {
                matches.opt_strs("l")
            } else {
                return Err(error::ConfigError::NoListenAddress);
            };

            let seeds = matches.opt_strs("s");

            let workspace =
                match matches.opt_str("w") {
                    Some(w) => w,
                    None => {
                        return Err(error::ConfigError::NoWorkspace);
                    }
                };

            let uuid =
                match matches.opt_str("u") {
                    Some(uuid) => {
                        match Uuid::parse_str(uuid.chars().as_str()) {
                            Ok(u) => {
                                u
                            }
                            Err(e) => {
                                return Err(error::ConfigError::InvalidUuid(e));
                            }
                        }
                    },
                    None => {
                        return Err(error::ConfigError::NoUuid);
                    }
                };

            return Ok(Config {
                listen_addresses: addresses,
                seed_addresses: seeds,
                workspace_dir: workspace,
                uuid: uuid
            });
        }
        Err(f) => {
            return Err(error::ConfigError::GetOptError(f));
        }
    };
}

fn handler(stream: &mut ssl::SslStream<TcpStream>) {
    if let Ok(peer) = stream.get_ref().peer_addr() {
        info!("connection from {}", peer);
    } else {
        info!("connection attempt");
    }
    let mut buf = [33; 1024];
    loop {
        match stream.ssl_read(&mut buf) {
            Ok(0) =>
                break,
            Ok(n) => {
                match stream.ssl_write(&buf[0..n]) {
                    Ok(x) =>
                        if x != n {
                            error!("could not write everything");
                            break;
                        },
                    Err(e) => {
                        error!("error when writing: {}", e);
                    }
                }
            },
            Err(e) => {
                error!("error when reading: {}", e);
                break;
            }
        }
    }
    if let Ok(peer) = stream.get_ref().peer_addr() {
        info!("closed connection from {}", peer);
    } else {
        info!("closed connection");
    }
}

fn listener(addr: String, ssl_context: &ssl::SslContext) {
    match TcpListener::bind(addr.as_str()) {
        Ok(listener) => {
            info!("listening on {}", listener.local_addr().unwrap());
            for stream in listener.incoming() {
                match stream {
                    Ok(mut stream) => {
                        match ssl::Ssl::new(ssl_context) {
                            Ok(ssl) => {
                                thread::spawn(move || {
                                    match ssl::SslStream::connect(ssl, stream) {
                                        Ok(mut ssl_stream) => {
                                            handler(&mut ssl_stream);
                                        },
                                        Err(e) => {
                                            error!("error when accepting connection: {}",
                                                   e)
                                        }
                                    }
                                });
                                ()
                            },
                            Err(e) => {
                                error!("error when accepting connection to {}: {}",
                                       addr, e)
                            }
                        }
                    },
                    Err(e) =>
                        error!("error when accepting connection to {}: {}",
                                 addr, e)
                }
            }
        },
        Err(e) => {
            error!("error when binding to {}: {}", addr, e);
        }
    }
}

pub fn start_listeners(config: &Config, ssl_context: &ssl::SslContext) {
    for addr in config.listen_addresses.clone() {
        let a = addr.clone();
        let ssl_ctx = ssl_context.clone();
        thread::spawn(move || listener(a, &ssl_ctx));
    }
}

mod test {
    #[allow(unused_imports)]
    use super::*;

    #[test]
    fn no_listen() {
        let ok = match parse_opts(vec!["prog".to_string()]) {
            Err(ConfigError::NoListenAddress) => true,
            _ => false
        };
        assert!(ok);
    }

    #[test]
    fn no_workspace() {
        let ok = match parse_opts(vec!["prog".to_string(),
                                       "--listen=localhost:1234".to_string()]) {
            Err(ConfigError::NoWorkspace) => true,
            _ => false
        };
        assert!(ok);
    }

    #[test]
    fn no_uuid() {
        let ok = match parse_opts(vec!["prog".to_string(),
                                       "--listen=localhost:1234".to_string(),
                                       "--workspace=space".to_string()]) {
            Err(ConfigError::NoUuid) => true,
            _ => false
        };
        assert!(ok);
    }

    #[test]
    fn invalid_uuid() {
        let ok = match parse_opts(vec!["prog".to_string(),
                                       "--listen=localhost:1234".to_string(),
                                       "--workspace=space".to_string(),
                                       "--uuid=abcde".to_string()]) {
            Err(ConfigError::InvalidUuid(_)) => true,
            _ => false
        };
        assert!(ok);
    }

    #[test]
    fn invalid_arg() {
        let ok = match parse_opts(vec!["prog".to_string(), "--hepl".to_string()]) {
            Err(ConfigError::GetOptError(_)) => true,
            _ => false
        };
        assert!(ok);
    }

    #[test]
    fn help_arg() {
        let ok = match parse_opts(vec!["prog".to_string(), "-h".to_string()]) {
            Err(ConfigError::HelpRequested(_)) => true,
            _ => false
        };
        assert!(ok);
    }

    #[test]
    fn long_help_arg() {
        let ok = match parse_opts(vec!["prog".to_string(), "--help".to_string()]) {
            Err(ConfigError::HelpRequested(_)) => true,
            _ => false
        };
        assert!(ok);
    }

    #[test]
    fn all_fine() {
        match parse_opts(vec!["prog".to_string(),
                              "--listen=localhost:1234".to_string(),
                              "--workspace=space".to_string(),
                              "--uuid=33d92212-7bf3-4573-8052-17789f520240".to_string()]) {
            Ok(config) => {
                assert!(config.listen_addresses.len() >= 1);
                for a in config.listen_addresses {
                    assert!(a.len() > 0);
                }
                assert!(config.workspace_dir.len() >= 1);
            }
            _ => {
                assert!(false);
            }
        };
    }

}
<|endoftext|>


<commit_before>#![feature(plugin,collections)]
#![plugin(regex_macros)]
extern crate rustc_serialize;
extern crate regex;
extern crate hyper;

use std::io::Read;
use hyper::Client;
use regex::Regex;

const URL: &'static str = "http://aviationweather.gov/products/nws/all";
static PATTERN: Regex = regex!(r"(?x)
    (?P<station>\w+)\s          # Airport code
    (?P<three_K>.{4})\s         # Winds aloft at 3,000'
    (?P<six_K>.{7})\s           # Winds aloft at 6,000'
    (?P<nine_K>.{7})\s          # Winds aloft at 9,000'
    (?P<twelve_K>.{7})\s        # Winds aloft at 12,000'
    (?P<eighteen_K>.{7})\s      # Winds aloft at 18,000'
    (?P<twenty_four_K>.{7})\s   # Winds aloft at 24,000'
    (?P<thirty_K>.{6})\s        # Winds aloft at 30,000'
    (?P<thirty_four_K>.{6})\s   # Winds aloft at 34,000'
    (?P<thirty_nine_K>.{6})\s   # Winds aloft at 39,000'
");
static WIND_PATTERN: Regex = regex!(r"(?x)
    \w+
");

#[derive(Debug,RustcDecodable, RustcEncodable)]
pub struct Wind {
    direction: u32,
    speed: u32,
    altitude: u32,
}

#[derive(Debug,RustcDecodable, RustcEncodable)]
pub struct WindsAloft {
    station: String,
    winds: Vec<Wind>,
}

impl Wind {
    pub fn new(direction: u32, speed: u32, altitude: u32) -> Wind {
        Wind {
            direction: direction,
            speed: speed,
            altitude: altitude,
        }
    }
}

impl WindsAloft {
    pub fn new(station: &str, winds: Vec<Wind>) -> WindsAloft {
        WindsAloft { station: station.to_string(), winds: winds }
    }

    pub fn wind_at_altitude(&self, altitude: u32) -> Option<&Wind> {
        for wind in self.winds.iter() {
            if wind.altitude == altitude {
                return Some(wind);
            }
        }
        None
    }
}

pub fn winds_aloft_for_station(station: &str) -> Option<WindsAloft> {
    let upper_station = station.to_uppercase();
    match all_winds_aloft() {
        Some(winds) => {
            for wind in winds {
                if wind.station == upper_station {
                    return Some(wind);
                }
            }
        },
        None => return None
    }
    None
}

pub fn all_winds_aloft() -> Option<Vec<WindsAloft>> {
    let mut client = Client::new();

    let mut res = match client.get(URL).send() {
        Ok(x) => x,
        Err(_) => return None,
    };

    let mut body = String::new();
    res.read_to_string(&mut body).unwrap();

    Some(winds_from_body(body))
}

fn winds_from_body(body: String) -> Vec<WindsAloft> {
    let mut wind_aloft: Vec<WindsAloft> = vec![];

    for group in PATTERN.captures_iter(&body) {
        let station = match group.name("station") {
            Some(x) => x,
            None => continue,
        };

        let mut winds: Vec<Wind> = vec![];

        match group.name("three_K") {
            Some(i) => {
                match parse_wind(i, 3000u32) {
                    Some(x) => winds.push(x),
                    None => { }
                }
            }
            None => { }
        }

        match group.name("six_K") {
            Some(i) => {
                match parse_wind(i, 6000u32) {
                    Some(x) => winds.push(x),
                    None => { }
                }
            }
            None => { }
        }

        match group.name("nine_K") {
            Some(i) => {
                match parse_wind(i, 9000u32) {
                    Some(x) => winds.push(x),
                    None => { }
                }
            }
            None => { }
        }

        match group.name("twelve_K") {
            Some(i) => {
                match parse_wind(i, 12000u32) {
                    Some(x) => winds.push(x),
                    None => { }
                }
            }
            None => { }
        }

        match group.name("eighteen_K") {
            Some(i) => {
                match parse_wind(i, 18000u32) {
                    Some(x) => winds.push(x),
                    None => { }
                }
            }
            None => { }
        }

        match group.name("twenty_four_K") {
            Some(i) => {
                match parse_wind(i, 24000u32) {
                    Some(x) => winds.push(x),
                    None => { }
                }
            }
            None => { }
        }

        match group.name("thirty_K") {
            Some(i) => {
                match parse_wind(i, 30000u32) {
                    Some(x) => winds.push(x),
                    None => { }
                }
            }
            None => { }
        }

        match group.name("thirty_four_K") {
            Some(i) => {
                match parse_wind(i, 34000u32) {
                    Some(x) => winds.push(x),
                    None => { }
                }
            }
            None => { }
        }

        match group.name("thirty_nine_K") {
            Some(i) => {
                match parse_wind(i, 39000u32) {
                    Some(x) => winds.push(x),
                    None => { }
                }
            }
            None => { }
        }

        wind_aloft.push(WindsAloft::new(station, winds));
    }

    wind_aloft
}

fn parse_wind(wind: &str, altitude: u32) -> Option<Wind> {
    if WIND_PATTERN.is_match(wind) {
        let dir_str = wind.slice_chars(0, 2);
        let spd_str = wind.slice_chars(2, 4);

        let direction = match dir_str.parse::<u32>() {
            Ok(x) => x * 10,
            Err(_) => return None,
        };

        let speed = match spd_str.parse::<u32>() {
            Ok(x) => x,
            Err(_) => return None,
        };

        Some(Wind::new(direction, speed, altitude))
    } else {
        None
    }
}

#[cfg(test)]
mod test {
    #[test]
    fn parse_basic_wind() {
        let wind = ::parse_wind("3127", 3000u32).unwrap();

        assert_eq!(wind.speed, 27u32);
        assert_eq!(wind.direction, 310u32);
    }

    #[test]
    fn parse_bad_wind() {
        let wind = ::parse_wind("invalid", 3000u32);
        let parsed = match wind {
            Some(_) => true,
            None => false,
        };

        assert_eq!(parsed, false);
    }

    #[test]
    fn parse_wind_with_negative_temp() {
        let wind = ::parse_wind("3127-32", 3000u32).unwrap();

        assert_eq!(wind.speed, 27u32);
        assert_eq!(wind.direction, 310u32);
    }

    #[test]
    fn parse_wind_with_positive_temp() {
        let wind = ::parse_wind("3127+32", 3000u32).unwrap();

        assert_eq!(wind.speed, 27u32);
        assert_eq!(wind.direction, 310u32);
    }
}
<commit_msg>add comments<commit_after>#![feature(plugin,collections)]
#![plugin(regex_macros)]
extern crate rustc_serialize;
extern crate regex;
extern crate hyper;

use std::io::Read;
use hyper::Client;
use regex::Regex;

/// Interact with winds aloft data from NOAA

const URL: &'static str = "http://aviationweather.gov/products/nws/all";
static PATTERN: Regex = regex!(r"(?x)
    (?P<station>\w+)\s          # Airport code
    (?P<three_K>.{4})\s         # Winds aloft at 3,000'
    (?P<six_K>.{7})\s           # Winds aloft at 6,000'
    (?P<nine_K>.{7})\s          # Winds aloft at 9,000'
    (?P<twelve_K>.{7})\s        # Winds aloft at 12,000'
    (?P<eighteen_K>.{7})\s      # Winds aloft at 18,000'
    (?P<twenty_four_K>.{7})\s   # Winds aloft at 24,000'
    (?P<thirty_K>.{6})\s        # Winds aloft at 30,000'
    (?P<thirty_four_K>.{6})\s   # Winds aloft at 34,000'
    (?P<thirty_nine_K>.{6})\s   # Winds aloft at 39,000'
");
static WIND_PATTERN: Regex = regex!(r"(?x)
    \w+
");

/// The wind data from one weather station at one altitude
#[derive(Debug,RustcDecodable, RustcEncodable)]
pub struct Wind {
    direction: u32,
    speed: u32,
    altitude: u32,
}

/// Weather data from one weather station at all altitudes
#[derive(Debug,RustcDecodable, RustcEncodable)]
pub struct WindsAloft {
    station: String,
    winds: Vec<Wind>,
}

impl Wind {
    pub fn new(direction: u32, speed: u32, altitude: u32) -> Wind {
        Wind {
            direction: direction,
            speed: speed,
            altitude: altitude,
        }
    }
}

impl WindsAloft {
    pub fn new(station: &str, winds: Vec<Wind>) -> WindsAloft {
        WindsAloft { station: station.to_string(), winds: winds }
    }

    /// Search through the wind data from this station, return the wind data from the given
    /// altitude if it exists, otherwise return `None`.
    pub fn wind_at_altitude(&self, altitude: u32) -> Option<&Wind> {
        for wind in self.winds.iter() {
            if wind.altitude == altitude {
                return Some(wind);
            }
        }
        None
    }
}

pub fn winds_aloft_for_station(station: &str) -> Option<WindsAloft> {
    let upper_station = station.to_uppercase();
    match all_winds_aloft() {
        Some(winds) => {
            for wind in winds {
                if wind.station == upper_station {
                    return Some(wind);
                }
            }
        },
        None => return None
    }
    None
}

pub fn all_winds_aloft() -> Option<Vec<WindsAloft>> {
    let mut client = Client::new();

    let mut res = match client.get(URL).send() {
        Ok(x) => x,
        Err(_) => return None,
    };

    let mut body = String::new();
    res.read_to_string(&mut body).unwrap();

    Some(winds_from_body(body))
}

fn winds_from_body(body: String) -> Vec<WindsAloft> {
    let mut wind_aloft: Vec<WindsAloft> = vec![];

    for group in PATTERN.captures_iter(&body) {
        let station = match group.name("station") {
            Some(x) => x,
            None => continue,
        };

        let mut winds: Vec<Wind> = vec![];

        match group.name("three_K") {
            Some(i) => {
                match parse_wind(i, 3000u32) {
                    Some(x) => winds.push(x),
                    None => { }
                }
            }
            None => { }
        }

        match group.name("six_K") {
            Some(i) => {
                match parse_wind(i, 6000u32) {
                    Some(x) => winds.push(x),
                    None => { }
                }
            }
            None => { }
        }

        match group.name("nine_K") {
            Some(i) => {
                match parse_wind(i, 9000u32) {
                    Some(x) => winds.push(x),
                    None => { }
                }
            }
            None => { }
        }

        match group.name("twelve_K") {
            Some(i) => {
                match parse_wind(i, 12000u32) {
                    Some(x) => winds.push(x),
                    None => { }
                }
            }
            None => { }
        }

        match group.name("eighteen_K") {
            Some(i) => {
                match parse_wind(i, 18000u32) {
                    Some(x) => winds.push(x),
                    None => { }
                }
            }
            None => { }
        }

        match group.name("twenty_four_K") {
            Some(i) => {
                match parse_wind(i, 24000u32) {
                    Some(x) => winds.push(x),
                    None => { }
                }
            }
            None => { }
        }

        match group.name("thirty_K") {
            Some(i) => {
                match parse_wind(i, 30000u32) {
                    Some(x) => winds.push(x),
                    None => { }
                }
            }
            None => { }
        }

        match group.name("thirty_four_K") {
            Some(i) => {
                match parse_wind(i, 34000u32) {
                    Some(x) => winds.push(x),
                    None => { }
                }
            }
            None => { }
        }

        match group.name("thirty_nine_K") {
            Some(i) => {
                match parse_wind(i, 39000u32) {
                    Some(x) => winds.push(x),
                    None => { }
                }
            }
            None => { }
        }

        wind_aloft.push(WindsAloft::new(station, winds));
    }

    wind_aloft
}

fn parse_wind(wind: &str, altitude: u32) -> Option<Wind> {
    if WIND_PATTERN.is_match(wind) {
        let dir_str = wind.slice_chars(0, 2);
        let spd_str = wind.slice_chars(2, 4);

        let direction = match dir_str.parse::<u32>() {
            Ok(x) => x * 10,
            Err(_) => return None,
        };

        let speed = match spd_str.parse::<u32>() {
            Ok(x) => x,
            Err(_) => return None,
        };

        Some(Wind::new(direction, speed, altitude))
    } else {
        None
    }
}

#[cfg(test)]
mod test {
    #[test]
    fn parse_basic_wind() {
        let wind = ::parse_wind("3127", 3000u32).unwrap();

        assert_eq!(wind.speed, 27u32);
        assert_eq!(wind.direction, 310u32);
    }

    #[test]
    fn parse_bad_wind() {
        let wind = ::parse_wind("invalid", 3000u32);
        let parsed = match wind {
            Some(_) => true,
            None => false,
        };

        assert_eq!(parsed, false);
    }

    #[test]
    fn parse_wind_with_negative_temp() {
        let wind = ::parse_wind("3127-32", 3000u32).unwrap();

        assert_eq!(wind.speed, 27u32);
        assert_eq!(wind.direction, 310u32);
    }

    #[test]
    fn parse_wind_with_positive_temp() {
        let wind = ::parse_wind("3127+32", 3000u32).unwrap();

        assert_eq!(wind.speed, 27u32);
        assert_eq!(wind.direction, 310u32);
    }
}
<|endoftext|>


<commit_before>#![allow(dead_code)]
#![allow(unused_variables)]
#![allow(unused_assignments)]
extern crate json;

use std::cmp::Ordering;

pub struct PhoneticParser<'a> {
    patterns: &'a json::JsonValue,
    vowel: String,
    consonant: String,
    numbers: String,
    case_sensitive: String,
}

impl<'a> PhoneticParser<'a> {
    pub fn new(rule: &json::JsonValue) -> PhoneticParser {
        PhoneticParser {
            patterns: &rule["patterns"],
            vowel: rule["vowel"].as_str().unwrap().to_string(),
            consonant: rule["consonant"].as_str().unwrap().to_string(),
            numbers: rule["number"].as_str().unwrap().to_string(),
            case_sensitive: rule["casesensitive"].as_str().unwrap().to_string(),
        }
    }

    pub fn convert(&self, input: String) -> String {
        let fixed = self.fix_string(input);
        let mut output = String::new();

        let _find = self.patterns[0]["find"].as_str().unwrap();
        let max_pattern_len = _find.len();

        let len = fixed.len();
        let mut cur = 0;
        while cur < len {
            let start = cur as i32;
            let mut end: i32 = 0;
            let mut matched = false;

            for chunk_len in (1..max_pattern_len + 1).rev() {
                end = start + chunk_len as i32;
                if end <= len as i32 {
                    let chunk = fixed.substring(start as usize, chunk_len as usize);

                    // Binary Search
                    let mut left: i32 = 0;
                    let mut right = self.patterns.len() as i32 - 1;
                    let mut mid: i32 = 0;
                    while right >= left {
                        mid = (right + left) / 2;
                        let pattern = &self.patterns[mid as usize];
                        let find = pattern["find"].as_str().unwrap();
                        //println!("chunk {} find {}", chunk, find);
                        if find == chunk {
                            let rules = &pattern["rules"];
                            if !rules.is_empty() {
                                for rule in rules.members() {
                                    let mut replace = true;
                                    let mut chk = 0;
                                    let matches = &rule["matches"];
                                    for _match in matches.members() {
                                        let value = _match["value"].as_str().unwrap_or("");
                                        let _type = _match["type"].as_str().unwrap();
                                        let mut scope = _match["scope"].as_str().unwrap();
                                        let mut is_negative = false;

                                        // Handle Negative
                                        if &scope[0..1] == "!" {
                                            is_negative = true;
                                            scope = &scope[1..];
                                        }

                                        if _find == "suffix" {
                                            chk = end;
                                        } else {
                                            chk = start - 1;
                                        }

                                        // Beginning
                                        match scope {
                                            "punctuation" => if ((chk < 0 && (_type == "prefix"))
                                                || (chk >= len as i32 && (_type == "suffix"))
                                                || self.is_punctuation(fixed.at(chk as usize)))
                                                == is_negative
                                            {
                                                replace = false;
                                                break;
                                            },
                                            "vowel" => if (((chk >= 0 && (_type == "prefix"))
                                                || (chk < len as i32 && (_type == "suffix")))
                                                && self.is_vowel(fixed.at(chk as usize)))
                                                == is_negative
                                            {
                                                replace = false;
                                                break;
                                            },

                                            "consonant" => if (((chk >= 0 && (_type == "prefix"))
                                                || (chk < len as i32 && (_type == "suffix")))
                                                && self.is_consonant(fixed.at(chk as usize)))
                                                == is_negative
                                            {
                                                replace = false;
                                                break;
                                            },

                                            "number" => if (((chk >= 0 && (_type == "prefix"))
                                                || (chk < len as i32 && (_type == "suffix")))
                                                && self.is_number(fixed.at(chk as usize)))
                                                == is_negative
                                            {
                                                replace = false;
                                                break;
                                            },

                                            "exact" => {
                                                let mut s: i32 = 0;
                                                let mut e: i32 = 0;
                                                if _type == "suffix" {
                                                    s = end;
                                                    e = end + value.len() as i32;
                                                } else {
                                                    // Prefix
                                                    s = start - value.len() as i32;
                                                    e = start;
                                                }
                                                if !self.is_exact(value, &fixed, s, e, is_negative)
                                                {
                                                    replace = false;
                                                    break;
                                                }
                                            }
                                            _ => panic!("Unknown scope"),
                                        };
                                    }

                                    if replace {
                                        output += rule["replace"].as_str().unwrap();
                                        cur = (end - 1) as usize;
                                        matched = true;
                                        break;
                                    }
                                }
                            }

                            if matched {
                                break;
                            }

                            // Default
                            output += pattern["replace"].as_str().unwrap();
                            cur = (end - 1) as usize;
                            matched = true;
                            break;
                        } else if find.len() > chunk.len()
                            || (find.len() == chunk.len() && find.cmp(&chunk) == Ordering::Less)
                        {
                            left = mid + 1;
                        } else {
                            right = mid - 1;
                        }
                    }
                    if matched {
                        break;
                    }
                }
            }

            if !matched {
                output += &fixed[cur..cur + 1];
            }
            cur += 1;
        }

        output
    }

    fn fix_string(&self, string: String) -> String {
        string
            .chars()
            .map(|character| {
                if self.is_case_sensitive(character) {
                    character
                } else {
                    character.to_lowercase().next().unwrap()
                }
            })
            .collect()
    }

    fn is_vowel(&self, string: &str) -> bool {
        self.vowel.contains(&string.to_lowercase())
    }

    fn is_consonant(&self, string: &str) -> bool {
        self.consonant.contains(&string.to_lowercase())
    }

    fn is_case_sensitive(&self, character: char) -> bool {
        self.case_sensitive
            .contains(&character.to_lowercase().to_string())
    }

    fn is_number(&self, character: &str) -> bool {
        self.numbers.contains(character)
    }

    fn is_exact(&self, needle: &str, heystack: &str, start: i32, end: i32, not: bool) -> bool {
        let len = end - start;
        (start >= 0 && end < heystack.len() as i32
            && (heystack.substring(start as usize, len as usize) == needle)) != not
    }

    fn is_punctuation(&self, character: &str) -> bool {
        !(self.is_vowel(character) || self.is_consonant(character))
    }
}

trait Substring {
    fn substring(&self, start: usize, length: usize) -> &str;
    fn at(&self, pos: usize) -> &str;
}

impl Substring for std::string::String {
    fn substring(&self, start: usize, length: usize) -> &str {
        &self[start..(start + length)]
    }

    fn at(&self, pos: usize) -> &str {
        &self[pos..pos + 1]
    }
}

impl Substring for str {
    fn substring(&self, start: usize, length: usize) -> &str {
        &self[start..(start + length)]
    }

    fn at(&self, pos: usize) -> &str {
        &self[pos..pos + 1]
    }
}

#[cfg(test)]
mod tests {
    use std::env;
    use std::fs::File;
    use std::io::Read;
    use json;
    use super::PhoneticParser;

    fn get_rule() -> json::JsonValue {
        // Get the rule file
        let mut p = env::current_dir().unwrap();
        p.push("AvroPhonetic.json");
        let path = p.to_str().unwrap();

        let mut grammer = String::new();

        let _ = File::open(path).unwrap().read_to_string(&mut grammer);

        json::parse(&grammer).unwrap()
    }

    #[test]
    fn test_helpers() {
        let json = get_rule();
        let parser = PhoneticParser::new(&json);

        assert!(parser.is_vowel("A"));
        assert_eq!(parser.is_vowel("b"), false);
        assert!(parser.is_consonant("B"));
        assert_eq!(parser.is_consonant("e"), false);
        assert_eq!(parser.fix_string("ODEr AMAr".to_string()), "ODer amar");
        assert!(parser.is_number("1"));
    }
}
<commit_msg>Use inclusive range! :tada:<commit_after>#![allow(dead_code)]
#![allow(unused_variables)]
#![allow(unused_assignments)]
extern crate json;

use std::cmp::Ordering;

pub struct PhoneticParser<'a> {
    patterns: &'a json::JsonValue,
    vowel: String,
    consonant: String,
    numbers: String,
    case_sensitive: String,
}

impl<'a> PhoneticParser<'a> {
    pub fn new(rule: &json::JsonValue) -> PhoneticParser {
        PhoneticParser {
            patterns: &rule["patterns"],
            vowel: rule["vowel"].as_str().unwrap().to_string(),
            consonant: rule["consonant"].as_str().unwrap().to_string(),
            numbers: rule["number"].as_str().unwrap().to_string(),
            case_sensitive: rule["casesensitive"].as_str().unwrap().to_string(),
        }
    }

    pub fn convert(&self, input: String) -> String {
        let fixed = self.fix_string(input);
        let mut output = String::new();

        let _find = self.patterns[0]["find"].as_str().unwrap();
        let max_pattern_len = _find.len();

        let len = fixed.len();
        let mut cur = 0;
        while cur < len {
            let start = cur as i32;
            let mut end: i32 = 0;
            let mut matched = false;

            for chunk_len in (1..=max_pattern_len).rev() {
                end = start + chunk_len as i32;
                if end <= len as i32 {
                    let chunk = fixed.substring(start as usize, chunk_len as usize);

                    // Binary Search
                    let mut left: i32 = 0;
                    let mut right = self.patterns.len() as i32 - 1;
                    let mut mid: i32 = 0;
                    while right >= left {
                        mid = (right + left) / 2;
                        let pattern = &self.patterns[mid as usize];
                        let find = pattern["find"].as_str().unwrap();
                        if find == chunk {
                            let rules = &pattern["rules"];
                            if !rules.is_empty() {
                                for rule in rules.members() {
                                    let mut replace = true;
                                    let mut chk = 0;
                                    let matches = &rule["matches"];
                                    for _match in matches.members() {
                                        let value = _match["value"].as_str().unwrap_or("");
                                        let _type = _match["type"].as_str().unwrap();
                                        let mut scope = _match["scope"].as_str().unwrap();
                                        let mut is_negative = false;

                                        // Handle Negative
                                        if &scope[0..1] == "!" {
                                            is_negative = true;
                                            scope = &scope[1..];
                                        }

                                        if _find == "suffix" {
                                            chk = end;
                                        } else {
                                            chk = start - 1;
                                        }

                                        // Beginning
                                        match scope {
                                            "punctuation" => if ((chk < 0 && (_type == "prefix"))
                                                || (chk >= len as i32 && (_type == "suffix"))
                                                || self.is_punctuation(fixed.at(chk as usize)))
                                                == is_negative
                                            {
                                                replace = false;
                                                break;
                                            },
                                            "vowel" => if (((chk >= 0 && (_type == "prefix"))
                                                || (chk < len as i32 && (_type == "suffix")))
                                                && self.is_vowel(fixed.at(chk as usize)))
                                                == is_negative
                                            {
                                                replace = false;
                                                break;
                                            },

                                            "consonant" => if (((chk >= 0 && (_type == "prefix"))
                                                || (chk < len as i32 && (_type == "suffix")))
                                                && self.is_consonant(fixed.at(chk as usize)))
                                                == is_negative
                                            {
                                                replace = false;
                                                break;
                                            },

                                            "number" => if (((chk >= 0 && (_type == "prefix"))
                                                || (chk < len as i32 && (_type == "suffix")))
                                                && self.is_number(fixed.at(chk as usize)))
                                                == is_negative
                                            {
                                                replace = false;
                                                break;
                                            },

                                            "exact" => {
                                                let mut s: i32 = 0;
                                                let mut e: i32 = 0;
                                                if _type == "suffix" {
                                                    s = end;
                                                    e = end + value.len() as i32;
                                                } else {
                                                    // Prefix
                                                    s = start - value.len() as i32;
                                                    e = start;
                                                }
                                                if !self.is_exact(value, &fixed, s, e, is_negative)
                                                {
                                                    replace = false;
                                                    break;
                                                }
                                            }
                                            _ => panic!("Unknown scope"),
                                        };
                                    }

                                    if replace {
                                        output += rule["replace"].as_str().unwrap();
                                        cur = (end - 1) as usize;
                                        matched = true;
                                        break;
                                    }
                                }
                            }

                            if matched {
                                break;
                            }

                            // Default
                            output += pattern["replace"].as_str().unwrap();
                            cur = (end - 1) as usize;
                            matched = true;
                            break;
                        } else if find.len() > chunk.len()
                            || (find.len() == chunk.len() && find.cmp(&chunk) == Ordering::Less)
                        {
                            left = mid + 1;
                        } else {
                            right = mid - 1;
                        }
                    }
                    if matched {
                        break;
                    }
                }
            }

            if !matched {
                output += &fixed[cur..cur + 1];
            }
            cur += 1;
        }

        output
    }

    fn fix_string(&self, string: String) -> String {
        string
            .chars()
            .map(|character| {
                if self.is_case_sensitive(character) {
                    character
                } else {
                    character.to_lowercase().next().unwrap()
                }
            })
            .collect()
    }

    fn is_vowel(&self, string: &str) -> bool {
        self.vowel.contains(&string.to_lowercase())
    }

    fn is_consonant(&self, string: &str) -> bool {
        self.consonant.contains(&string.to_lowercase())
    }

    fn is_case_sensitive(&self, character: char) -> bool {
        self.case_sensitive
            .contains(&character.to_lowercase().to_string())
    }

    fn is_number(&self, character: &str) -> bool {
        self.numbers.contains(character)
    }

    fn is_exact(&self, needle: &str, heystack: &str, start: i32, end: i32, not: bool) -> bool {
        let len = end - start;
        (start >= 0 && end < heystack.len() as i32
            && (heystack.substring(start as usize, len as usize) == needle)) != not
    }

    fn is_punctuation(&self, character: &str) -> bool {
        !(self.is_vowel(character) || self.is_consonant(character))
    }
}

trait Substring {
    fn substring(&self, start: usize, length: usize) -> &str;
    fn at(&self, pos: usize) -> &str;
}

impl Substring for std::string::String {
    fn substring(&self, start: usize, length: usize) -> &str {
        &self[start..(start + length)]
    }

    fn at(&self, pos: usize) -> &str {
        &self[pos..pos + 1]
    }
}

impl Substring for str {
    fn substring(&self, start: usize, length: usize) -> &str {
        &self[start..(start + length)]
    }

    fn at(&self, pos: usize) -> &str {
        &self[pos..pos + 1]
    }
}

#[cfg(test)]
mod tests {
    use std::env;
    use std::fs::File;
    use std::io::Read;
    use json;
    use super::PhoneticParser;

    fn get_rule() -> json::JsonValue {
        // Get the rule file
        let mut p = env::current_dir().unwrap();
        p.push("AvroPhonetic.json");
        let path = p.to_str().unwrap();

        let mut grammer = String::new();

        let _ = File::open(path).unwrap().read_to_string(&mut grammer);

        json::parse(&grammer).unwrap()
    }

    #[test]
    fn test_helpers() {
        let json = get_rule();
        let parser = PhoneticParser::new(&json);

        assert!(parser.is_vowel("A"));
        assert_eq!(parser.is_vowel("b"), false);
        assert!(parser.is_consonant("B"));
        assert_eq!(parser.is_consonant("e"), false);
        assert_eq!(parser.fix_string("ODEr AMAr".to_string()), "ODer amar");
        assert!(parser.is_number("1"));
    }
}
<|endoftext|>


<commit_before>// Copyright (c) 2013-2015 Sandstorm Development Group, Inc. and contributors
// Licensed under the MIT License:
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// 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 OR COPYRIGHT HOLDERS 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.

//! # Cap'n Proto Runtime Library
//!
//! [Cap'n Proto](http://kentonv.github.io/capnproto/) is an extremely efficient protocol for
//! sharing data and capabilities.
//!
//! The Rust implementation is split into three separate crates.
//!
//! Code generation is handled by [capnpc-rust](https://github.com/dwrensha/capnpc-rust).
//!
//! The present crate is the runtime library required by that generated code. It is hosted on Github
//! [here](https://github.com/dwrensha/capnproto-rust).
//!
//! [capnp-rpc-rust](https://github.com/dwrensha/capnp-rpc-rust) is an implementation of a
//! distributed object-capability layer.

#![feature(alloc, core, io, unsafe_destructor)]
#![allow(raw_pointer_derive)]

#![crate_name="capnp"]
#![crate_type = "lib"]

// reexports
pub use message::{MessageBuilder, BuilderOptions, MessageReader, ReaderOptions};
pub use message::MallocMessageBuilder;
pub use serialize::OwnedSpaceMessageReader;

pub mod any_pointer;
pub mod capability;
pub mod data;
pub mod data_list;
pub mod enum_list;
pub mod private;
pub mod io;
pub mod list_list;
pub mod message;
pub mod primitive_list;
pub mod serialize;
pub mod serialize_packed;
pub mod struct_list;
pub mod text;
pub mod text_list;
pub mod traits;

/// Eight bytes of memory with opaque interior.
///
/// This type is used to ensure that the data of a message is properly aligned.
#[derive(Copy)]
#[repr(C)]
pub struct Word {_unused_member : u64}

impl Word {
    /// Do this, but faster:
    /// `::std::iter::repeat(Word{ _unused_member : 0}).take(length).collect()`
    pub fn allocate_zeroed_vec(length : usize) -> ::std::vec::Vec<Word> {
        let mut result : ::std::vec::Vec<Word> = ::std::vec::Vec::with_capacity(length);
        unsafe {
            result.set_len(length);
            let p : *mut u8 = ::std::mem::transmute(result.as_mut_slice().as_mut_ptr());
            ::std::ptr::write_bytes(p, 0u8, length * ::std::mem::size_of::<Word>());
        }
        return result;
    }

    pub fn bytes_to_words<'a>(bytes : &'a [u8]) -> &'a [Word] {
        unsafe {
            ::std::slice::from_raw_parts(::std::mem::transmute(bytes.as_ptr()), bytes.len() / 8)
        }
    }

    pub fn words_to_bytes<'a>(words : &'a [Word]) -> &'a [u8] {
        unsafe {
            ::std::slice::from_raw_parts(::std::mem::transmute(words.as_ptr()), words.len() * 8)
        }
    }
}

/// Size of a message. Every generated struct has a method `.total_size()` that returns this.
#[derive(Copy)]
pub struct MessageSize {
    pub word_count : u64,

    /// Size of the capability table.
    pub cap_count : u32
}

impl MessageSize {
    pub fn plus_eq(&mut self, other : MessageSize) {
        self.word_count += other.word_count;
        self.cap_count += other.cap_count;
    }
}

/// An enum value or union discriminant that was not found among those defined in a schema.
#[derive(PartialEq, Copy, Debug)]
pub struct NotInSchema(pub u16);
<commit_msg>Error and Result types.<commit_after>// Copyright (c) 2013-2015 Sandstorm Development Group, Inc. and contributors
// Licensed under the MIT License:
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// 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 OR COPYRIGHT HOLDERS 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.

//! # Cap'n Proto Runtime Library
//!
//! [Cap'n Proto](http://kentonv.github.io/capnproto/) is an extremely efficient protocol for
//! sharing data and capabilities.
//!
//! The Rust implementation is split into three separate crates.
//!
//! Code generation is handled by [capnpc-rust](https://github.com/dwrensha/capnpc-rust).
//!
//! The present crate is the runtime library required by that generated code. It is hosted on Github
//! [here](https://github.com/dwrensha/capnproto-rust).
//!
//! [capnp-rpc-rust](https://github.com/dwrensha/capnp-rpc-rust) is an implementation of a
//! distributed object-capability layer.

#![feature(alloc, core, io, unsafe_destructor)]
#![allow(raw_pointer_derive)]

#![crate_name="capnp"]
#![crate_type = "lib"]

// reexports
pub use message::{MessageBuilder, BuilderOptions, MessageReader, ReaderOptions};
pub use message::MallocMessageBuilder;
pub use serialize::OwnedSpaceMessageReader;

pub mod any_pointer;
pub mod capability;
pub mod data;
pub mod data_list;
pub mod enum_list;
pub mod private;
pub mod io;
pub mod list_list;
pub mod message;
pub mod primitive_list;
pub mod serialize;
pub mod serialize_packed;
pub mod struct_list;
pub mod text;
pub mod text_list;
pub mod traits;

/// Eight bytes of memory with opaque interior.
///
/// This type is used to ensure that the data of a message is properly aligned.
#[derive(Copy)]
#[repr(C)]
pub struct Word {_unused_member : u64}

impl Word {
    /// Do this, but faster:
    /// `::std::iter::repeat(Word{ _unused_member : 0}).take(length).collect()`
    pub fn allocate_zeroed_vec(length : usize) -> ::std::vec::Vec<Word> {
        let mut result : ::std::vec::Vec<Word> = ::std::vec::Vec::with_capacity(length);
        unsafe {
            result.set_len(length);
            let p : *mut u8 = ::std::mem::transmute(result.as_mut_slice().as_mut_ptr());
            ::std::ptr::write_bytes(p, 0u8, length * ::std::mem::size_of::<Word>());
        }
        return result;
    }

    pub fn bytes_to_words<'a>(bytes : &'a [u8]) -> &'a [Word] {
        unsafe {
            ::std::slice::from_raw_parts(::std::mem::transmute(bytes.as_ptr()), bytes.len() / 8)
        }
    }

    pub fn words_to_bytes<'a>(words : &'a [Word]) -> &'a [u8] {
        unsafe {
            ::std::slice::from_raw_parts(::std::mem::transmute(words.as_ptr()), words.len() * 8)
        }
    }
}

/// Size of a message. Every generated struct has a method `.total_size()` that returns this.
#[derive(Copy)]
pub struct MessageSize {
    pub word_count : u64,

    /// Size of the capability table.
    pub cap_count : u32
}

impl MessageSize {
    pub fn plus_eq(&mut self, other : MessageSize) {
        self.word_count += other.word_count;
        self.cap_count += other.cap_count;
    }
}

/// An enum value or union discriminant that was not found among those defined in a schema.
#[derive(PartialEq, Copy, Debug)]
pub struct NotInSchema(pub u16);

/// Because messages are lazily validated, the return type of any method that reads a pointer field
/// must be wrapped in a Result.
pub type Result<T> = ::std::result::Result<T, Error>;

/// Things that can go wrong when you read a message.
#[derive(Debug)]
pub enum Error {
    Decode { description : &'static str,
             detail : Option<String> },
    Io(std::io::Error),
}

impl Error {
    pub fn new_decode_error(description : &'static str, detail : Option<String>) -> Error {
        Error::Decode { description : description, detail : detail}
    }
}

impl ::std::error::FromError<::std::io::Error> for Error {
    fn from_error(err : ::std::io::Error) -> Error {
        Error::Io(err)
    }
}

impl ::std::error::FromError<NotInSchema> for Error {
    fn from_error(NotInSchema(x) : NotInSchema) -> Error {
        Error::new_decode_error("Enum value or union descriminant not in schema.",
                                Some(format!("value : {}", x)))
    }
}

<|endoftext|>