<commit_before>//! A library to drive web browsers using the webdriver
//! interface.

use std::convert::From;
use std::io::Read;
use std::io;
use std::fmt::{self, Debug};

extern crate hyper;
use hyper::client::*;
use hyper::Url;

extern crate serde;
use serde::Serialize;
use serde::de::DeserializeOwned;

extern crate serde_json;
pub use serde_json::Value as JsonValue;

#[macro_use]
extern crate serde_derive;

#[macro_use]
extern crate log;

extern crate rand;

pub mod messages;
use messages::*;

pub mod firefox;

#[derive(Debug)]
pub enum Error {
    FailedToLaunchDriver,
    InvalidUrl,
    ConnectionError,
    JsonDecodeError(serde_json::Error),
    WebDriverError(WebDriverError),
}

impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            Error::FailedToLaunchDriver => write!(f, "Unable to start browser driver"),
            Error::InvalidUrl => write!(f, "Invalid URL"),
            Error::ConnectionError => write!(f, "Error connecting to browser"),
            Error::JsonDecodeError(ref s) => write!(f, "Received invalid response from browser: {}", s),
            Error::WebDriverError(ref err) => write!(f, "Error: {}", err.message),
        }
    }
}

impl From<hyper::Error> for Error {
    fn from(_: hyper::Error) -> Error {
        Error::ConnectionError
    }
}

impl From<io::Error> for Error {
    fn from(_: io::Error) -> Error {
        Error::ConnectionError
    }
}

impl From<serde_json::Error> for Error {
    fn from(e: serde_json::Error) -> Error {
        Error::JsonDecodeError(e)
    }
}

pub trait Driver {
    fn url(&self) -> &str;
}

/// A WebDriver session.
///
/// The session is removed on `Drop`
pub struct DriverSession<T> {
    driver: Option<T>,
    baseurl: Url,
    client: Client,
    session_id: String,
}

impl<T> DriverSession<T> where T: Driver {
    pub fn new(driver: T) -> Result<Self,Error> {
        let mut s = try!(Self::for_url(driver.url()));
        s.driver = Some(driver);
        Ok(s)
    }
}

impl<T> DriverSession<T> {
    /// Connect to an existing WebDriver
    pub fn for_url(url: &str) -> Result<Self, Error> {
        let baseurl = try!(Url::parse(url).map_err(|_| Error::InvalidUrl));
        let mut s = DriverSession {
            driver: None,
            baseurl: baseurl,
            client: Client::new(),
            session_id: String::new(),
        };
        info!("Creating session at {}", url);
        let sess = try!(s.new_session(&NewSessionCmd::new()));
        s.session_id = sess.sessionId;
        info!("Session {} created", s.session_id);
        Ok(s)
    }

    /// Connect to an existing WebDriver session
    pub fn for_session(url: &str, session_id: &str) -> Result<Self, Error> {
        let baseurl = try!(Url::parse(url).map_err(|_| Error::InvalidUrl));
        let s = DriverSession {
            driver: None,
            baseurl: baseurl,
            client: Client::new(),
            session_id: session_id.to_owned(),
        };
        info!("Connecting to session at {} with id {}", url, session_id);
        // We can fetch any value for the session to verify it exists.
        // The page URL will work.
        s.get_current_url()?;
        info!("Connected to existing session {}", s.session_id);
        Ok(s)
    }

    pub fn session_id(&self) -> &str {
        &self.session_id
    }

    fn get<D: DeserializeOwned + Debug>(&self, path: &str) -> Result<D, Error> {
        let url = try!(self.baseurl.join(path)
                           .map_err(|_| Error::InvalidUrl));
        debug!("GET {}", url);
        let mut res = try!(self.client.get(url)
                            .send());
        Self::decode(&mut res)
    }

    fn delete<D: DeserializeOwned + Debug>(&self, path: &str) -> Result<D, Error> {
        let url = try!(self.baseurl.join(path)
                           .map_err(|_| Error::InvalidUrl));
        debug!("DELETE {}", url);
        let mut res = try!(self.client.delete(url)
                            .send());
        Self::decode(&mut res)
    }

    fn decode<D: DeserializeOwned + Debug>(res: &mut Response) -> Result<D, Error> {
        let mut data = String::new();
        try!(res.read_to_string(&mut data));
        debug!("result status: {}\n\
                body: '{}'", res.status, data);

        if !res.status.is_success() {
            let err: Value<WebDriverError> = try!(serde_json::from_str(&data));
            trace!("deserialize error result: {:#?}", err);
            return Err(Error::WebDriverError(err.value));
        }
        let response = serde_json::from_str(&data);
        trace!("deserialize result: {:#?}", response);
        Ok(response?)
    }

    fn post<D: DeserializeOwned + Debug, E: Serialize>(&self, path: &str, body: &E) -> Result<D, Error> {
        let url = try!(self.baseurl.join(path)
                           .map_err(|_| Error::InvalidUrl));
        let body_str = try!(serde_json::to_string(body));
        debug!("POST url: {}\n\
                body: {}", url, body_str);
        let mut res = try!(self.client.post(url)
                            .body(&body_str)
                            .send());
        Self::decode(&mut res)
    }

    /// Create a new webdriver session
    fn new_session(&mut self, params: &NewSessionCmd) -> Result<Session, Error> {
        let resp: Value<Session> = try!(self.post("/session", &params));
        Ok(resp.value)
    }

    /// Navigate to the given URL
    pub fn go(&self, url: &str) -> Result<(), Error> {
        let params = GoCmd { url: url.to_string() };
        let _: Empty = try!(self.post(&format!("/session/{}/url", &self.session_id), &params));
        Ok(())
    }

    pub fn get_current_url(&self) -> Result<String, Error> {
        let v: Value<_> = try!(self.get(&format!("/session/{}/url", self.session_id)));
        Ok(v.value)
    }

    pub fn back(&self) -> Result<(), Error> {
        let _: Empty = try!(self.post(&format!("/session/{}/back", self.session_id), &Empty {}));
        Ok(())
    }

    pub fn forward(&self) -> Result<(), Error> {
        let _: Empty = try!(self.post(&format!("/session/{}/forward", self.session_id), &Empty {}));
        Ok(())
    }

    pub fn refresh(&self) -> Result<(), Error> {
        let _: Empty = try!(self.post(&format!("/session/{}/refresh", self.session_id), &Empty {}));
        Ok(())
    }

    pub fn get_page_source(&self) -> Result<String, Error> {
        let v: Value<_> = try!(self.get(&format!("/session/{}/source", self.session_id)));
        Ok(v.value)
    }

    pub fn get_title(&self) -> Result<String, Error> {
        let v: Value<_> = try!(self.get(&format!("/session/{}/title", self.session_id)));
        Ok(v.value)
    }

    /// Get all cookies
    pub fn get_cookies(&self) -> Result<Vec<Cookie>, Error> {
        let v: Value<_> = try!(self.get(&format!("/session/{}/cookie", self.session_id)));
        Ok(v.value)
    }

    pub fn get_window_handle(&self) -> Result<String, Error> {
        let v: Value<_> = try!(self.get(&format!("/session/{}/window", self.session_id)));
        Ok(v.value)
    }

    pub fn switch_window(&mut self, handle: &str) -> Result<(), Error> {
        let _: Empty = try!(self.post(&format!("/session/{}/window", self.session_id), &SwitchWindowCmd::from(handle)));
        Ok(())
    }

    pub fn close_window(&mut self) -> Result<(), Error> {
        let _: Empty = try!(self.delete(&format!("/session/{}/window", self.session_id)));
        Ok(())
    }

    pub fn get_window_handles(&self) -> Result<Vec<String>, Error> {
        let v: Value<_> = try!(self.get(&format!("/session/{}/window/handles", self.session_id)));
        Ok(v.value)
    }

    pub fn find_element(&self, selector: &str, strategy: LocationStrategy) -> Result<Element<T>, Error> {
        let cmd = FindElementCmd { using: strategy, value: selector};
        let v: Value<ElementReference> = try!(self.post(&format!("/session/{}/element", self.session_id), &cmd));
        Ok(Element::new(self, v.value.reference))
    }

    pub fn find_elements(&self, selector: &str, strategy: LocationStrategy) -> Result<Vec<Element<T>>, Error> {
        let cmd = FindElementCmd { using: strategy, value: selector};
        let mut v: Value<Vec<ElementReference>> = try!(self.post(&format!("/session/{}/elements", self.session_id), &cmd));

        let mut elems = Vec::new();
        while let Some(er) = v.value.pop() {
            elems.push(Element::new(self, er.reference))
        }
        Ok(elems)
    }

    pub fn execute(&self, script: ExecuteCmd) -> Result<JsonValue, Error> {
        let v: Value<JsonValue> = try!(self.post(&format!("/session/{}/execute/sync", self.session_id), &script));
        Ok(v.value)
    }

    pub fn execute_async(&self, script: ExecuteCmd) -> Result<JsonValue, Error> {
        let v: Value<JsonValue> = try!(self.post(&format!("/session/{}/execute/async", self.session_id), &script));
        Ok(v.value)
    }

    pub fn switch_to_frame(&self, handle: JsonValue) -> Result<(), Error> {
        let _: Empty = try!(self.post(&format!("/session/{}/frame", self.session_id), &SwitchFrameCmd::from(handle)));
        Ok(())
    }
}

impl<T> Drop for DriverSession<T> {
    fn drop(&mut self) {
        let _: Result<Empty,_> = self.delete(&format!("/session/{}", self.session_id));
    }
}

pub struct Element<'a, T: 'a> {
    session: &'a DriverSession<T>,
    reference: String,
}

impl<'a, T> Element<'a, T> {
    fn new(s: &'a DriverSession<T>, reference: String) -> Self {
        Element { session: s, reference: reference }
    }

    pub fn attribute(&self, name: &str) -> Result<String, Error> {
        let v: Value<_> = try!(self.session.get(&format!("/session/{}/element/{}/attribute/{}", self.session.session_id(), self.reference, name)));
        Ok(v.value)
    }

//    pub fn property(&self, name: &str) -> Result<String, Error> {
//        let v: Value<_> = try!(self.get(&format!("/session/{}/element/{}/property/{}", self.session_id, el.reference, name)));
//        Ok(v.value)
//    }

    pub fn css_value(&self, name: &str) -> Result<String, Error> {
        let v: Value<_> = try!(self.session.get(&format!("/session/{}/element/{}/css/{}", self.session.session_id(), self.reference, name)));
        Ok(v.value)
    }

    pub fn text(&self) -> Result<String, Error> {
        let v: Value<_> = try!(self.session.get(&format!("/session/{}/element/{}/text", self.session.session_id(), self.reference)));
        Ok(v.value)
    }

    /// Returns the tag name for this element
    pub fn name(&self) -> Result<String, Error> {
        let v: Value<_> = try!(self.session.get(&format!("/session/{}/element/{}/name", self.session.session_id(), self.reference)));
        Ok(v.value)
    }

    pub fn reference(&self) -> Result<JsonValue, Error> {
        serde_json::to_value(&ElementReference::from_str(&self.reference))
            .map_err(|err| Error::from(err))
    }

    /// Gets the `innerHTML` javascript attribute for this element. Some drivers can get
    /// this using regular attributes, in others it does not work. This method gets it
    /// executing a bit of javascript.
    pub fn inner_html(&self) -> Result<JsonValue, Error> {
        let script = ExecuteCmd {
            script: "return arguments[0].innerHTML;".to_owned(),
            args: vec![self.reference()?],
        };
        self.session.execute(script)
    }

    pub fn outer_html(&self) -> Result<JsonValue, Error> {
        let script = ExecuteCmd {
            script: "return arguments[0].outerHTML;".to_owned(),
            args: vec![self.reference()?],
        };
        self.session.execute(script)
    }
}

/// Switch the context of the current session to the given frame reference.
///
/// This structure implements Drop, and restores the session context
/// to the current top level window.
pub struct FrameContext<'a, T: 'a> {
    session: &'a DriverSession<T>,
}

impl<'a, T> FrameContext<'a, T> {
    pub fn new(session: &DriverSession<T>, frameref: JsonValue) -> Result<FrameContext<T>, Error> {
        session.switch_to_frame(frameref)?;
        Ok(FrameContext { session: session })
    }
}

impl<'a, T> Drop for FrameContext<'a, T> {
    fn drop(&mut self) {
        let _ = self.session.switch_to_frame(JsonValue::Null);
    }
}
<commit_msg>#![deny(warnings)] for lib.rs<commit_after>#![deny(warnings)]

//! A library to drive web browsers using the webdriver
//! interface.

use std::convert::From;
use std::io::Read;
use std::io;
use std::fmt::{self, Debug};

extern crate hyper;
use hyper::client::*;
use hyper::Url;

extern crate serde;
use serde::Serialize;
use serde::de::DeserializeOwned;

extern crate serde_json;
pub use serde_json::Value as JsonValue;

#[macro_use]
extern crate serde_derive;

#[macro_use]
extern crate log;

extern crate rand;

pub mod messages;
use messages::*;

pub mod firefox;

#[derive(Debug)]
pub enum Error {
    FailedToLaunchDriver,
    InvalidUrl,
    ConnectionError,
    JsonDecodeError(serde_json::Error),
    WebDriverError(WebDriverError),
}

impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            Error::FailedToLaunchDriver => write!(f, "Unable to start browser driver"),
            Error::InvalidUrl => write!(f, "Invalid URL"),
            Error::ConnectionError => write!(f, "Error connecting to browser"),
            Error::JsonDecodeError(ref s) => write!(f, "Received invalid response from browser: {}", s),
            Error::WebDriverError(ref err) => write!(f, "Error: {}", err.message),
        }
    }
}

impl From<hyper::Error> for Error {
    fn from(_: hyper::Error) -> Error {
        Error::ConnectionError
    }
}

impl From<io::Error> for Error {
    fn from(_: io::Error) -> Error {
        Error::ConnectionError
    }
}

impl From<serde_json::Error> for Error {
    fn from(e: serde_json::Error) -> Error {
        Error::JsonDecodeError(e)
    }
}

pub trait Driver {
    fn url(&self) -> &str;
}

/// A WebDriver session.
///
/// The session is removed on `Drop`
pub struct DriverSession<T> {
    driver: Option<T>,
    baseurl: Url,
    client: Client,
    session_id: String,
}

impl<T> DriverSession<T> where T: Driver {
    pub fn new(driver: T) -> Result<Self,Error> {
        let mut s = try!(Self::for_url(driver.url()));
        s.driver = Some(driver);
        Ok(s)
    }
}

impl<T> DriverSession<T> {
    /// Connect to an existing WebDriver
    pub fn for_url(url: &str) -> Result<Self, Error> {
        let baseurl = try!(Url::parse(url).map_err(|_| Error::InvalidUrl));
        let mut s = DriverSession {
            driver: None,
            baseurl: baseurl,
            client: Client::new(),
            session_id: String::new(),
        };
        info!("Creating session at {}", url);
        let sess = try!(s.new_session(&NewSessionCmd::new()));
        s.session_id = sess.sessionId;
        info!("Session {} created", s.session_id);
        Ok(s)
    }

    /// Connect to an existing WebDriver session
    pub fn for_session(url: &str, session_id: &str) -> Result<Self, Error> {
        let baseurl = try!(Url::parse(url).map_err(|_| Error::InvalidUrl));
        let s = DriverSession {
            driver: None,
            baseurl: baseurl,
            client: Client::new(),
            session_id: session_id.to_owned(),
        };
        info!("Connecting to session at {} with id {}", url, session_id);
        // We can fetch any value for the session to verify it exists.
        // The page URL will work.
        s.get_current_url()?;
        info!("Connected to existing session {}", s.session_id);
        Ok(s)
    }

    pub fn session_id(&self) -> &str {
        &self.session_id
    }

    fn get<D: DeserializeOwned + Debug>(&self, path: &str) -> Result<D, Error> {
        let url = try!(self.baseurl.join(path)
                           .map_err(|_| Error::InvalidUrl));
        debug!("GET {}", url);
        let mut res = try!(self.client.get(url)
                            .send());
        Self::decode(&mut res)
    }

    fn delete<D: DeserializeOwned + Debug>(&self, path: &str) -> Result<D, Error> {
        let url = try!(self.baseurl.join(path)
                           .map_err(|_| Error::InvalidUrl));
        debug!("DELETE {}", url);
        let mut res = try!(self.client.delete(url)
                            .send());
        Self::decode(&mut res)
    }

    fn decode<D: DeserializeOwned + Debug>(res: &mut Response) -> Result<D, Error> {
        let mut data = String::new();
        try!(res.read_to_string(&mut data));
        debug!("result status: {}\n\
                body: '{}'", res.status, data);

        if !res.status.is_success() {
            let err: Value<WebDriverError> = try!(serde_json::from_str(&data));
            trace!("deserialize error result: {:#?}", err);
            return Err(Error::WebDriverError(err.value));
        }
        let response = serde_json::from_str(&data);
        trace!("deserialize result: {:#?}", response);
        Ok(response?)
    }

    fn post<D: DeserializeOwned + Debug, E: Serialize>(&self, path: &str, body: &E) -> Result<D, Error> {
        let url = try!(self.baseurl.join(path)
                           .map_err(|_| Error::InvalidUrl));
        let body_str = try!(serde_json::to_string(body));
        debug!("POST url: {}\n\
                body: {}", url, body_str);
        let mut res = try!(self.client.post(url)
                            .body(&body_str)
                            .send());
        Self::decode(&mut res)
    }

    /// Create a new webdriver session
    fn new_session(&mut self, params: &NewSessionCmd) -> Result<Session, Error> {
        let resp: Value<Session> = try!(self.post("/session", &params));
        Ok(resp.value)
    }

    /// Navigate to the given URL
    pub fn go(&self, url: &str) -> Result<(), Error> {
        let params = GoCmd { url: url.to_string() };
        let _: Empty = try!(self.post(&format!("/session/{}/url", &self.session_id), &params));
        Ok(())
    }

    pub fn get_current_url(&self) -> Result<String, Error> {
        let v: Value<_> = try!(self.get(&format!("/session/{}/url", self.session_id)));
        Ok(v.value)
    }

    pub fn back(&self) -> Result<(), Error> {
        let _: Empty = try!(self.post(&format!("/session/{}/back", self.session_id), &Empty {}));
        Ok(())
    }

    pub fn forward(&self) -> Result<(), Error> {
        let _: Empty = try!(self.post(&format!("/session/{}/forward", self.session_id), &Empty {}));
        Ok(())
    }

    pub fn refresh(&self) -> Result<(), Error> {
        let _: Empty = try!(self.post(&format!("/session/{}/refresh", self.session_id), &Empty {}));
        Ok(())
    }

    pub fn get_page_source(&self) -> Result<String, Error> {
        let v: Value<_> = try!(self.get(&format!("/session/{}/source", self.session_id)));
        Ok(v.value)
    }

    pub fn get_title(&self) -> Result<String, Error> {
        let v: Value<_> = try!(self.get(&format!("/session/{}/title", self.session_id)));
        Ok(v.value)
    }

    /// Get all cookies
    pub fn get_cookies(&self) -> Result<Vec<Cookie>, Error> {
        let v: Value<_> = try!(self.get(&format!("/session/{}/cookie", self.session_id)));
        Ok(v.value)
    }

    pub fn get_window_handle(&self) -> Result<String, Error> {
        let v: Value<_> = try!(self.get(&format!("/session/{}/window", self.session_id)));
        Ok(v.value)
    }

    pub fn switch_window(&mut self, handle: &str) -> Result<(), Error> {
        let _: Empty = try!(self.post(&format!("/session/{}/window", self.session_id), &SwitchWindowCmd::from(handle)));
        Ok(())
    }

    pub fn close_window(&mut self) -> Result<(), Error> {
        let _: Empty = try!(self.delete(&format!("/session/{}/window", self.session_id)));
        Ok(())
    }

    pub fn get_window_handles(&self) -> Result<Vec<String>, Error> {
        let v: Value<_> = try!(self.get(&format!("/session/{}/window/handles", self.session_id)));
        Ok(v.value)
    }

    pub fn find_element(&self, selector: &str, strategy: LocationStrategy) -> Result<Element<T>, Error> {
        let cmd = FindElementCmd { using: strategy, value: selector};
        let v: Value<ElementReference> = try!(self.post(&format!("/session/{}/element", self.session_id), &cmd));
        Ok(Element::new(self, v.value.reference))
    }

    pub fn find_elements(&self, selector: &str, strategy: LocationStrategy) -> Result<Vec<Element<T>>, Error> {
        let cmd = FindElementCmd { using: strategy, value: selector};
        let mut v: Value<Vec<ElementReference>> = try!(self.post(&format!("/session/{}/elements", self.session_id), &cmd));

        let mut elems = Vec::new();
        while let Some(er) = v.value.pop() {
            elems.push(Element::new(self, er.reference))
        }
        Ok(elems)
    }

    pub fn execute(&self, script: ExecuteCmd) -> Result<JsonValue, Error> {
        let v: Value<JsonValue> = try!(self.post(&format!("/session/{}/execute/sync", self.session_id), &script));
        Ok(v.value)
    }

    pub fn execute_async(&self, script: ExecuteCmd) -> Result<JsonValue, Error> {
        let v: Value<JsonValue> = try!(self.post(&format!("/session/{}/execute/async", self.session_id), &script));
        Ok(v.value)
    }

    pub fn switch_to_frame(&self, handle: JsonValue) -> Result<(), Error> {
        let _: Empty = try!(self.post(&format!("/session/{}/frame", self.session_id), &SwitchFrameCmd::from(handle)));
        Ok(())
    }
}

impl<T> Drop for DriverSession<T> {
    fn drop(&mut self) {
        let _: Result<Empty,_> = self.delete(&format!("/session/{}", self.session_id));
    }
}

pub struct Element<'a, T: 'a> {
    session: &'a DriverSession<T>,
    reference: String,
}

impl<'a, T> Element<'a, T> {
    fn new(s: &'a DriverSession<T>, reference: String) -> Self {
        Element { session: s, reference: reference }
    }

    pub fn attribute(&self, name: &str) -> Result<String, Error> {
        let v: Value<_> = try!(self.session.get(&format!("/session/{}/element/{}/attribute/{}", self.session.session_id(), self.reference, name)));
        Ok(v.value)
    }

//    pub fn property(&self, name: &str) -> Result<String, Error> {
//        let v: Value<_> = try!(self.get(&format!("/session/{}/element/{}/property/{}", self.session_id, el.reference, name)));
//        Ok(v.value)
//    }

    pub fn css_value(&self, name: &str) -> Result<String, Error> {
        let v: Value<_> = try!(self.session.get(&format!("/session/{}/element/{}/css/{}", self.session.session_id(), self.reference, name)));
        Ok(v.value)
    }

    pub fn text(&self) -> Result<String, Error> {
        let v: Value<_> = try!(self.session.get(&format!("/session/{}/element/{}/text", self.session.session_id(), self.reference)));
        Ok(v.value)
    }

    /// Returns the tag name for this element
    pub fn name(&self) -> Result<String, Error> {
        let v: Value<_> = try!(self.session.get(&format!("/session/{}/element/{}/name", self.session.session_id(), self.reference)));
        Ok(v.value)
    }

    pub fn reference(&self) -> Result<JsonValue, Error> {
        serde_json::to_value(&ElementReference::from_str(&self.reference))
            .map_err(|err| Error::from(err))
    }

    /// Gets the `innerHTML` javascript attribute for this element. Some drivers can get
    /// this using regular attributes, in others it does not work. This method gets it
    /// executing a bit of javascript.
    pub fn inner_html(&self) -> Result<JsonValue, Error> {
        let script = ExecuteCmd {
            script: "return arguments[0].innerHTML;".to_owned(),
            args: vec![self.reference()?],
        };
        self.session.execute(script)
    }

    pub fn outer_html(&self) -> Result<JsonValue, Error> {
        let script = ExecuteCmd {
            script: "return arguments[0].outerHTML;".to_owned(),
            args: vec![self.reference()?],
        };
        self.session.execute(script)
    }
}

/// Switch the context of the current session to the given frame reference.
///
/// This structure implements Drop, and restores the session context
/// to the current top level window.
pub struct FrameContext<'a, T: 'a> {
    session: &'a DriverSession<T>,
}

impl<'a, T> FrameContext<'a, T> {
    pub fn new(session: &DriverSession<T>, frameref: JsonValue) -> Result<FrameContext<T>, Error> {
        session.switch_to_frame(frameref)?;
        Ok(FrameContext { session: session })
    }
}

impl<'a, T> Drop for FrameContext<'a, T> {
    fn drop(&mut self) {
        let _ = self.session.switch_to_frame(JsonValue::Null);
    }
}
<|endoftext|>


<commit_before>#![crate_name = "spinlock"]
#![experimental]
#![crate_type = "lib"]
#![feature(unsafe_destructor)]
#![feature(associated_types)]
#![warn(missing_docs)]

#![no_std]

//! This crate provides a thread-safe data container.
//!
//! # Description
//!
//! This structure behaves a lot like a normal Mutex. There are some differences:
//!
//! - It may be used outside the runtime.
//!   - A normal mutex will fail when used without the runtime, this will just lock
//!   - When the runtime is present, it will call the deschedule function when appropriate
//! - No lock poisoning. When a fail occurs when the lock is held, no guarantees are made
//!
//! When calling rust functions from bare threads, such as C `pthread`s, this lock will be very
//! helpfull. In other cases however, you are encouraged to use the locks from the standard
//! library.
//!
//! # Simple example
//!
//! ```
//! use spinlock::Spinlock;
//! let spinlock = Spinlock::new(0u);
//!
//! // Modify the data
//! {
//!     let mut data = spinlock.lock();
//!     *data = 2;
//! }
//!
//! // Read the data
//! let answer =
//! {
//!     let data = spinlock.lock();
//!     *data
//! };
//!
//! assert_eq!(answer, 2u);
//! ```
//!
//! # Thread-safety example
//!
//! ```
//! use spinlock::Spinlock;
//! use std::sync::{Arc, Barrier};
//!
//! let numthreads = 1000;
//! let spinlock = Arc::new(Spinlock::new(0u));
//!
//! // We use a barrier to ensure the readout happens after all writing
//! let barrier = Arc::new(Barrier::new(numthreads + 1));
//!
//! for _ in range(0, numthreads)
//! {
//!     let my_barrier = barrier.clone();
//!     let my_lock = spinlock.clone();
//!     std::thread::Thread::spawn(move||
//!     {
//!         let mut guard = my_lock.lock();
//!         *guard += 1;
//!
//!         // Release the lock to prevent a deadlock
//!         drop(guard);
//!         my_barrier.wait();
//!     }).detach();
//! }
//!
//! barrier.wait();
//!
//! let answer = { *spinlock.lock() };
//! assert_eq!(answer, numthreads);
//! ```

#[cfg(test)] extern crate std;
extern crate core;

use core::atomic::{AtomicBool, Ordering, ATOMIC_BOOL_INIT};
use core::cell::UnsafeCell;
use core::kinds::Sync;
use core::ops::{Drop, Deref, DerefMut};

/// A wrapper for the data giving access in a thread-safe manner
pub struct Spinlock<T>
{
    lock: AtomicBool,
    data: UnsafeCell<T>,
}

/// A guard to which the protected data can be accessed
///
/// When the guard falls out of scope it will release the lock.
#[experimental]
pub struct SpinlockGuard<'a, T:'a>
{
    lock: &'a AtomicBool,
    data: &'a mut T,
}

unsafe impl<T> Sync for Spinlock<T> {}

/// A Spinlock which may be used statically.
///
/// ```
/// use spinlock::{StaticSpinlock, STATIC_SPINLOCK_INIT};
///
/// static SPLCK: StaticSpinlock = STATIC_SPINLOCK_INIT;
///
/// fn demo() {
///     let lock = SPLCK.lock();
///     // do something with lock
///     drop(lock);
/// }
/// ```
pub type StaticSpinlock = Spinlock<()>;

/// A initializer for StaticSpinlock, containing no data.
pub const STATIC_SPINLOCK_INIT: StaticSpinlock = Spinlock {
    lock: ATOMIC_BOOL_INIT,
    data: UnsafeCell { value: () },
};

#[deprecated = "renamed to STATIC_SPINLOCK_INIT"]
pub const INIT_STATIC_SPINLOCK: StaticSpinlock = STATIC_SPINLOCK_INIT;

impl<T> Spinlock<T>
{
    /// Creates a new spinlock wrapping the supplied data.
    #[unstable]
    pub fn new(user_data: T) -> Spinlock<T>
    {
        Spinlock
        {
            lock: ATOMIC_BOOL_INIT,
            data: UnsafeCell::new(user_data),
        }
    }

    fn obtain_lock(&self)
    {
        while self.lock.compare_and_swap(false, true, Ordering::SeqCst) != false
        {
            // Do nothing
        }
    }

    /// Locks the spinlock and returns a guard.
    ///
    /// The returned value may be dereferenced for data access
    /// and the lock will be dropped when the guard falls out of scope.
    ///
    /// ```
    /// let mylock = spinlock::Spinlock::new(0u);
    /// {
    ///     let mut data = mylock.lock();
    ///     // The lock is now locked and the data can be accessed
    ///     *data += 1;
    /// }
    /// // The lock is dropped
    /// ```
    pub fn lock(&self) -> SpinlockGuard<T>
    {
        self.obtain_lock();
        SpinlockGuard
        {
            lock: &self.lock,
            data: unsafe { &mut *self.data.get() },
        }
    }
}

impl<'a, T> Deref for SpinlockGuard<'a, T>
{
    type Target = T;
    fn deref<'b>(&'b self) -> &'b T { &*self.data }
}

impl<'a, T> DerefMut for SpinlockGuard<'a, T>
{
    fn deref_mut<'b>(&'b mut self) -> &'b mut T { &mut *self.data }
}

#[unsafe_destructor]
impl<'a, T> Drop for SpinlockGuard<'a, T>
{
    /// The dropping of the SpinlockGuard will release the lock it was created from.
    fn drop(&mut self)
    {
        self.lock.store(false, Ordering::SeqCst);
    }
}
<commit_msg>Remove #![feature(associated_types)]<commit_after>#![crate_name = "spinlock"]
#![experimental]
#![crate_type = "lib"]
#![feature(unsafe_destructor)]
#![warn(missing_docs)]

#![no_std]

//! This crate provides a thread-safe data container.
//!
//! # Description
//!
//! This structure behaves a lot like a normal Mutex. There are some differences:
//!
//! - It may be used outside the runtime.
//!   - A normal mutex will fail when used without the runtime, this will just lock
//!   - When the runtime is present, it will call the deschedule function when appropriate
//! - No lock poisoning. When a fail occurs when the lock is held, no guarantees are made
//!
//! When calling rust functions from bare threads, such as C `pthread`s, this lock will be very
//! helpfull. In other cases however, you are encouraged to use the locks from the standard
//! library.
//!
//! # Simple example
//!
//! ```
//! use spinlock::Spinlock;
//! let spinlock = Spinlock::new(0u);
//!
//! // Modify the data
//! {
//!     let mut data = spinlock.lock();
//!     *data = 2;
//! }
//!
//! // Read the data
//! let answer =
//! {
//!     let data = spinlock.lock();
//!     *data
//! };
//!
//! assert_eq!(answer, 2u);
//! ```
//!
//! # Thread-safety example
//!
//! ```
//! use spinlock::Spinlock;
//! use std::sync::{Arc, Barrier};
//!
//! let numthreads = 1000;
//! let spinlock = Arc::new(Spinlock::new(0u));
//!
//! // We use a barrier to ensure the readout happens after all writing
//! let barrier = Arc::new(Barrier::new(numthreads + 1));
//!
//! for _ in range(0, numthreads)
//! {
//!     let my_barrier = barrier.clone();
//!     let my_lock = spinlock.clone();
//!     std::thread::Thread::spawn(move||
//!     {
//!         let mut guard = my_lock.lock();
//!         *guard += 1;
//!
//!         // Release the lock to prevent a deadlock
//!         drop(guard);
//!         my_barrier.wait();
//!     }).detach();
//! }
//!
//! barrier.wait();
//!
//! let answer = { *spinlock.lock() };
//! assert_eq!(answer, numthreads);
//! ```

#[cfg(test)] extern crate std;
extern crate core;

use core::atomic::{AtomicBool, Ordering, ATOMIC_BOOL_INIT};
use core::cell::UnsafeCell;
use core::kinds::Sync;
use core::ops::{Drop, Deref, DerefMut};

/// A wrapper for the data giving access in a thread-safe manner
pub struct Spinlock<T>
{
    lock: AtomicBool,
    data: UnsafeCell<T>,
}

/// A guard to which the protected data can be accessed
///
/// When the guard falls out of scope it will release the lock.
#[experimental]
pub struct SpinlockGuard<'a, T:'a>
{
    lock: &'a AtomicBool,
    data: &'a mut T,
}

unsafe impl<T> Sync for Spinlock<T> {}

/// A Spinlock which may be used statically.
///
/// ```
/// use spinlock::{StaticSpinlock, STATIC_SPINLOCK_INIT};
///
/// static SPLCK: StaticSpinlock = STATIC_SPINLOCK_INIT;
///
/// fn demo() {
///     let lock = SPLCK.lock();
///     // do something with lock
///     drop(lock);
/// }
/// ```
pub type StaticSpinlock = Spinlock<()>;

/// A initializer for StaticSpinlock, containing no data.
pub const STATIC_SPINLOCK_INIT: StaticSpinlock = Spinlock {
    lock: ATOMIC_BOOL_INIT,
    data: UnsafeCell { value: () },
};

#[deprecated = "renamed to STATIC_SPINLOCK_INIT"]
pub const INIT_STATIC_SPINLOCK: StaticSpinlock = STATIC_SPINLOCK_INIT;

impl<T> Spinlock<T>
{
    /// Creates a new spinlock wrapping the supplied data.
    #[unstable]
    pub fn new(user_data: T) -> Spinlock<T>
    {
        Spinlock
        {
            lock: ATOMIC_BOOL_INIT,
            data: UnsafeCell::new(user_data),
        }
    }

    fn obtain_lock(&self)
    {
        while self.lock.compare_and_swap(false, true, Ordering::SeqCst) != false
        {
            // Do nothing
        }
    }

    /// Locks the spinlock and returns a guard.
    ///
    /// The returned value may be dereferenced for data access
    /// and the lock will be dropped when the guard falls out of scope.
    ///
    /// ```
    /// let mylock = spinlock::Spinlock::new(0u);
    /// {
    ///     let mut data = mylock.lock();
    ///     // The lock is now locked and the data can be accessed
    ///     *data += 1;
    /// }
    /// // The lock is dropped
    /// ```
    pub fn lock(&self) -> SpinlockGuard<T>
    {
        self.obtain_lock();
        SpinlockGuard
        {
            lock: &self.lock,
            data: unsafe { &mut *self.data.get() },
        }
    }
}

impl<'a, T> Deref for SpinlockGuard<'a, T>
{
    type Target = T;
    fn deref<'b>(&'b self) -> &'b T { &*self.data }
}

impl<'a, T> DerefMut for SpinlockGuard<'a, T>
{
    fn deref_mut<'b>(&'b mut self) -> &'b mut T { &mut *self.data }
}

#[unsafe_destructor]
impl<'a, T> Drop for SpinlockGuard<'a, T>
{
    /// The dropping of the SpinlockGuard will release the lock it was created from.
    fn drop(&mut self)
    {
        self.lock.store(false, Ordering::SeqCst);
    }
}
<|endoftext|>


<commit_before>#![deny(missing_copy_implementations,
        trivial_casts, trivial_numeric_casts,
        unsafe_code,
        unstable_features,
        unused_import_braces, unused_qualifications)]

#![cfg_attr(feature = "unstable", allow(unstable_features))]
#![cfg_attr(feature = "unstable", feature(plugin))]
#![cfg_attr(feature = "unstable", plugin(clippy))]

#![cfg_attr(test, allow(unsafe_code))]

extern crate byteorder;
extern crate flate2;
extern crate regex;
extern crate tabwriter;
extern crate tar;
extern crate time;
#[macro_use]
extern crate try_opt;
#[cfg(test)]
#[macro_use]
extern crate lazy_static;

mod macros;
mod time_utils;
pub mod backend;
pub mod collections;
pub mod signatures;

use std::cell::RefCell;

pub struct Backup {
    collections: RefCell<Option<collections::CollectionsStatus>>,
    signatures: RefCell<Option<signatures::BackupFiles>>,
}

impl Backup {
    pub fn new() -> Self {
        Backup {
            collections: RefCell::new(None),
            signatures: RefCell::new(None),
        }
    }

    pub fn collections(&self) -> &collections::CollectionsStatus {
//        {
//            // check if there is a cached collections value
//            let mut coll = self.collections.borrow_mut();
//            if coll.is_some() {
//                return coll.as_ref().unwrap()
//            }
//            // compute collections now
//        }

        // recursive call to return the just cached value
        // need to close previous scope
        self.collections()
    }
}
<commit_msg>Implemented caching for collections getter<commit_after>#![deny(missing_copy_implementations,
        trivial_casts, trivial_numeric_casts,
        unsafe_code,
        unstable_features,
        unused_import_braces, unused_qualifications)]

#![cfg_attr(feature = "unstable", allow(unstable_features))]
#![cfg_attr(feature = "unstable", feature(plugin))]
#![cfg_attr(feature = "unstable", plugin(clippy))]

#![cfg_attr(test, allow(unsafe_code))]

extern crate byteorder;
extern crate flate2;
extern crate regex;
extern crate tabwriter;
extern crate tar;
extern crate time;
#[macro_use]
extern crate try_opt;
#[cfg(test)]
#[macro_use]
extern crate lazy_static;

mod macros;
mod time_utils;
pub mod backend;
pub mod collections;
pub mod signatures;

use std::cell::{Ref, RefCell};
use std::io;

use backend::Backend;
use collections::Collections;
use signatures::BackupFiles;

pub struct Backup<'a, B: Backend + 'a> {
    backend: &'a B,
    collections: RefCell<Option<Collections>>,
    signatures: RefCell<Option<BackupFiles>>,
}

pub struct Snapshots<'a>(Ref<'a, Option<Collections>>);

impl<'a, B: Backend> Backup<'a, B> {
    pub fn new(backend: &'a B) -> Self {
        Backup {
            backend: backend,
            collections: RefCell::new(None),
            signatures: RefCell::new(None),
        }
    }

    pub fn collections(&self) -> io::Result<Snapshots> {
        {
            // check if there is a cached collections value
            let mut coll = self.collections.borrow_mut();
            if coll.is_none() {
                // compute collections now
                let filenames = try!(self.backend.get_file_names());
                *coll = Some(Collections::from_filenames(filenames));
            }
        }

        // need to close previous scope to borrow again
        // return the cached value
        Ok(Snapshots(self.collections.borrow()))
    }
}
<|endoftext|>


<commit_before>/*!
A macro for declaring lazily evaluated statics.

Using this macro, it is possible to have `static`s that require code to be
executed at runtime in order to be initialized.
This includes anything requiring heap allocations, like vectors or hash maps,
as well as anything that requires function calls to be computed.

# Syntax

```ignore
lazy_static! {
    [pub] static ref NAME_1: TYPE_1 = EXPR_1;
    [pub] static ref NAME_2: TYPE_2 = EXPR_2;
    ...
    [pub] static ref NAME_N: TYPE_N = EXPR_N;
}
```

# Semantic

For a given `static ref NAME: TYPE = EXPR;`, the macro generates a
unique type that implements `Deref<TYPE>` and stores it in a static with name `NAME`.

On first deref, `EXPR` gets evaluated and stored internally, such that all further derefs
can return a reference to the same object.

Like regular `static mut`s, this macro only works for types that fulfill the `Sync`
trait.

# Example

Using the macro:

```rust
#[macro_use]
extern crate lazy_static;

use std::collections::HashMap;

lazy_static! {
    static ref HASHMAP: HashMap<u32, &'static str> = {
        let mut m = HashMap::new();
        m.insert(0, "foo");
        m.insert(1, "bar");
        m.insert(2, "baz");
        m
    };
    static ref COUNT: usize = HASHMAP.len();
    static ref NUMBER: u32 = times_two(21);
}

fn times_two(n: u32) -> u32 { n * 2 }

fn main() {
    println!("The map has {} entries.", *COUNT);
    println!("The entry for `0` is \"{}\".", HASHMAP.get(&0).unwrap());
    println!("A expensive calculation on a static results in: {}.", *NUMBER);
}
```

# Implementation details

The `Deref` implementation uses a hidden `static mut` that is guarded by a atomic check
using the `sync::Once` abstraction. All lazily evaluated values are currently
put in a heap allocated box, due to the Rust language currently not providing any way to
define uninitialized `static mut` values.

*/
#![feature(no_std)]
#![no_std]

#![crate_type = "dylib"]

#[macro_export]
macro_rules! lazy_static {
    (static ref $N:ident : $T:ty = $e:expr; $($t:tt)*) => {
        lazy_static!(PRIV static ref $N : $T = $e; $($t)*);
    };
    (pub static ref $N:ident : $T:ty = $e:expr; $($t:tt)*) => {
        lazy_static!(PUB static ref $N : $T = $e; $($t)*);
    };
    ($VIS:ident static ref $N:ident : $T:ty = $e:expr; $($t:tt)*) => {
        lazy_static!(MAKE TY $VIS $N);
        impl ::std::ops::Deref for $N {
            type Target = $T;
            fn deref<'a>(&'a self) -> &'a $T {
                use alloc::boxed::Box;

                #[inline(always)]
                fn __static_ref_initialize() -> Box<$T> { Box::new($e) }

                unsafe {
                    use core::mem::transmute;

                    #[inline(always)]
                    fn require_sync<T: Sync>(_: &T) { }

                    static mut ONCE: bool = false;
                    static mut DATA: *const $T = 0 as *const $T;

                    if !ONCE { // TODO(ryan): race condition
                        DATA = transmute::<Box<$T>, *const $T>(__static_ref_initialize());
                        ONCE = true
                    }

                    let static_ref = &*DATA;
                    require_sync(static_ref);
                    static_ref
                }
            }
        }
        lazy_static!($($t)*);
    };
    (MAKE TY PUB $N:ident) => {
        #[allow(missing_copy_implementations)]
        #[allow(non_camel_case_types)]
        #[allow(dead_code)]
        pub struct $N {__private_field: ()}
        pub static $N: $N = $N {__private_field: ()};
    };
    (MAKE TY PRIV $N:ident) => {
        #[allow(missing_copy_implementations)]
        #[allow(non_camel_case_types)]
        #[allow(dead_code)]
        struct $N {__private_field: ()}
        static $N: $N = $N {__private_field: ()};
    };
    () => ()
}
<commit_msg>~ don't require sync (for now)<commit_after>/*!
A macro for declaring lazily evaluated statics.

Using this macro, it is possible to have `static`s that require code to be
executed at runtime in order to be initialized.
This includes anything requiring heap allocations, like vectors or hash maps,
as well as anything that requires function calls to be computed.

# Syntax

```ignore
lazy_static! {
    [pub] static ref NAME_1: TYPE_1 = EXPR_1;
    [pub] static ref NAME_2: TYPE_2 = EXPR_2;
    ...
    [pub] static ref NAME_N: TYPE_N = EXPR_N;
}
```

# Semantic

For a given `static ref NAME: TYPE = EXPR;`, the macro generates a
unique type that implements `Deref<TYPE>` and stores it in a static with name `NAME`.

On first deref, `EXPR` gets evaluated and stored internally, such that all further derefs
can return a reference to the same object.

Like regular `static mut`s, this macro only works for types that fulfill the `Sync`
trait.

# Example

Using the macro:

```rust
#[macro_use]
extern crate lazy_static;

use std::collections::HashMap;

lazy_static! {
    static ref HASHMAP: HashMap<u32, &'static str> = {
        let mut m = HashMap::new();
        m.insert(0, "foo");
        m.insert(1, "bar");
        m.insert(2, "baz");
        m
    };
    static ref COUNT: usize = HASHMAP.len();
    static ref NUMBER: u32 = times_two(21);
}

fn times_two(n: u32) -> u32 { n * 2 }

fn main() {
    println!("The map has {} entries.", *COUNT);
    println!("The entry for `0` is \"{}\".", HASHMAP.get(&0).unwrap());
    println!("A expensive calculation on a static results in: {}.", *NUMBER);
}
```

# Implementation details

The `Deref` implementation uses a hidden `static mut` that is guarded by a atomic check
using the `sync::Once` abstraction. All lazily evaluated values are currently
put in a heap allocated box, due to the Rust language currently not providing any way to
define uninitialized `static mut` values.

*/
#![feature(no_std)]
#![no_std]

#![crate_type = "dylib"]

#[macro_export]
macro_rules! lazy_static {
    (static ref $N:ident : $T:ty = $e:expr; $($t:tt)*) => {
        lazy_static!(PRIV static ref $N : $T = $e; $($t)*);
    };
    (pub static ref $N:ident : $T:ty = $e:expr; $($t:tt)*) => {
        lazy_static!(PUB static ref $N : $T = $e; $($t)*);
    };
    ($VIS:ident static ref $N:ident : $T:ty = $e:expr; $($t:tt)*) => {
        lazy_static!(MAKE TY $VIS $N);
        impl ::std::ops::Deref for $N {
            type Target = $T;
            fn deref<'a>(&'a self) -> &'a $T {
                use alloc::boxed::Box;

                #[inline(always)]
                fn __static_ref_initialize() -> Box<$T> { Box::new($e) }

                unsafe {
                    use core::mem::transmute;

                    static mut ONCE: bool = false;
                    static mut DATA: *const $T = 0 as *const $T;

                    if !ONCE { // TODO(ryan): race condition
                        DATA = transmute::<Box<$T>, *const $T>(__static_ref_initialize());
                        ONCE = true
                    }

                    let static_ref = &*DATA;
                    // TODO(ryan): require_sync(static_ref);
                    static_ref
                }
            }
        }
        lazy_static!($($t)*);
    };
    (MAKE TY PUB $N:ident) => {
        #[allow(missing_copy_implementations)]
        #[allow(non_camel_case_types)]
        #[allow(dead_code)]
        pub struct $N {__private_field: ()}
        pub static $N: $N = $N {__private_field: ()};
    };
    (MAKE TY PRIV $N:ident) => {
        #[allow(missing_copy_implementations)]
        #[allow(non_camel_case_types)]
        #[allow(dead_code)]
        struct $N {__private_field: ()}
        static $N: $N = $N {__private_field: ()};
    };
    () => ()
}
<|endoftext|>


<commit_before>//! Please contact me on github and file any issues if you find some, I'm also open to PRs or other suggestions.
//!
//! Updated to Tera 0.10 / Serde 1.0 / Iron 0.5 !
//! Serde 0.9.0 to_value returns a `Result`, this means you need to handle the possiblity of a serialization failure.
//! If you just want to `unwrap()` there is an implementation of From<Value> for TemplateMode so `Template::new(path, value)`
//! works also.
//!
//! **update iron-tera-0.4.0**: If you build this crate with feature = "unstable" on the nightly compiler,
//! I've included a TryFrom impl so that you can use try without having to unwrap. Instead of
//! `TemplateMode::from_serial` for `serde_json::Value`s use `TemplateMode::try_serialize`.
//! `try_serialize` returns a `Result`, allowing you to explicitly handle or propagate up.
//!
//!
//! ## Examples
//! The following is a complete working example that I've tested with serde 0.9.0, tera 0.7.0 and iron-tera 0.2.0
//!
//! ```ignore
//! extern crate tera;
//! extern crate iron;
//! extern crate router;
//! #[macro_use] extern crate serde_json;
//! extern crate iron_tera;
//! fn main() {
//!     use iron::prelude::*;
//!     use iron::status;
//!     use router::Router;
//!     use tera::Context;
//!
//!     use iron_tera::{Template, TeraEngine}; // import TemplateMode to explicitly handle serialization errors
//!
//!     let mut router = Router::new();
//!     router.get("/context", context_handler, "context");
//!     router.get("/json", json_handler, "json");
//!
//!     let mut chain = Chain::new(router);
//!     let teng = TeraEngine::new("src/examples/templates/**/*");
//!     chain.link_after(teng);
//!
//!     Iron::new(chain).http("localhost:5000").unwrap();
//!
//!
//!     fn context_handler(_: &mut Request) -> IronResult<Response> {
//!         let mut resp = Response::new();
//!
//!         let mut context = Context::new();
//!         context.add("username", &"Bob");
//!         context.add("my_var", &"Thing"); // comment out to see alternate thing
//!         context.add("numbers", &vec![1, 2, 3]);
//!         context.add("bio", &"<script>alert('pwnd');</script>");
//!
//!         // can use Template::new(path, TemplateMode::from_context(context)) or TemplateMode::from(context) also
//!         resp.set_mut(Template::new("users/profile.html", context))
//!             .set_mut(status::Ok);
//!         Ok(resp)
//!     }
//!     fn json_handler(_: &mut Request) -> IronResult<Response> {
//!         let mut resp = Response::new();
//!
//!         let blob = json!({
//!             "username": "John Doe",
//!             "my_var": "Thing",
//!             "numbers": [
//!                 "1",
//!                 "+44 2345678",
//!                 "3"
//!             ],
//!             "bio": "<script>alert('pwnd');</script>"
//!          });
//!         // you can use Template::new(path, TemplateMode::from_serial(serde_json::Value)) to handle
//!         // serialization error explicitly
//!         resp.set_mut(Template::new("users/profile.html", blob))
//!             .set_mut(status::Ok);
//!         Ok(resp)
//!     }
//! }
//! ```
//!
//! Creating a template from a struct
//!
//! ```ignore
//! // The following uses serde's Serialize
//! #[derive(Serialize)]
//! struct Product {
//!     name: String,
//!     value: i32,
//! }
//! // Rendering from a struct that implements Serialize
//! fn produce_handler(_: &mut Request) -> IronResult<Response> {
//!     let mut resp = Response::new();
//!
//!     // Using serialized values
//!     let product = Product {
//!         name: "Foo".into(),
//!         value: 42,
//!     };
//!     // You can use TemplateMode::from()
//!     resp.set_mut(Template::new("product.html", product))
//!         .set_mut(status::Ok);
//!     Ok(resp)
//! }
//! ```
#![cfg_attr(feature = "unstable", feature(try_from))]

#![allow(dead_code)]
#[macro_use]
extern crate serde_json;
#[macro_use]
extern crate tera;
extern crate iron;
extern crate serde;
extern crate plugin;

use iron::{AfterMiddleware, status, typemap};
use iron::headers::ContentType;
use iron::modifier::Modifier;
use iron::prelude::*;

use plugin::Plugin;

use serde::ser::Serialize;
use serde_json::{Value, to_value};
use std::convert::From;

#[cfg(feature = "unstable")]
use std::convert::TryFrom;

use tera::{Context, Tera};

/// There are 2 main ways to pass data to generate a template.
#[derive(Clone)]
pub enum TemplateMode {
    /// TeraContext constructor takes a `Context`
    TeraContext(Context),
    /// Serialized constructor takes a `Value` from `serde_json`
    Serialized(Value),
}

/// TemplateMode should only ever be created from these smart constructors,
/// not with the enums type constructors.
impl TemplateMode {
    pub fn from_context(context: Context) -> TemplateMode {
        TemplateMode::TeraContext(context)
    }

    #[cfg(feature = "unstable")]
    pub fn try_serialize<S: Serialize>(
        serializeable: S,
    ) -> Result<TemplateMode, serde_json::Error> {
        Ok(TemplateMode::try_from(serializeable)?)
    }

    #[cfg(not(feature = "unstable"))]
    pub fn from_serial<S: Serialize>(serializeable: S) -> Result<TemplateMode, serde_json::Error> {
        Ok(TemplateMode::Serialized(to_value(serializeable)?))
    }
}

impl From<Context> for TemplateMode {
    fn from(context: Context) -> Self {
        TemplateMode::from_context(context)
    }
}

#[cfg(not(feature = "unstable"))]
impl From<Value> for TemplateMode {
    fn from(serializeable: Value) -> Self {
        TemplateMode::from_serial(serializeable).unwrap()
    }
}

#[cfg(feature = "unstable")]
impl<S> TryFrom<S> for TemplateMode
where
    S: Serialize,
{
    type Error = serde_json::Error;
    fn try_from(value: S) -> Result<Self, Self::Error> {
        from_serde(value)
    }
}

#[cfg(feature = "unstable")]
fn from_serde<S: Serialize>(serializeable: S) -> Result<TemplateMode, serde_json::Error> {
    Ok(TemplateMode::Serialized(to_value(serializeable)?))
}

/// Our template holds a name (path to template) and a mode (constructed with `from_context` or `from_serial`)
#[derive(Clone)]
pub struct Template {
    mode: TemplateMode,
    name: String,
}

impl Template {
    pub fn new<T: Into<TemplateMode>, S: Into<String>>(name: S, mode: T) -> Template {
        Template {
            name: name.into(),
            mode: mode.into(),
        }
    }
}

/// TeraEngine holds the Tera struct so that it can be used by many handlers without explicitly passing
pub struct TeraEngine {
    pub tera: Tera,
}

/// `compile_templates!` is used to parse the contents of a dir for all templates.
impl TeraEngine {
    /// Take a `String` and convert to a slice
    pub fn new<S: AsRef<str>>(dir: S) -> TeraEngine {
        TeraEngine { tera: compile_templates!(dir.as_ref()) }
    }
}

impl typemap::Key for TeraEngine {
    type Value = Template;
}

impl Modifier<Response> for Template {
    fn modify(self, resp: &mut Response) {
        resp.extensions.insert::<TeraEngine>(self);
    }
}

/// The middleware implementation for TeraEngine
impl AfterMiddleware for TeraEngine {
    /// This is where all the magic happens. We extract `TeraEngine` from Iron's `Response`,
    /// determine what `TemplateMode` we should render in, and pass the appropriate values to
    /// tera's render methods.
    fn after(&self, _: &mut Request, mut resp: Response) -> IronResult<Response> {
        let wrapper = resp.extensions.remove::<TeraEngine>().and_then(
            |t| match t.mode {
                TemplateMode::TeraContext(ref context) => Some(self.tera.render(&t.name, context)),
                TemplateMode::Serialized(ref value) => Some(self.tera.render(&t.name, value)),
            },
        );
        match wrapper {
            Some(result) => {
                result
                    .map_err(|e| IronError::new(e, status::InternalServerError))
                    .and_then(|page| {
                        if !resp.headers.has::<ContentType>() {
                            resp.headers.set(ContentType::html());
                        }
                        resp.set_mut(page);
                        Ok(resp)
                    })
            }
            None => Ok(resp),
        }
    }

    fn catch(&self, req: &mut Request, mut err: IronError) -> IronResult<Response> {
        err.response = self.after(req, err.response)?;
        Err(err)
    }
}

impl Plugin<Response> for TeraEngine {
    type Error = ();

    fn eval(resp: &mut Response) -> Result<Template, ()> {
        match resp.extensions.get::<TeraEngine>() {
            Some(t) => Ok(t.clone()),
            None => Err(()),
        }
    }
}

#[cfg(test)]
mod tests {
    use super::{Template, TemplateMode, TeraEngine};
    use iron::prelude::*;
    use tera::Context;

    fn from_context_response() -> IronResult<Response> {
        let resp = Response::new();
        let mut context = Context::new();
        context.add("greeting", &"hi!");
        Ok(resp.set(
            Template::new("./test_template/users/foo.html", context),
        ))
    }

    #[test]
    fn test_from_context() {
        let mut resp = from_context_response().ok().expect("response expected");
        match resp.get::<TeraEngine>() {
            Ok(h) => {
                assert_eq!(h.name, "./test_template/users/foo.html".to_string());
                if let TemplateMode::TeraContext(context) = h.mode {
                    assert_eq!(
                        context
                            .as_json()
                            .unwrap()
                            .get("greeting")
                            .unwrap()
                            .as_str()
                            .unwrap(),
                        "hi!"
                    );
                } else {
                    panic!("TeraContext expected");
                }
            }
            _ => panic!("template expected"),
        }
    }
}
<commit_msg>work on docs<commit_after>//! Please contact me on github and file any issues if you find some, I'm also open to PRs or other suggestions.
//!
//! Updated to Tera 0.10 / Serde 1.0 / Iron 0.5 !
//! Serde 0.9.0 to_value returns a `Result`, this means you need to handle the possiblity of a serialization failure.
//! If you just want to `unwrap()` there is an implementation of From<Value> for TemplateMode so `Template::new(path, value)`
//! works also.
//!
//! **update iron-tera-0.4.0**: If you build this crate with feature = "unstable" on the nightly compiler,
//! I've included a `TryFrom` impl to improve API ergonomics. Instead of
//! `TemplateMode::from_serial` use `value.try_into()`. This uses the `TryFrom` feature to return a fallible `Result`.
//!
//! ## Examples
//! The following is a complete working example that I've tested with serde 0.9.0, tera 0.7.0 and iron-tera 0.2.0
//!
//! ```ignore
//!   [dependencies]
//!   iron-tera = { version = "0.4.0" }  # optional:  features = [ "unstable" ]
//! ```
//!
//! Using `iron-tera` stable.
//! ```rust

//! extern crate tera;
//! extern crate iron;
//! extern crate router;
//! #[macro_use] extern crate serde_json;
//! #[macro_use] extern crate serde_derive;
//! extern crate iron_tera;
//!
//! # fn main() {
//! use iron::prelude::*;
//! use iron::status;
//! use router::Router;
//! use tera::Context;
//!
//! use iron_tera::{Template, TeraEngine}; // import TemplateMode to explicitly handle serialization errors
//! fn main() {
//!
//!     let mut router = Router::new();
//!     router.get("/context", context_handler, "context");
//!     router.get("/json", json_handler, "json");
//!
//!     let mut chain = Chain::new(router);
//!     let teng = TeraEngine::new("src/examples/templates/**/*");
//!     chain.link_after(teng);
//!
//!     Iron::new(chain).http("localhost:5000").unwrap();
//!
//!
//!     fn context_handler(_: &mut Request) -> IronResult<Response> {
//!         let mut resp = Response::new();
//!
//!         let mut context = Context::new();
//!         context.add("username", &"Bob");
//!         context.add("my_var", &"Thing"); // comment out to see alternate thing
//!         context.add("numbers", &vec![1, 2, 3]);
//!         context.add("bio", &"<script>alert('pwnd');</script>");
//!
//!         // can use Template::new(path, TemplateMode::from_context(context)) or TemplateMode::from(context) also
//!         resp.set_mut(Template::new("users/profile.html", context))
//!             .set_mut(status::Ok);
//!         Ok(resp)
//!     }
//!     fn json_handler(_: &mut Request) -> IronResult<Response> {
//!         let mut resp = Response::new();
//!
//!         let blob = json!({
//!             "username": "John Doe",
//!             "my_var": "Thing",
//!             "numbers": [
//!                 "1",
//!                 "+44 2345678",
//!                 "3"
//!             ],
//!             "bio": "<script>alert('pwnd');</script>"
//!          });
//!         // you can use blob.try_into() and handle the `Result` explicitly (not shown here)
//!         // on the `unstable` feature of `iron-tera`
//!         resp.set_mut(Template::new("users/profile.html", blob))
//!             .set_mut(status::Ok);
//!         Ok(resp)
//!     }
//! }
//! # }
//! ```
//!
//! Creating a template from a struct
//!
//! ```ignore
//! // The following uses serde's Serialize
//! #[derive(Serialize)]
//! struct Product {
//!     name: String,
//!     value: i32,
//! }
//! // Rendering from a struct that implements Serialize
//! fn produce_handler(_: &mut Request) -> IronResult<Response> {
//!     let mut resp = Response::new();
//!
//!     // Using serialized values
//!     let product = Product {
//!         name: "Foo".into(),
//!         value: 42,
//!     };
//!     // You can use TemplateMode::from()
//!     resp.set_mut(Template::new("product.html", product))
//!         .set_mut(status::Ok);
//!     Ok(resp)
//! }
//! ```
#![cfg_attr(feature = "unstable", feature(try_from))]

#![allow(dead_code)]
#[macro_use]
extern crate serde_json;
#[macro_use]
extern crate tera;
extern crate iron;
extern crate serde;
extern crate plugin;

use iron::{AfterMiddleware, status, typemap};
use iron::headers::ContentType;
use iron::modifier::Modifier;
use iron::prelude::*;

use plugin::Plugin;

use serde::ser::Serialize;
use serde_json::{Value, to_value};

#[cfg(feature = "unstable")]
use std::convert::{TryFrom, TryInto};
use std::convert::From;

use tera::{Context, Tera};

/// There are 2 main ways to pass data to generate a template.
#[derive(Clone)]
pub enum TemplateMode {
    /// TeraContext constructor takes a `Context`
    TeraContext(Context),
    /// Serialized constructor takes a `Value` from `serde_json`
    Serialized(Value),
}

/// TemplateMode should only ever be created from these smart constructors,
/// not with the enums type constructors.
impl TemplateMode {
    pub fn from_context(context: Context) -> TemplateMode {
        TemplateMode::TeraContext(context)
    }

    #[cfg(feature = "unstable")]
    pub fn try_serialize<S: Serialize>(
        serializeable: S,
    ) -> Result<TemplateMode, serde_json::Error> {
        Ok(serializeable.try_into()?)
    }

    #[cfg(not(feature = "unstable"))]
    pub fn from_serial<S: Serialize>(serializeable: S) -> Result<TemplateMode, serde_json::Error> {
        Ok(TemplateMode::Serialized(to_value(serializeable)?))
    }
}

impl From<Context> for TemplateMode {
    fn from(context: Context) -> Self {
        TemplateMode::from_context(context)
    }
}

#[cfg(not(feature = "unstable"))]
impl From<Value> for TemplateMode {
    fn from(serializeable: Value) -> Self {
        TemplateMode::from_serial(serializeable).unwrap()
    }
}

#[cfg(feature = "unstable")]
impl<S> TryFrom<S> for TemplateMode
where
    S: Serialize,
{
    type Error = serde_json::Error;
    fn try_from(value: S) -> Result<Self, Self::Error> {
        from_serde(value)
    }
}

#[cfg(feature = "unstable")]
fn from_serde<S: Serialize>(serializeable: S) -> Result<TemplateMode, serde_json::Error> {
    Ok(TemplateMode::Serialized(to_value(serializeable)?))
}

/// Our template holds a name (path to template) and a mode (constructed with `from_context` or `from_serial`)
#[derive(Clone)]
pub struct Template {
    mode: TemplateMode,
    name: String,
}

impl Template {
    #[cfg(not(feature = "unstable"))]
    pub fn new<T: Into<TemplateMode>, S: Into<String>>(name: S, mode: T) -> Template {
        Template {
            name: name.into(),
            mode: mode.into(),
        }
    }
    #[cfg(feature = "unstable")]
    pub fn new<S: Into<String>>(name: S, mode: TemplateMode) -> Template {
        Template {
            name: name.into(),
            mode,
        }
    }
}

/// TeraEngine holds the Tera struct so that it can be used by many handlers without explicitly passing
pub struct TeraEngine {
    pub tera: Tera,
}

/// `compile_templates!` is used to parse the contents of a dir for all templates.
impl TeraEngine {
    /// Take a `String` and convert to a slice
    pub fn new<S: AsRef<str>>(dir: S) -> TeraEngine {
        TeraEngine { tera: compile_templates!(dir.as_ref()) }
    }
}

impl typemap::Key for TeraEngine {
    type Value = Template;
}

impl Modifier<Response> for Template {
    fn modify(self, resp: &mut Response) {
        resp.extensions.insert::<TeraEngine>(self);
    }
}

/// The middleware implementation for TeraEngine
impl AfterMiddleware for TeraEngine {
    /// This is where all the magic happens. We extract `TeraEngine` from Iron's `Response`,
    /// determine what `TemplateMode` we should render in, and pass the appropriate values to
    /// tera's render methods.
    fn after(&self, _: &mut Request, mut resp: Response) -> IronResult<Response> {
        let wrapper = resp.extensions.remove::<TeraEngine>().and_then(
            |t| match t.mode {
                TemplateMode::TeraContext(ref context) => Some(self.tera.render(&t.name, context)),
                TemplateMode::Serialized(ref value) => Some(self.tera.render(&t.name, value)),
            },
        );
        match wrapper {
            Some(result) => {
                result
                    .map_err(|e| IronError::new(e, status::InternalServerError))
                    .and_then(|page| {
                        if !resp.headers.has::<ContentType>() {
                            resp.headers.set(ContentType::html());
                        }
                        resp.set_mut(page);
                        Ok(resp)
                    })
            }
            None => Ok(resp),
        }
    }

    fn catch(&self, req: &mut Request, mut err: IronError) -> IronResult<Response> {
        err.response = self.after(req, err.response)?;
        Err(err)
    }
}

impl Plugin<Response> for TeraEngine {
    type Error = ();

    fn eval(resp: &mut Response) -> Result<Template, ()> {
        match resp.extensions.get::<TeraEngine>() {
            Some(t) => Ok(t.clone()),
            None => Err(()),
        }
    }
}

#[cfg(test)]
mod tests {
    use super::{Template, TemplateMode, TeraEngine};
    use iron::prelude::*;
    use tera::Context;

    fn from_context_response() -> IronResult<Response> {
        let resp = Response::new();
        let mut context = Context::new();
        context.add("greeting", &"hi!");
        Ok(resp.set(
            Template::new("./test_template/users/foo.html", context),
        ))
    }

    #[test]
    fn test_from_context() {
        let mut resp = from_context_response().ok().expect("response expected");
        match resp.get::<TeraEngine>() {
            Ok(h) => {
                assert_eq!(h.name, "./test_template/users/foo.html".to_string());
                if let TemplateMode::TeraContext(context) = h.mode {
                    assert_eq!(
                        context
                            .as_json()
                            .unwrap()
                            .get("greeting")
                            .unwrap()
                            .as_str()
                            .unwrap(),
                        "hi!"
                    );
                } else {
                    panic!("TeraContext expected");
                }
            }
            _ => panic!("template expected"),
        }
    }
}
<|endoftext|>


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

use std::fmt;
use std::ptr;

type HttpCallback = extern fn(*mut HttpParser) -> *mut libc::c_int;
type HttpDataCallback = extern fn(*mut HttpParser, *const u32, libc::size_t) -> *mut libc::c_int;

#[repr(C)]
enum HttpParserType {
    HttpRequest,
    HttpResponse,
    HttpBoth
}

#[repr(C)]
struct HttpParser {
    // Private Interface
    internal_state: u32,
    nread: u32,
    content_length: u64,

    // Read-Only
    http_major: libc::c_ushort,
    http_minor: libc::c_ushort,
    extended_status: u32,

    // Public Interface
    data: *mut libc::c_void
}

#[repr(C)]
struct HttpParserSettings {
    on_message_begin: HttpCallback,
    on_url: HttpDataCallback,
    on_status: HttpDataCallback,
    on_header_field: HttpDataCallback,
    on_header_value: HttpDataCallback,
    on_headers_complete: HttpCallback,
    on_body: HttpDataCallback,
    on_message_complete: HttpCallback,
    on_chunk_header: HttpCallback,
    on_chunk_complete: HttpCallback
}

#[inline]
unsafe fn unwrap_parser<'a>(http: *mut HttpParser) -> &'a mut Parser<'a> {
    return &mut *((*http).data as *mut Parser);
}

macro_rules! notify_fn_wrapper {
    ( $callback:ident ) => ({
        extern "C" fn $callback(http: *mut HttpParser) -> *mut libc::c_int {
            unsafe {
                unwrap_parser(http).handler.$callback();
            };
            return 0 as *mut libc::c_int;
        };
        $callback
    });
}

macro_rules! data_fn_wrapper {
    ( $callback:ident ) => ({
        extern "C" fn $callback(http: *mut HttpParser, data: *const u32, size: libc::size_t) -> *mut libc::c_int {
            unsafe {
                let mut dst = Vec::<u8>::with_capacity(size as usize);
                dst.set_len(size as usize);
                ptr::copy(data as *const u8, dst.as_mut_ptr(), size as usize);

                let data = String::from_utf8(dst).unwrap();

                unwrap_parser(http).handler.$callback(&data);
            };
            return 0 as *mut libc::c_int;
        };

        $callback
    });
}

static CALLBACK_WRAPPERS: HttpParserSettings = HttpParserSettings {
    on_url: data_fn_wrapper!(on_url),
    on_message_begin: notify_fn_wrapper!(on_message_begin),
    on_status: data_fn_wrapper!(on_status),
    on_header_field: data_fn_wrapper!(on_header_field),
    on_header_value: data_fn_wrapper!(on_header_value),
    on_headers_complete: notify_fn_wrapper!(on_headers_complete),
    on_body: data_fn_wrapper!(on_body),
    on_message_complete: notify_fn_wrapper!(on_message_complete),
    on_chunk_header: notify_fn_wrapper!(on_chunk_header),
    on_chunk_complete: notify_fn_wrapper!(on_chunk_complete)
};

extern "C" {
    fn http_parser_version() -> u32;
    fn http_parser_init(parser: *mut HttpParser, parser_type: HttpParserType);
    fn http_parser_settings_init(settings: *mut HttpParserSettings);
    fn http_parser_execute(parser: *mut HttpParser, settings: *const HttpParserSettings, data: *const u8, len: libc::size_t) -> libc::size_t;
    // fn http_method_str(m: HttpMethod);
}

// Rust Interface
pub trait ParserHandler {
    fn on_url(&self, &String) { }
    fn on_status(&self, &String) { }
    fn on_header_field(&self, &String) { }
    fn on_header_value(&self, &String) { }
    fn on_body(&self, &String) { }
    fn on_message_begin(&self) { }
    fn on_headers_complete(&self) { }
    fn on_message_complete(&self) { }
    fn on_chunk_header(&self) { }
    fn on_chunk_complete(&self) { }
}

pub struct Parser<'a> {
    handler: &'a ParserHandler,
    state: HttpParser
}

impl<'a> Parser<'a> {
    pub fn new(handler: &'a ParserHandler) -> Parser<'a> {
        Parser {
            handler: handler,
            state: HttpParser {
                internal_state: 0,
                nread: 0,
                content_length: 0,
                http_major: 0,
                http_minor: 0,
                extended_status: 0,
                data: 0 as *mut libc::c_void
            }
        }
    }
}

pub fn parse(pars: &mut Parser, data: &[u8]) -> usize {
    unsafe {
        http_parser_init(&mut pars.state as *mut _, HttpParserType::HttpRequest);

        pars.state.data = &mut (*pars) as *mut _ as *mut libc::c_void;

        let parsed = http_parser_execute(&mut pars.state as *mut _,
                                         &CALLBACK_WRAPPERS as *const _,
                                         data.as_ptr(),
                                         data.len() as u64);

        return parsed as usize;
    }
}

pub fn version() -> String {
    unsafe {
        let v = http_parser_version();

        let major = (v >> 16) & 255;
        let minor = (v >> 8) & 255;
        let patch = v & 255;

        return fmt::format(format_args!("{}.{}.{}", major, minor, patch));
    }
}

#[test]
fn test_version() {
    assert_eq!("2.5.0", version());
}

#[test]
fn request_parser() {
    struct TestParserImpl;
    impl ParserHandler for TestParserImpl {
        fn on_url(&self, url: &String) { assert_eq!("/hello", url); }
        fn on_header_field(&self, hdr: &String) { assert_eq!("Host", hdr); }
        fn on_header_value(&self, val: &String) { assert_eq!("localhost.localdomain", val); }
    }
    let req = "GET /hello HTTP/1.0\r\nHost: localhost.localdomain\r\n\r\n";
    let parsed = parse(&mut Parser::new(&TestParserImpl), req.as_bytes());
    assert!(parsed > 0);

    // assert_eq!(parser.http_major, 1);
    // assert_eq!(parser.http_minor, 0);
}

#[test]
fn response_parser() {
}
<commit_msg>Parser types & test for HTTP response parser<commit_after>#![allow(dead_code)]
extern crate libc;

use std::fmt;
use std::ptr;

type HttpCallback = extern fn(*mut HttpParser) -> *mut libc::c_int;
type HttpDataCallback = extern fn(*mut HttpParser, *const u32, libc::size_t) -> *mut libc::c_int;

#[repr(C)]
#[derive(Clone, Copy)]
pub enum ParserType {
    HttpRequest,
    HttpResponse,
    HttpBoth
}

#[repr(C)]
struct HttpParser {
    // Private Interface
    internal_state: u32,
    nread: u32,
    content_length: u64,

    // Read-Only
    http_major: libc::c_ushort,
    http_minor: libc::c_ushort,
    extended_status: u32,

    // Public Interface
    data: *mut libc::c_void
}

impl HttpParser {
    fn new() -> HttpParser {
        HttpParser {
            internal_state: 0,
            nread: 0,
            content_length: 0,
            http_major: 0,
            http_minor: 0,
            extended_status: 0,
            data: 0 as *mut libc::c_void
        }
    }
}

#[repr(C)]
struct HttpParserSettings {
    on_message_begin: HttpCallback,
    on_url: HttpDataCallback,
    on_status: HttpDataCallback,
    on_header_field: HttpDataCallback,
    on_header_value: HttpDataCallback,
    on_headers_complete: HttpCallback,
    on_body: HttpDataCallback,
    on_message_complete: HttpCallback,
    on_chunk_header: HttpCallback,
    on_chunk_complete: HttpCallback
}

#[inline]
unsafe fn unwrap_parser<'a>(http: *mut HttpParser) -> &'a mut Parser<'a> {
    return &mut *((*http).data as *mut Parser);
}

macro_rules! notify_fn_wrapper {
    ( $callback:ident ) => ({
        extern "C" fn $callback(http: *mut HttpParser) -> *mut libc::c_int {
            unsafe {
                unwrap_parser(http).handler.$callback();
            };
            return 0 as *mut libc::c_int;
        };
        $callback
    });
}

macro_rules! data_fn_wrapper {
    ( $callback:ident ) => ({
        extern "C" fn $callback(http: *mut HttpParser, data: *const u32, size: libc::size_t) -> *mut libc::c_int {
            unsafe {
                let mut dst = Vec::<u8>::with_capacity(size as usize);
                dst.set_len(size as usize);
                ptr::copy(data as *const u8, dst.as_mut_ptr(), size as usize);

                let data = String::from_utf8(dst).unwrap();

                unwrap_parser(http).handler.$callback(&data);
            };
            return 0 as *mut libc::c_int;
        };

        $callback
    });
}

static CALLBACK_WRAPPERS: HttpParserSettings = HttpParserSettings {
    on_url: data_fn_wrapper!(on_url),
    on_message_begin: notify_fn_wrapper!(on_message_begin),
    on_status: data_fn_wrapper!(on_status),
    on_header_field: data_fn_wrapper!(on_header_field),
    on_header_value: data_fn_wrapper!(on_header_value),
    on_headers_complete: notify_fn_wrapper!(on_headers_complete),
    on_body: data_fn_wrapper!(on_body),
    on_message_complete: notify_fn_wrapper!(on_message_complete),
    on_chunk_header: notify_fn_wrapper!(on_chunk_header),
    on_chunk_complete: notify_fn_wrapper!(on_chunk_complete)
};

extern "C" {
    fn http_parser_version() -> u32;
    fn http_parser_init(parser: *mut HttpParser, parser_type: ParserType);
    fn http_parser_settings_init(settings: *mut HttpParserSettings);
    fn http_parser_execute(parser: *mut HttpParser, settings: *const HttpParserSettings, data: *const u8, len: libc::size_t) -> libc::size_t;
    // fn http_method_str(m: HttpMethod);
}

// Rust Interface
pub trait ParserHandler {
    fn on_url(&self, &String) { }
    fn on_status(&self, &String) { }
    fn on_header_field(&self, &String) { }
    fn on_header_value(&self, &String) { }
    fn on_body(&self, &String) { }
    fn on_message_begin(&self) { }
    fn on_headers_complete(&self) { }
    fn on_message_complete(&self) { }
    fn on_chunk_header(&self) { }
    fn on_chunk_complete(&self) { }
}

pub struct Parser<'a> {
    handler: &'a ParserHandler,
    state: HttpParser,
    parser_type: ParserType
}

impl<'a> Parser<'a> {
    pub fn response(handler: &'a ParserHandler) -> Parser<'a> {
        Parser {
            handler: handler,
            state: HttpParser::new(),
            parser_type: ParserType::HttpResponse
        }
    }

    pub fn request(handler: &'a ParserHandler) -> Parser<'a> {
        Parser {
            handler: handler,
            state: HttpParser::new(),
            parser_type: ParserType::HttpRequest
        }
    }

    pub fn request_and_response(handler: &'a ParserHandler) -> Parser<'a> {
        Parser {
            handler: handler,
            state: HttpParser::new(),
            parser_type: ParserType::HttpBoth
        }
    }
}

pub fn parse(parser: &mut Parser, data: &[u8]) -> usize {
    unsafe {
        http_parser_init(&mut parser.state as *mut _, parser.parser_type);

        parser.state.data = &mut (*parser) as *mut _ as *mut libc::c_void;

        return http_parser_execute(&mut parser.state as *mut _,
                                         &CALLBACK_WRAPPERS as *const _,
                                         data.as_ptr(),
                                         data.len() as u64) as usize;
    }
}

pub fn version() -> String {
    unsafe {
        let v = http_parser_version();

        let major = (v >> 16) & 255;
        let minor = (v >> 8) & 255;
        let patch = v & 255;

        return fmt::format(format_args!("{}.{}.{}", major, minor, patch));
    }
}

#[test]
fn test_version() {
    assert_eq!("2.5.0", version());
}

#[test]
fn request_parser() {
    struct TestRequestParser;
    impl ParserHandler for TestRequestParser {
        fn on_url(&self, url: &String) { assert_eq!("/hello", url); }
        fn on_header_field(&self, hdr: &String) { assert_eq!("Host", hdr); }
        fn on_header_value(&self, val: &String) { assert_eq!("localhost.localdomain", val); }
    }
    let req = "GET /hello HTTP/1.0\r\nHost: localhost.localdomain\r\n\r\n";
    let parsed = parse(&mut Parser::request(&TestRequestParser), req.as_bytes());
    assert!(parsed > 0);

    // assert_eq!(parser.http_major, 1);
    // assert_eq!(parser.http_minor, 0);
}

#[test]
fn response_parser() {
    struct TestResponseParser;
    impl ParserHandler for TestResponseParser {
        fn on_status(&self, status: &String) { assert_eq!("OK", status); }
        fn on_header_field(&self, hdr: &String) { assert_eq!("Host", hdr); }
        fn on_header_value(&self, val: &String) { assert_eq!("localhost.localdomain", val); }
    }
    let req = "HTTP/1.1 200 OK\r\nHost: localhost.localdomain\r\n\r\n";
    let parsed = parse(&mut Parser::response(&TestResponseParser), req.as_bytes());
    assert!(parsed > 0);
}
<|endoftext|>