<commit_before>
//! A NIF resource allows you to safely store rust structs in a term, and therefore keep it across
//! NIF calls. The struct will be automatically dropped when the BEAM GC decides that there are no
//! more references to the resource.

extern crate libc;
use self::libc::{ c_void };

use std::mem;
use std::ptr;
use std::marker::PhantomData;

use super::{ NifTerm, NifEnv, NifError, NifEncoder, NifDecoder, NifResult };
use ::wrapper::nif_interface::{ NIF_RESOURCE_TYPE, NIF_RESOURCE_HANDLE, NIF_ENV, NifResourceFlags };

pub struct NifResourceType<T> {
    pub res: NIF_RESOURCE_TYPE,
    pub struct_type: PhantomData<T>,
}

pub trait NifResourceTypeProvider: Sized {
    fn get_dtor() -> extern "C" fn(env: NIF_ENV, handle: NIF_RESOURCE_HANDLE);
    fn get_type<'a>() -> &'a NifResourceType<Self>;
    unsafe fn set_type(typ: NifResourceType<Self>);
}

impl<T> NifEncoder for ResourceCell<T> where T: NifResourceTypeProvider {
    fn encode<'a>(&self, env: &'a NifEnv) -> NifTerm<'a> {
        self.as_term(env)
    }
}
impl<'a, T> NifDecoder<'a> for ResourceCell<T> where T: NifResourceTypeProvider+'a {
    fn decode(term: NifTerm<'a>) -> NifResult<Self> {
        ResourceCell::from_term(term)
    }
}

pub fn open_struct_resource_type<T: NifResourceTypeProvider>(env: &NifEnv, name: &str,
                                 flags: NifResourceFlags) -> Option<NifResourceType<T>> {
    let res: Option<NIF_RESOURCE_TYPE> = ::wrapper::resource::open_resource_type(env.as_c_arg(), name,
                                                                                Some(T::get_dtor()), flags);
    if res.is_some() {
        Some(NifResourceType {
            res: res.unwrap(),
            struct_type: PhantomData,
        })
    } else {
        None
    }
}

fn get_alloc_size_struct<T>() -> usize {
    mem::size_of::<T>() + mem::align_of::<T>()
}

/// Exported for use by codegen_runtime
/// This is unsafe as it allows us to do nasty things with pointers
pub unsafe fn align_alloced_mem_for_struct<T>(ptr: *mut c_void) -> *mut c_void {
    let offset = mem::align_of::<T>() - ((ptr as usize) % mem::align_of::<T>());
    ptr.offset(offset as isize)
}

use std::sync::RwLock;

pub struct ResourceCell<T> where T: NifResourceTypeProvider {
    raw: *mut c_void,
    inner: *mut RwLock<T>,
}

use std::sync::{LockResult, RwLockReadGuard, RwLockWriteGuard};
impl<T> ResourceCell<T> where T: NifResourceTypeProvider {
    pub fn new(data: T) -> Self {
        let alloc_size = get_alloc_size_struct::<RwLock<T>>();
        let mem_raw = ::wrapper::resource::alloc_resource(T::get_type().res, alloc_size);
        let aligned_mem = unsafe { align_alloced_mem_for_struct::<RwLock<T>>(mem_raw) } as *mut RwLock<T>;
        unsafe { ptr::write(aligned_mem, RwLock::new(data)) };
        ResourceCell {
            raw: mem_raw,
            inner: aligned_mem,
        }
    }
    fn from_term(term: NifTerm) -> Result<Self, NifError> {
        let res_resource = match ::wrapper::resource::get_resource(term.env.as_c_arg(), term.as_c_arg(), T::get_type().res) {
            Some(res) => res,
            None => return Err(NifError::BadArg),
        };
        ::wrapper::resource::keep_resource(res_resource);
        let casted_ptr = unsafe { align_alloced_mem_for_struct::<RwLock<T>>(res_resource) } as *mut RwLock<T>;
        Ok(ResourceCell {
            raw: res_resource,
            inner: casted_ptr,
        })
    }

    fn as_term<'a>(&self, env: &'a NifEnv) -> NifTerm<'a> {
        let raw_term = ::wrapper::resource::make_resource(env.as_c_arg(), self.raw);
        NifTerm::new(env, raw_term)
    }

    fn as_c_arg(&mut self) -> *mut c_void {
        self.raw
    }

    pub fn read(&self) -> LockResult<RwLockReadGuard<T>> {
        self.get_rwlock().read()
    }
    pub fn write(&self) -> LockResult<RwLockWriteGuard<T>> {
        self.get_rwlock().write()
    }

    pub fn get_rwlock(&self) -> &RwLock<T> {
        unsafe { &*self.inner }
    }
}

impl<T> Clone for ResourceCell<T> where T: NifResourceTypeProvider {
    fn clone(&self) -> Self {
        ::wrapper::resource::keep_resource(self.raw);
        ResourceCell {
            raw: self.raw,
            inner: self.inner,
        }
    }
}

impl<T> Drop for ResourceCell<T> where T: NifResourceTypeProvider {
    fn drop(&mut self) {
        unsafe { ::wrapper::nif_interface::enif_release_resource(self.as_c_arg()) };
    }
}
<commit_msg>basic documentation for resource.rs<commit_after>
//! A NIF resource allows you to safely store rust structs in a term, and therefore keep it across
//! NIF calls. The struct will be automatically dropped when the BEAM GC decides that there are no
//! more references to the resource.

extern crate libc;
use self::libc::{ c_void };

use std::mem;
use std::ptr;
use std::marker::PhantomData;

use super::{ NifTerm, NifEnv, NifError, NifEncoder, NifDecoder, NifResult };
use ::wrapper::nif_interface::{ NIF_RESOURCE_TYPE, NIF_RESOURCE_HANDLE, NIF_ENV, NifResourceFlags };

/// The NifResourceType struct contains a  NIF_RESOURCE_TYPE and a phantom reference to the type it
/// is for. It serves as a holder for the information needed to interact with the Erlang VM about
/// the resource type.
///
/// This is usually stored in an implementation of NifResourceTypeProvider.
pub struct NifResourceType<T> {
    pub res: NIF_RESOURCE_TYPE,
    pub struct_type: PhantomData<T>,
}

/// This trait gets implemented for the type we want to put into a resource when a type
/// is annotated with #[NifResource]. set_type is then later called on it by the
/// resource_struct_init! macro when the type is made in on_load. It provides the
/// destructor and the NifResourceType.
///
/// In most cases the user should not have to worry about this.
pub trait NifResourceTypeProvider: Sized {
    fn get_dtor() -> extern "C" fn(env: NIF_ENV, handle: NIF_RESOURCE_HANDLE);
    fn get_type<'a>() -> &'a NifResourceType<Self>;
    unsafe fn set_type(typ: NifResourceType<Self>);
}

impl<T> NifEncoder for ResourceCell<T> where T: NifResourceTypeProvider {
    fn encode<'a>(&self, env: &'a NifEnv) -> NifTerm<'a> {
        self.as_term(env)
    }
}
impl<'a, T> NifDecoder<'a> for ResourceCell<T> where T: NifResourceTypeProvider+'a {
    fn decode(term: NifTerm<'a>) -> NifResult<Self> {
        ResourceCell::from_term(term)
    }
}

/// This is the function that gets called from resource_struct_init! in on_load to create a new
/// resource type.
pub fn open_struct_resource_type<T: NifResourceTypeProvider>(env: &NifEnv, name: &str,
                                 flags: NifResourceFlags) -> Option<NifResourceType<T>> {
    let res: Option<NIF_RESOURCE_TYPE> = ::wrapper::resource::open_resource_type(env.as_c_arg(), name,
                                                                                Some(T::get_dtor()), flags);
    if res.is_some() {
        Some(NifResourceType {
            res: res.unwrap(),
            struct_type: PhantomData,
        })
    } else {
        None
    }
}

fn get_alloc_size_struct<T>() -> usize {
    mem::size_of::<T>() + mem::align_of::<T>()
}

/// Exported for use by codegen_runtime
/// This is unsafe as it allows us to do nasty things with pointers
pub unsafe fn align_alloced_mem_for_struct<T>(ptr: *mut c_void) -> *mut c_void {
    let offset = mem::align_of::<T>() - ((ptr as usize) % mem::align_of::<T>());
    ptr.offset(offset as isize)
}

use std::sync::RwLock;

/// This is the struct that holds a reference to a resource. It increments the refcounter for the
/// resource instance on creation, and decrements when dropped.
pub struct ResourceCell<T> where T: NifResourceTypeProvider {
    raw: *mut c_void,
    inner: *mut RwLock<T>,
}

use std::sync::{LockResult, RwLockReadGuard, RwLockWriteGuard};
impl<T> ResourceCell<T> where T: NifResourceTypeProvider {
    /// Makes a new ResourceCell from the given type. Note that the type must have
    /// NifResourceTypeProvider implemented for it. See module documentation for info on this.
    pub fn new(data: T) -> Self {
        let alloc_size = get_alloc_size_struct::<RwLock<T>>();
        let mem_raw = ::wrapper::resource::alloc_resource(T::get_type().res, alloc_size);
        let aligned_mem = unsafe { align_alloced_mem_for_struct::<RwLock<T>>(mem_raw) } as *mut RwLock<T>;
        unsafe { ptr::write(aligned_mem, RwLock::new(data)) };
        ResourceCell {
            raw: mem_raw,
            inner: aligned_mem,
        }
    }
    fn from_term(term: NifTerm) -> Result<Self, NifError> {
        let res_resource = match ::wrapper::resource::get_resource(term.env.as_c_arg(), term.as_c_arg(), T::get_type().res) {
            Some(res) => res,
            None => return Err(NifError::BadArg),
        };
        ::wrapper::resource::keep_resource(res_resource);
        let casted_ptr = unsafe { align_alloced_mem_for_struct::<RwLock<T>>(res_resource) } as *mut RwLock<T>;
        Ok(ResourceCell {
            raw: res_resource,
            inner: casted_ptr,
        })
    }

    fn as_term<'a>(&self, env: &'a NifEnv) -> NifTerm<'a> {
        let raw_term = ::wrapper::resource::make_resource(env.as_c_arg(), self.raw);
        NifTerm::new(env, raw_term)
    }

    fn as_c_arg(&mut self) -> *mut c_void {
        self.raw
    }

    pub fn read(&self) -> LockResult<RwLockReadGuard<T>> {
        self.get_rwlock().read()
    }
    pub fn write(&self) -> LockResult<RwLockWriteGuard<T>> {
        self.get_rwlock().write()
    }

    pub fn get_rwlock(&self) -> &RwLock<T> {
        unsafe { &*self.inner }
    }
}

impl<T> Clone for ResourceCell<T> where T: NifResourceTypeProvider {
    /// For a ResourceCell this will simply increment the refcounter for the resource instance and
    /// perform a fairly standard clone.
    fn clone(&self) -> Self {
        ::wrapper::resource::keep_resource(self.raw);
        ResourceCell {
            raw: self.raw,
            inner: self.inner,
        }
    }
}

impl<T> Drop for ResourceCell<T> where T: NifResourceTypeProvider {
    /// When drop is called for a ResourceCell, the reference held to the resource by the Cell is
    /// released.
    fn drop(&mut self) {
        unsafe { ::wrapper::nif_interface::enif_release_resource(self.as_c_arg()) };
    }
}
<|endoftext|>


<commit_before>use std::fmt;
use std::io::{self, Read};

use hyper::header::{Headers, ContentEncoding, ContentLength, Encoding, TransferEncoding};
use hyper::status::StatusCode;
use hyper::version::HttpVersion;
use serde::Deserialize;
use serde_json;

/// A Response to a submitted `Request`.
pub struct Response {
    inner: Decoder,
}

pub fn new(res: ::hyper::client::Response, gzip: bool) -> Response {
    Response {
        inner: Decoder::from_hyper_response(res, gzip)
    }
}

impl fmt::Debug for Response {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        return match &self.inner {
            &Decoder::PlainText(ref hyper_response) => {
                f.debug_struct("Response")
                    .field("status", &hyper_response.status)
                    .field("headers", &hyper_response.headers)
                    .field("version", &hyper_response.version)
                    .finish()
            },
            &Decoder::Gzip{ref status, ref version, ref headers, ..} => {
                f.debug_struct("Response")
                    .field("status", &status)
                    .field("headers", &headers)
                    .field("version", &version)
                    .finish()
            }
        }
    }
}

impl Response {
    /// Get the `StatusCode`.
    #[inline]
    pub fn status(&self) -> &StatusCode {
        match &self.inner {
            &Decoder::PlainText(ref hyper_response) => &hyper_response.status,
            &Decoder::Gzip{ref status, ..} => status
        }
    }

    /// Get the `Headers`.
    #[inline]
    pub fn headers(&self) -> &Headers {
        match &self.inner {
            &Decoder::PlainText(ref hyper_response) => &hyper_response.headers,
            &Decoder::Gzip{ref headers, ..} => headers
        }
    }

    /// Get the `HttpVersion`.
    #[inline]
    pub fn version(&self) -> &HttpVersion {
        match &self.inner {
            &Decoder::PlainText(ref hyper_response) => &hyper_response.version,
            &Decoder::Gzip{ref version, ..} => version
        }
    }

    /// Try and deserialize the response body as JSON.
    #[inline]
    pub fn json<T: Deserialize>(&mut self) -> ::Result<T> {
        serde_json::from_reader(self).map_err(::Error::from)
    }
}

enum Decoder {
    /// A `PlainText` decoder just returns the response content as is.
    PlainText(::hyper::client::Response),
    /// A `Gzip` decoder will uncompress the gziped response content before returning it.
    Gzip {
        decoder: ::libflate::gzip::Decoder<::hyper::client::Response>,
        headers: ::hyper::header::Headers,
        version: ::hyper::version::HttpVersion,
        status: ::hyper::status::StatusCode,
    }
}

impl Decoder {
    /// Constructs a Decoder from a hyper request.
    ///
    /// A decoder is just a wrapper around the hyper request that knows
    /// how to decode the content body of the request.
    ///
    /// Uses the correct variant by inspecting the Content-Encoding header.
    fn from_hyper_response(mut res: ::hyper::client::Response, check_gzip: bool) -> Self {
        if !check_gzip {
            return Decoder::PlainText(res);
        }
        let content_encoding_gzip: bool;
        let mut is_gzip = {
            content_encoding_gzip = res.headers.get::<ContentEncoding>().map_or(false, |encs|{
                encs.contains(&Encoding::Gzip)
            });
            content_encoding_gzip || res.headers.get::<TransferEncoding>().map_or(false, |encs|{
                encs.contains(&Encoding::Gzip)
            })
        };
        if content_encoding_gzip {
            res.headers.remove::<ContentEncoding>();
            res.headers.remove::<ContentLength>();
        }
        if is_gzip {
            if let Some(content_length) = res.headers.get::<ContentLength>() {
                if content_length.0 == 0 {
                    warn!("GZipped response with content-length of 0");
                    is_gzip = false;
                }
            }
        }
        if is_gzip {
            return Decoder::Gzip {
                status: res.status.clone(),
                version: res.version.clone(),
                headers: res.headers.clone(),
                decoder: ::libflate::gzip::Decoder::new(res).unwrap(),
            };
        } else {
            return Decoder::PlainText(res);
        }
    }
}

impl Read for Decoder {
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
        match self {
            &mut Decoder::PlainText(ref mut hyper_response) => {
                hyper_response.read(buf)
            },
            &mut Decoder::Gzip{ref mut decoder, ..} => {
                decoder.read(buf)
            }
        }
    }
}

/// Read the body of the Response.
impl Read for Response {
    #[inline]
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
        self.inner.read(buf)
    }
}

<commit_msg>Expose the final response URL<commit_after>use std::fmt;
use std::io::{self, Read};

use hyper::header::{Headers, ContentEncoding, ContentLength, Encoding, TransferEncoding};
use hyper::status::StatusCode;
use hyper::version::HttpVersion;
use hyper::Url;
use serde::Deserialize;
use serde_json;

/// A Response to a submitted `Request`.
pub struct Response {
    inner: Decoder,
}

pub fn new(res: ::hyper::client::Response, gzip: bool) -> Response {
    Response {
        inner: Decoder::from_hyper_response(res, gzip)
    }
}

impl fmt::Debug for Response {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        return match &self.inner {
            &Decoder::PlainText(ref hyper_response) => {
                f.debug_struct("Response")
                    .field("url", &hyper_response.url)
                    .field("status", &hyper_response.status)
                    .field("headers", &hyper_response.headers)
                    .field("version", &hyper_response.version)
                    .finish()
            },
            &Decoder::Gzip{ref url, ref status, ref version, ref headers, ..} => {
                f.debug_struct("Response")
                    .field("url", &url)
                    .field("status", &status)
                    .field("headers", &headers)
                    .field("version", &version)
                    .finish()
            }
        }
    }
}

impl Response {
    /// Get the final `Url` of this response.
    #[inline]
    pub fn url(&self) -> &Url {
        match &self.inner {
            &Decoder::PlainText(ref hyper_response) => &hyper_response.url,
            &Decoder::Gzip{ref url, ..} => url,
        }
    }

    /// Get the `StatusCode`.
    #[inline]
    pub fn status(&self) -> &StatusCode {
        match &self.inner {
            &Decoder::PlainText(ref hyper_response) => &hyper_response.status,
            &Decoder::Gzip{ref status, ..} => status
        }
    }

    /// Get the `Headers`.
    #[inline]
    pub fn headers(&self) -> &Headers {
        match &self.inner {
            &Decoder::PlainText(ref hyper_response) => &hyper_response.headers,
            &Decoder::Gzip{ref headers, ..} => headers
        }
    }

    /// Get the `HttpVersion`.
    #[inline]
    pub fn version(&self) -> &HttpVersion {
        match &self.inner {
            &Decoder::PlainText(ref hyper_response) => &hyper_response.version,
            &Decoder::Gzip{ref version, ..} => version
        }
    }

    /// Try and deserialize the response body as JSON.
    #[inline]
    pub fn json<T: Deserialize>(&mut self) -> ::Result<T> {
        serde_json::from_reader(self).map_err(::Error::from)
    }
}

enum Decoder {
    /// A `PlainText` decoder just returns the response content as is.
    PlainText(::hyper::client::Response),
    /// A `Gzip` decoder will uncompress the gziped response content before returning it.
    Gzip {
        decoder: ::libflate::gzip::Decoder<::hyper::client::Response>,
        url: ::hyper::Url,
        headers: ::hyper::header::Headers,
        version: ::hyper::version::HttpVersion,
        status: ::hyper::status::StatusCode,
    }
}

impl Decoder {
    /// Constructs a Decoder from a hyper request.
    ///
    /// A decoder is just a wrapper around the hyper request that knows
    /// how to decode the content body of the request.
    ///
    /// Uses the correct variant by inspecting the Content-Encoding header.
    fn from_hyper_response(mut res: ::hyper::client::Response, check_gzip: bool) -> Self {
        if !check_gzip {
            return Decoder::PlainText(res);
        }
        let content_encoding_gzip: bool;
        let mut is_gzip = {
            content_encoding_gzip = res.headers.get::<ContentEncoding>().map_or(false, |encs|{
                encs.contains(&Encoding::Gzip)
            });
            content_encoding_gzip || res.headers.get::<TransferEncoding>().map_or(false, |encs|{
                encs.contains(&Encoding::Gzip)
            })
        };
        if content_encoding_gzip {
            res.headers.remove::<ContentEncoding>();
            res.headers.remove::<ContentLength>();
        }
        if is_gzip {
            if let Some(content_length) = res.headers.get::<ContentLength>() {
                if content_length.0 == 0 {
                    warn!("GZipped response with content-length of 0");
                    is_gzip = false;
                }
            }
        }
        if is_gzip {
            return Decoder::Gzip {
                url: res.url.clone(),
                status: res.status.clone(),
                version: res.version.clone(),
                headers: res.headers.clone(),
                decoder: ::libflate::gzip::Decoder::new(res).unwrap(),
            };
        } else {
            return Decoder::PlainText(res);
        }
    }
}

impl Read for Decoder {
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
        match self {
            &mut Decoder::PlainText(ref mut hyper_response) => {
                hyper_response.read(buf)
            },
            &mut Decoder::Gzip{ref mut decoder, ..} => {
                decoder.read(buf)
            }
        }
    }
}

/// Read the body of the Response.
impl Read for Response {
    #[inline]
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
        self.inner.read(buf)
    }
}

<|endoftext|>


<commit_before>use std::mem;
use std::fmt;
use std::io::{self, Read};
use std::time::Duration;

use futures::{Async, Poll, Stream};
use serde::de::DeserializeOwned;
use serde_json;

use client::KeepCoreThreadAlive;
use header::Headers;
use {async_impl, StatusCode, Url, wait};

/// A Response to a submitted `Request`.
pub struct Response {
    inner: async_impl::Response,
    body: async_impl::ReadableChunks<WaitBody>,
    _thread_handle: KeepCoreThreadAlive,
}

impl fmt::Debug for Response {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        fmt::Debug::fmt(&self.inner, f)
    }
}

impl Response {
    /// Get the final `Url` of this `Response`.
    ///
    /// # Example
    ///
    /// ```rust
    /// # fn run() -> Result<(), Box<::std::error::Error>> {
    /// let resp = reqwest::get("http://httpbin.org/redirect/1")?;
    /// assert_eq!(resp.url().as_str(), "http://httpbin.org/get");
    /// # Ok(())
    /// # }
    /// ```
    #[inline]
    pub fn url(&self) -> &Url {
        self.inner.url()
    }

    /// Get the `StatusCode` of this `Response`.
    ///
    /// # Examples
    ///
    /// ```rust
    /// # fn run() -> Result<(), Box<::std::error::Error>> {
    /// let resp = reqwest::get("http://httpbin.org/get")?;
    /// if resp.status().is_success() {
    ///     println!("success!");
    /// } else if resp.status().is_server_error() {
    ///     println!("server error!");
    /// } else {
    ///     println!("Something else happened. Status: {:?}", resp.status());
    /// }
    /// # Ok(())
    /// # }
    /// ```
    ///
    /// ```rust
    /// use reqwest::Client;
    /// use reqwest::StatusCode;
    /// # fn run() -> Result<(), Box<::std::error::Error>> {
    /// let client = Client::new();
    /// let resp = client.post("http://httpbin.org/post")
    ///             .body("possibly too large")
    ///             .send()?;
    /// match resp.status() {
    ///     StatusCode::Ok => println!("success!"),
    ///     StatusCode::PayloadTooLarge => {
    ///         println!("Request payload is too large!");
    ///     }
    ///     s => println!("Received response status: {:?}", s),
    /// };
    /// # Ok(())
    /// # }
    /// ```
    #[inline]
    pub fn status(&self) -> StatusCode {
        self.inner.status()
    }

    /// Get the `Headers` of this `Response`.
    ///
    /// # Example
    ///
    /// Checking the `Content-Length` header before reading the response body.
    ///
    /// ```rust
    /// # use std::io::{Read, Write};
    /// # use reqwest::Client;
    /// # use reqwest::header::ContentLength;
    /// #
    /// # fn run() -> Result<(), Box<::std::error::Error>> {
    /// let client = Client::new();
    /// let mut resp = client.head("http://httpbin.org/bytes/3000").send()?;
    /// if resp.status().is_success() {
    ///     let len = resp.headers().get::<ContentLength>()
    ///                 .map(|ct_len| **ct_len)
    ///                 .unwrap_or(0);
    ///     // limit 1mb response
    ///     if len <= 1_000_000 {
    ///         let mut buf = Vec::with_capacity(len as usize);
    ///         let mut resp = reqwest::get("http://httpbin.org/bytes/3000")?;
    ///         if resp.status().is_success() {
    ///             ::std::io::copy(&mut resp, &mut buf)?;
    ///         }
    ///     }
    /// }
    /// # Ok(())
    /// # }
    /// ```
    #[inline]
    pub fn headers(&self) -> &Headers {
        self.inner.headers()
    }

    /// Try and deserialize the response body as JSON using `serde`.
    ///
    /// # Examples
    ///
    /// ```rust
    /// # extern crate reqwest;
    /// # #[macro_use] extern crate serde_derive;
    /// #
    /// # use reqwest::Error;
    /// #
    /// #[derive(Deserialize)]
    /// struct Ip {
    ///     origin: String,
    /// }
    ///
    /// # fn run() -> Result<(), Error> {
    /// let json: Ip = reqwest::get("http://httpbin.org/ip")?.json()?;
    /// # Ok(())
    /// # }
    /// #
    /// # fn main() { }
    /// ```
    ///
    /// # Errors
    ///
    /// This method fails whenever the response body is not in JSON format
    /// or it cannot be properly deserialized to target type `T`. For more
    /// details please see [`serde_json::from_reader`].
    /// [`serde_json::from_reader`]: https://docs.serde.rs/serde_json/fn.from_reader.html
    #[inline]
    pub fn json<T: DeserializeOwned>(&mut self) -> ::Result<T> {
        // There's 2 ways we could implement this:
        //
        // 1. Just using from_reader(self), making use of our blocking read adapter
        // 2. Just use self.inner.json().wait()
        //
        // Doing 1 is pretty easy, but it means we have the `serde_json` code
        // in more than one place, doing basically the same thing.
        //
        // Doing 2 would mean `serde_json` is only in one place, but we'd
        // need to update the sync Response to lazily make a blocking read
        // adapter, so that our `inner` could possibly still have the original
        // body.
        //
        // Went for easier for now, just to get it working.
        serde_json::from_reader(self).map_err(::error::from)
    }

    /// Get the response text.
    ///
    /// # Example
    ///
    /// ```rust
    /// # extern crate reqwest;
    /// # fn run() -> Result<(), Box<::std::error::Error>> {
    /// let content = reqwest::get("http://httpbin.org/range/26")?.text()?;
    /// # Ok(())
    /// # }
    /// ```
    pub fn text(&mut self) -> io::Result<String> {
        let len = self.headers().get::<::header::ContentLength>()
            .map(|ct_len| **ct_len)
            .unwrap_or(0);
        let mut content = String::with_capacity(len as usize);
        self.read_to_string(&mut content)?;
        Ok(content)
    }

    /// Copy the response body into a writer.
    ///
    /// This function internally uses [`std::io::copy`] and hence will continuously read data from
    /// the body and then write it into writer in a streaming fashion until EOF is met.
    ///
    /// On success, the total number of bytes that were copied to `writer` is returned.
    /// [`std::io::copy`]: https://doc.rust-lang.org/std/io/fn.copy.html
    ///
    /// # Example
    ///
    /// ```rust
    /// # fn run() -> Result<(), Box<::std::error::Error>> {
    /// let mut resp = reqwest::get("http://httpbin.org/range/5")?;
    /// let mut buf: Vec<u8> = vec![];
    /// resp.copy_to(&mut buf)?;
    /// assert_eq!(b"abcde", buf.as_slice());
    /// # Ok(())
    /// # }
    /// ```
    #[inline]
    pub fn copy_to<W: ?Sized>(&mut self, w: &mut W) -> io::Result<u64>
        where W: io::Write
    {
        io::copy(self, w)
    }

    /// Turn a response into an error if the server returned an error.
    ///
    /// # Example
    ///
    /// ```rust,no_run
    /// # extern crate reqwest;
    /// # fn run() -> Result<(), Box<::std::error::Error>> {
    /// let res = reqwest::get("http://httpbin.org/status/400")?
    ///     .error_for_status();
    /// if let Err(err) = res {
    ///     assert_eq!(err.status(), Some(reqwest::StatusCode::BadRequest));
    /// }
    /// # Ok(())
    /// # }
    /// # fn main() {}
    /// ```
    #[inline]
    pub fn error_for_status(self) -> ::Result<Self> {
        let Response { body, inner, _thread_handle } = self;
        inner.error_for_status().map(move |inner| {
            Response {
                inner: inner,
                body: body,
                _thread_handle: _thread_handle,
            }
        })
    }
}

impl Read for Response {
    #[inline]
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
        self.body.read(buf)
    }
}

struct WaitBody {
    inner: wait::WaitStream<async_impl::Decoder>
}

impl Stream for WaitBody {
    type Item = <async_impl::Decoder as Stream>::Item;
    type Error = <async_impl::Decoder as Stream>::Error;

    fn poll(&mut self) -> Poll<Option<Self::Item>, Self::Error> {
        match self.inner.next() {
            Some(Ok(chunk)) => Ok(Async::Ready(Some(chunk))),
            Some(Err(e)) => {
                let req_err = match e {
                    wait::Waited::TimedOut => ::error::timedout(None),
                    wait::Waited::Err(e) => e,
                };

                Err(req_err)
            },
            None => Ok(Async::Ready(None)),
        }
    }
}

// pub(crate)

pub fn new(mut res: async_impl::Response, timeout: Option<Duration>, thread: KeepCoreThreadAlive) -> Response {
    let body = mem::replace(res.body_mut(), async_impl::Decoder::empty());
    let body = async_impl::ReadableChunks::new(WaitBody {
        inner: wait::stream(body, timeout)
    });

    Response {
        inner: res,
        body: body,
        _thread_handle: thread,
    }
}
<commit_msg>fix(response): `copy_to()` and `text()` return `reqwest::Result`<commit_after>use std::mem;
use std::fmt;
use std::io::{self, Read};
use std::time::Duration;

use futures::{Async, Poll, Stream};
use serde::de::DeserializeOwned;
use serde_json;

use client::KeepCoreThreadAlive;
use header::Headers;
use {async_impl, StatusCode, Url, wait};

/// A Response to a submitted `Request`.
pub struct Response {
    inner: async_impl::Response,
    body: async_impl::ReadableChunks<WaitBody>,
    _thread_handle: KeepCoreThreadAlive,
}

impl fmt::Debug for Response {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        fmt::Debug::fmt(&self.inner, f)
    }
}

impl Response {
    /// Get the final `Url` of this `Response`.
    ///
    /// # Example
    ///
    /// ```rust
    /// # fn run() -> Result<(), Box<::std::error::Error>> {
    /// let resp = reqwest::get("http://httpbin.org/redirect/1")?;
    /// assert_eq!(resp.url().as_str(), "http://httpbin.org/get");
    /// # Ok(())
    /// # }
    /// ```
    #[inline]
    pub fn url(&self) -> &Url {
        self.inner.url()
    }

    /// Get the `StatusCode` of this `Response`.
    ///
    /// # Examples
    ///
    /// ```rust
    /// # fn run() -> Result<(), Box<::std::error::Error>> {
    /// let resp = reqwest::get("http://httpbin.org/get")?;
    /// if resp.status().is_success() {
    ///     println!("success!");
    /// } else if resp.status().is_server_error() {
    ///     println!("server error!");
    /// } else {
    ///     println!("Something else happened. Status: {:?}", resp.status());
    /// }
    /// # Ok(())
    /// # }
    /// ```
    ///
    /// ```rust
    /// use reqwest::Client;
    /// use reqwest::StatusCode;
    /// # fn run() -> Result<(), Box<::std::error::Error>> {
    /// let client = Client::new();
    /// let resp = client.post("http://httpbin.org/post")
    ///             .body("possibly too large")
    ///             .send()?;
    /// match resp.status() {
    ///     StatusCode::Ok => println!("success!"),
    ///     StatusCode::PayloadTooLarge => {
    ///         println!("Request payload is too large!");
    ///     }
    ///     s => println!("Received response status: {:?}", s),
    /// };
    /// # Ok(())
    /// # }
    /// ```
    #[inline]
    pub fn status(&self) -> StatusCode {
        self.inner.status()
    }

    /// Get the `Headers` of this `Response`.
    ///
    /// # Example
    ///
    /// Checking the `Content-Length` header before reading the response body.
    ///
    /// ```rust
    /// # use std::io::{Read, Write};
    /// # use reqwest::Client;
    /// # use reqwest::header::ContentLength;
    /// #
    /// # fn run() -> Result<(), Box<::std::error::Error>> {
    /// let client = Client::new();
    /// let mut resp = client.head("http://httpbin.org/bytes/3000").send()?;
    /// if resp.status().is_success() {
    ///     let len = resp.headers().get::<ContentLength>()
    ///                 .map(|ct_len| **ct_len)
    ///                 .unwrap_or(0);
    ///     // limit 1mb response
    ///     if len <= 1_000_000 {
    ///         let mut buf = Vec::with_capacity(len as usize);
    ///         let mut resp = reqwest::get("http://httpbin.org/bytes/3000")?;
    ///         if resp.status().is_success() {
    ///             ::std::io::copy(&mut resp, &mut buf)?;
    ///         }
    ///     }
    /// }
    /// # Ok(())
    /// # }
    /// ```
    #[inline]
    pub fn headers(&self) -> &Headers {
        self.inner.headers()
    }

    /// Try and deserialize the response body as JSON using `serde`.
    ///
    /// # Examples
    ///
    /// ```rust
    /// # extern crate reqwest;
    /// # #[macro_use] extern crate serde_derive;
    /// #
    /// # use reqwest::Error;
    /// #
    /// #[derive(Deserialize)]
    /// struct Ip {
    ///     origin: String,
    /// }
    ///
    /// # fn run() -> Result<(), Error> {
    /// let json: Ip = reqwest::get("http://httpbin.org/ip")?.json()?;
    /// # Ok(())
    /// # }
    /// #
    /// # fn main() { }
    /// ```
    ///
    /// # Errors
    ///
    /// This method fails whenever the response body is not in JSON format
    /// or it cannot be properly deserialized to target type `T`. For more
    /// details please see [`serde_json::from_reader`].
    /// [`serde_json::from_reader`]: https://docs.serde.rs/serde_json/fn.from_reader.html
    #[inline]
    pub fn json<T: DeserializeOwned>(&mut self) -> ::Result<T> {
        // There's 2 ways we could implement this:
        //
        // 1. Just using from_reader(self), making use of our blocking read adapter
        // 2. Just use self.inner.json().wait()
        //
        // Doing 1 is pretty easy, but it means we have the `serde_json` code
        // in more than one place, doing basically the same thing.
        //
        // Doing 2 would mean `serde_json` is only in one place, but we'd
        // need to update the sync Response to lazily make a blocking read
        // adapter, so that our `inner` could possibly still have the original
        // body.
        //
        // Went for easier for now, just to get it working.
        serde_json::from_reader(self).map_err(::error::from)
    }

    /// Get the response text.
    ///
    /// # Example
    ///
    /// ```rust
    /// # extern crate reqwest;
    /// # fn run() -> Result<(), Box<::std::error::Error>> {
    /// let content = reqwest::get("http://httpbin.org/range/26")?.text()?;
    /// # Ok(())
    /// # }
    /// ```
    pub fn text(&mut self) -> ::Result<String> {
        let len = self.headers().get::<::header::ContentLength>()
            .map(|ct_len| **ct_len)
            .unwrap_or(0);
        let mut content = String::with_capacity(len as usize);
        self.read_to_string(&mut content).map_err(::error::from)?;
        Ok(content)
    }

    /// Copy the response body into a writer.
    ///
    /// This function internally uses [`std::io::copy`] and hence will continuously read data from
    /// the body and then write it into writer in a streaming fashion until EOF is met.
    ///
    /// On success, the total number of bytes that were copied to `writer` is returned.
    /// [`std::io::copy`]: https://doc.rust-lang.org/std/io/fn.copy.html
    ///
    /// # Example
    ///
    /// ```rust
    /// # fn run() -> Result<(), Box<::std::error::Error>> {
    /// let mut resp = reqwest::get("http://httpbin.org/range/5")?;
    /// let mut buf: Vec<u8> = vec![];
    /// resp.copy_to(&mut buf)?;
    /// assert_eq!(b"abcde", buf.as_slice());
    /// # Ok(())
    /// # }
    /// ```
    #[inline]
    pub fn copy_to<W: ?Sized>(&mut self, w: &mut W) -> ::Result<u64>
        where W: io::Write
    {
        io::copy(self, w).map_err(::error::from)
    }

    /// Turn a response into an error if the server returned an error.
    ///
    /// # Example
    ///
    /// ```rust,no_run
    /// # extern crate reqwest;
    /// # fn run() -> Result<(), Box<::std::error::Error>> {
    /// let res = reqwest::get("http://httpbin.org/status/400")?
    ///     .error_for_status();
    /// if let Err(err) = res {
    ///     assert_eq!(err.status(), Some(reqwest::StatusCode::BadRequest));
    /// }
    /// # Ok(())
    /// # }
    /// # fn main() {}
    /// ```
    #[inline]
    pub fn error_for_status(self) -> ::Result<Self> {
        let Response { body, inner, _thread_handle } = self;
        inner.error_for_status().map(move |inner| {
            Response {
                inner: inner,
                body: body,
                _thread_handle: _thread_handle,
            }
        })
    }
}

impl Read for Response {
    #[inline]
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
        self.body.read(buf)
    }
}

struct WaitBody {
    inner: wait::WaitStream<async_impl::Decoder>
}

impl Stream for WaitBody {
    type Item = <async_impl::Decoder as Stream>::Item;
    type Error = <async_impl::Decoder as Stream>::Error;

    fn poll(&mut self) -> Poll<Option<Self::Item>, Self::Error> {
        match self.inner.next() {
            Some(Ok(chunk)) => Ok(Async::Ready(Some(chunk))),
            Some(Err(e)) => {
                let req_err = match e {
                    wait::Waited::TimedOut => ::error::timedout(None),
                    wait::Waited::Err(e) => e,
                };

                Err(req_err)
            },
            None => Ok(Async::Ready(None)),
        }
    }
}

// pub(crate)

pub fn new(mut res: async_impl::Response, timeout: Option<Duration>, thread: KeepCoreThreadAlive) -> Response {
    let body = mem::replace(res.body_mut(), async_impl::Decoder::empty());
    let body = async_impl::ReadableChunks::new(WaitBody {
        inner: wait::stream(body, timeout)
    });

    Response {
        inner: res,
        body: body,
        _thread_handle: thread,
    }
}
<|endoftext|>


<commit_before>extern crate regex;

use regex::Regex;
use std::collections::HashSet;
use std::io::{self, Write};

use super::{Compile, Name, ParseError, Path, Statement, Template};
use self::runtime::RUNTIME;

mod runtime;

/// A program is the final result of Mustache AST to Ruby extension source
/// translation that is presented to the main compiler driver for output.
///
/// It contains all external rendering functions generated by the translator,
/// which are exposed to Ruby code after the extension is compiled.
#[derive(Debug)]
pub struct Program {
    global: Scope,
}

impl Program {
    fn new() -> Self {
        Program { global: Scope::new(Name::new("global")) }
    }

    fn merge(&mut self, scope: Scope) -> &mut Self {
        self.global.merge(scope);
        self
    }
}

impl Compile for Program {
    /// Writes the final translated source code to an output buffer.
    ///
    /// This emits fully-formed Ruby extension source code that may be input
    /// into a mkmf build process, creating a dynamically loadable shared
    /// object file.
    fn emit(&self, buf: &mut Write) -> io::Result<()> {
        // Emit runtime preamble.
        writeln!(buf, "{}", RUNTIME)?;

        // Emit function declarations.
        for fun in &self.global.functions {
            writeln!(buf, "{};", fun.decl)?;
        }

        writeln!(buf, "")?;

        // Emit function definitions.
        for fun in &self.global.functions {
            fun.emit(buf)?
        }

        // Emit public render function.
        let renders: Vec<_> = self.global.functions.iter().filter_map(|f| f.invoke_if()).collect();
        writeln!(buf,
                 r#"static VALUE render(VALUE self, VALUE name, VALUE context) {{
                        const char *ptr = StringValuePtr(name);
                        const long length = RSTRING_LEN(name);
                        VALUE buf = rb_str_buf_new(0);
                        VALUE stack = rb_ary_new_from_args(1, context);
                        {}
                        else {{
                            rb_raise(rb_eArgError, "Template not found");
                        }}
                        return buf;
                    }}"#,
                 renders.join(" else "))
    }
}

/// A store for functions created by the translation process of an input
/// template to source code output.
///
/// Scopes have name generators that are used in function naming, providing a
/// stable name that other scopes may rely on for partial template function
/// calls.
///
/// After each template is translated into a scope they are merged into a
/// Program's global scope for final output.
#[derive(Debug)]
struct Scope {
    name: Name,
    functions: Vec<Function>,
}

impl Scope {
    fn new(name: Name) -> Self {
        Scope {
            name: name,
            functions: Vec::new(),
        }
    }

    /// Combines this scope's function definitions with another's.
    fn merge(&mut self, mut other: Scope) -> &mut Self {
        self.functions.append(&mut other.functions);
        self
    }

    /// Advances the scope's name generator to the next unique identifier. This
    /// should be called before descending another level in the recursive
    /// tree translation process.
    fn next(&mut self) -> &mut Self {
        self.name.next();
        self
    }

    /// Adds a function to this scope.
    fn register(&mut self, fun: Function) {
        self.functions.push(fun);
    }

    /// Returns the template path used to generate function names in this
    /// scope (e.g. "includes/header").
    fn base_name(&self) -> String {
        self.name.base.clone()
    }
}

#[derive(Debug)]
struct Function {
    name: String,
    decl: String,
    body: Vec<String>,
    export: Option<String>,
}

impl Function {
    /// Writes the function definition to the buffer.
    fn emit(&self, buf: &mut Write) -> io::Result<()> {
        writeln!(buf, "{} {{", self.decl)?;
        for node in &self.body {
            writeln!(buf, "{}", node)?;
        }
        writeln!(buf, "}}\n")
    }

    /// Builds a conditional statement to call the function if the template
    /// name matches the function's exported name, like "includes/header".
    fn invoke_if(&self) -> Option<String> {
        if self.export.is_none() {
            return None;
        }

        let export = self.export.as_ref().unwrap();
        Some(format!("if (length == {len} && strncmp(ptr, \"{path}\", {len}) == 0) {{
                          {fun}(buf, stack);
                      }}",
                     len = export.len(),
                     path = export,
                     fun = self.name))
    }
}

/// Recursively walks the AST, translating Mustache statement tree nodes into
/// the corresponding Ruby extension source code.
///
/// Sections are extracted into top-level functions paired with a function
/// call at the location the section appeared in the template. Partials are
/// similarly translated into a function call which is expected to be provided
/// by another template in the final tree.
fn transform(scope: &mut Scope, node: &Statement) -> Option<String> {
    match *node {
        Statement::Program(ref block) => {
            let id = scope.name.id();

            // Build private render function.
            let children = block.statements
                .iter()
                .filter_map(|stmt| transform(scope.next(), stmt))
                .collect();

            let render = Function {
                name: format!("render_{}", id),
                decl: format!("static void render_{}(VALUE buf, VALUE stack)", id),
                body: children,
                export: Some(scope.base_name()),
            };

            scope.register(render);
            None
        }
        Statement::Section(ref path, ref block) => {
            let children = block.statements
                .iter()
                .filter_map(|stmt| transform(scope.next(), stmt))
                .collect();

            let fun = Function {
                name: format!("section_{}", scope.name),
                decl: format!("static void section_{}(VALUE buf, VALUE stack)", scope.name),
                body: children,
                export: None,
            };

            let call = format!("{{ {} section(buf, stack, &path, {}); }}",
                               path_ary(path),
                               fun.name);

            scope.register(fun);
            Some(call)
        }
        Statement::Inverted(ref path, ref block) => {
            let children = block.statements
                .iter()
                .filter_map(|stmt| transform(scope.next(), stmt))
                .collect();

            let fun = Function {
                name: format!("section_{}", scope.name),
                decl: format!("static void section_{}(VALUE buf, VALUE stack)", scope.name),
                body: children,
                export: None,
            };

            let call = format!("{{ {} inverted(buf, stack, &path, {}); }}",
                               path_ary(path),
                               fun.name);

            scope.register(fun);
            Some(call)
        }
        Statement::Partial(ref name, ref padding) => {
            let name = Name::new(name);
            Some(format!("render_{}(buf, stack);", name.id()))
        }
        Statement::Comment(_) => None,
        Statement::Content(ref text) => {
            let text = clean(text);
            Some(format!("rb_str_cat_cstr(buf, \"{}\");", text))
        }
        Statement::Variable(ref path) => {
            let path = path_ary(path);
            Some(format!("{{ {} append_value(buf, stack, &path, true); }}", path))
        }
        Statement::Html(ref path) => {
            let path = path_ary(path);
            Some(format!("{{ {} append_value(buf, stack, &path, false); }}", path))
        }
    }
}

/// Transforms the AST of each parsed template into a source code tree
/// and links each template together into a single executable program.
pub fn link(templates: &Vec<Template>) -> Result<Program, ParseError> {
    validate(templates)?;

    let mut program = Program::new();
    templates.iter()
        .map(|template| {
            let mut scope = Scope::new(template.name());
            transform(&mut scope, &template.tree);
            scope
        })
        .fold(&mut program, |program, scope| program.merge(scope));

    Ok(program)
}

/// Ensures all templates may be linked together into an executable.
///
/// This method checks that all partial template paths are provided by
/// another template. For example, a `{{>include/header}}` partial invocation
/// must be provided by an `include/header.mustache` template file.
///
/// Partials can be considered function calls, so the function must be defined.
fn validate(templates: &Vec<Template>) -> Result<(), ParseError> {
    let all: HashSet<_> = templates.iter().map(|temp| &temp.name).collect();

    for template in templates {
        let names: HashSet<_> = template.tree.partials().into_iter().collect();
        let missing = &names - &all;
        if !missing.is_empty() {
            let name = missing.into_iter().next().unwrap();
            return Err(ParseError::UnknownPartial(name.clone(), template.path.clone()));
        }
    }

    Ok(())
}

/// Replaces string literal characters considered invalid inside a cstr with
/// their escaped counterparts.
fn clean(text: &str) -> String {
    let re = Regex::new(r"\\").unwrap();
    let text = re.replace_all(&text, "\\\\");

    let re = Regex::new(r"\r").unwrap();
    let text = re.replace_all(&text, "\\r");

    let re = Regex::new(r"\n").unwrap();
    let text = re.replace_all(&text, "\\n");

    let re = Regex::new(r#"["]"#).unwrap();
    re.replace_all(&text, "\\\"").into_owned()
}

/// Transforms a Mustache variable key path into the source code to build a
/// Ruby array. At runtime, each key in the array is recursively processed to
/// find the replacement text for a Mustache expression.
fn path_ary(path: &Path) -> String {
    let args = path.keys
        .iter()
        .map(|key| format!("\"{}\"", key))
        .collect::<Vec<String>>()
        .join(", ");

    format!("static const struct path path = {{ .keys = {{ {} }}, .length = {} }};",
            args,
            path.keys.len())
}

#[cfg(test)]
mod tests {
    use super::{link, transform, Scope};
    use super::super::{Name, ParseError, Statement, Template};
    use std::path::{Path, PathBuf};

    #[test]
    fn validates_valid_partial_reference() {
        let base = PathBuf::from("app/templates");
        let path = PathBuf::from("app/templates/machines/robots.mustache");
        let tree = Statement::Partial(String::from("machines/robot"), None);
        let master = Template::new(&base, path, tree);

        let path = PathBuf::from("app/templates/machines/robot.mustache");
        let tree = Statement::Content(String::from("hubot"));
        let detail = Template::new(&base, path, tree);

        let templates = vec![master, detail];
        match link(&templates) {
            Ok(_) => (),
            Err(e) => panic!("Must link valid partials: {}", e),
        }
    }

    #[test]
    fn validates_invalid_partial_reference() {
        let base = PathBuf::from("app/templates");
        let path = PathBuf::from("app/templates/machines/robots.mustache");
        let tree = Statement::Partial(String::from("machines/unknown"), None);

        let master = Template::new(&base, path, tree);

        let path = PathBuf::from("app/templates/machines/robot.mustache");
        let tree = Statement::Content(String::from("hubot"));
        let detail = Template::new(&base, path, tree);

        let templates = vec![master, detail];
        match link(&templates) {
            Err(ParseError::UnknownPartial(ref name, ref path)) => {
                assert_eq!("machines/unknown", name);
                assert_eq!(Path::new("app/templates/machines/robots.mustache"), path);
            }
            _ => panic!("Must enforce partial references"),
        }
    }

    #[test]
    fn transforms_tree_into_functions() {
        let text = "
            {{> includes/header }}

                {{# robots}}

{{ name.first }}

                {{/ robots}}
                {{^ robots}}
                    {{! else clause }}
                    No robots
                {{/ robots}}

            {{> includes/footer }}
            {{{ unescaped.html }}}
        ";

        match Statement::parse(text) {
            Ok(tree) => {
                let mut scope = Scope::new(Name::new("machines/robot"));
                transform(&mut scope, &tree);

                // One for each section, private render, and exported template function.
                let names: Vec<_> = scope.functions.iter().map(|fun| &fun.name).collect();
                assert_eq!(vec!["section_machines_robot7",
                                "section_machines_robot10",
                                "render_machines_robot"],
                           names);

                // Single exported function name.
                let exports: Vec<_> =
                    scope.functions.iter().filter_map(|fun| fun.export.as_ref()).collect();
                assert_eq!(vec!["machines/robot"], exports);
            }
            Err(e) => panic!("Failed to parse tree: {}", e),
        }
    }
}
<commit_msg>Remove memcpy appending static strings to buffer<commit_after>extern crate regex;

use regex::Regex;
use std::collections::HashSet;
use std::io::{self, Write};

use super::{Compile, Name, ParseError, Path, Statement, Template};
use self::runtime::RUNTIME;

mod runtime;

/// A program is the final result of Mustache AST to Ruby extension source
/// translation that is presented to the main compiler driver for output.
///
/// It contains all external rendering functions generated by the translator,
/// which are exposed to Ruby code after the extension is compiled.
#[derive(Debug)]
pub struct Program {
    global: Scope,
}

impl Program {
    fn new() -> Self {
        Program { global: Scope::new(Name::new("global")) }
    }

    fn merge(&mut self, scope: Scope) -> &mut Self {
        self.global.merge(scope);
        self
    }
}

impl Compile for Program {
    /// Writes the final translated source code to an output buffer.
    ///
    /// This emits fully-formed Ruby extension source code that may be input
    /// into a mkmf build process, creating a dynamically loadable shared
    /// object file.
    fn emit(&self, buf: &mut Write) -> io::Result<()> {
        // Emit runtime preamble.
        writeln!(buf, "{}", RUNTIME)?;

        // Emit function declarations.
        for fun in &self.global.functions {
            writeln!(buf, "{};", fun.decl)?;
        }

        writeln!(buf, "")?;

        // Emit function definitions.
        for fun in &self.global.functions {
            fun.emit(buf)?
        }

        // Emit public render function.
        let renders: Vec<_> = self.global.functions.iter().filter_map(|f| f.invoke_if()).collect();
        writeln!(buf,
                 r#"static VALUE render(VALUE self, VALUE name, VALUE context) {{
                        const char *ptr = StringValuePtr(name);
                        const long length = RSTRING_LEN(name);
                        VALUE buf = rb_str_buf_new(0);
                        VALUE stack = rb_ary_new_from_args(1, context);
                        {}
                        else {{
                            rb_raise(rb_eArgError, "Template not found");
                        }}
                        return buf;
                    }}"#,
                 renders.join(" else "))
    }
}

/// A store for functions created by the translation process of an input
/// template to source code output.
///
/// Scopes have name generators that are used in function naming, providing a
/// stable name that other scopes may rely on for partial template function
/// calls.
///
/// After each template is translated into a scope they are merged into a
/// Program's global scope for final output.
#[derive(Debug)]
struct Scope {
    name: Name,
    functions: Vec<Function>,
}

impl Scope {
    fn new(name: Name) -> Self {
        Scope {
            name: name,
            functions: Vec::new(),
        }
    }

    /// Combines this scope's function definitions with another's.
    fn merge(&mut self, mut other: Scope) -> &mut Self {
        self.functions.append(&mut other.functions);
        self
    }

    /// Advances the scope's name generator to the next unique identifier. This
    /// should be called before descending another level in the recursive
    /// tree translation process.
    fn next(&mut self) -> &mut Self {
        self.name.next();
        self
    }

    /// Adds a function to this scope.
    fn register(&mut self, fun: Function) {
        self.functions.push(fun);
    }

    /// Returns the template path used to generate function names in this
    /// scope (e.g. "includes/header").
    fn base_name(&self) -> String {
        self.name.base.clone()
    }
}

#[derive(Debug)]
struct Function {
    name: String,
    decl: String,
    body: Vec<String>,
    export: Option<String>,
}

impl Function {
    /// Writes the function definition to the buffer.
    fn emit(&self, buf: &mut Write) -> io::Result<()> {
        writeln!(buf, "{} {{", self.decl)?;
        for node in &self.body {
            writeln!(buf, "{}", node)?;
        }
        writeln!(buf, "}}\n")
    }

    /// Builds a conditional statement to call the function if the template
    /// name matches the function's exported name, like "includes/header".
    fn invoke_if(&self) -> Option<String> {
        if self.export.is_none() {
            return None;
        }

        let export = self.export.as_ref().unwrap();
        Some(format!("if (length == {len} && strncmp(ptr, \"{path}\", {len}) == 0) {{
                          {fun}(buf, stack);
                      }}",
                     len = export.len(),
                     path = export,
                     fun = self.name))
    }
}

/// Recursively walks the AST, translating Mustache statement tree nodes into
/// the corresponding Ruby extension source code.
///
/// Sections are extracted into top-level functions paired with a function
/// call at the location the section appeared in the template. Partials are
/// similarly translated into a function call which is expected to be provided
/// by another template in the final tree.
fn transform(scope: &mut Scope, node: &Statement) -> Option<String> {
    match *node {
        Statement::Program(ref block) => {
            let id = scope.name.id();

            // Build private render function.
            let children = block.statements
                .iter()
                .filter_map(|stmt| transform(scope.next(), stmt))
                .collect();

            let render = Function {
                name: format!("render_{}", id),
                decl: format!("static void render_{}(VALUE buf, VALUE stack)", id),
                body: children,
                export: Some(scope.base_name()),
            };

            scope.register(render);
            None
        }
        Statement::Section(ref path, ref block) => {
            let children = block.statements
                .iter()
                .filter_map(|stmt| transform(scope.next(), stmt))
                .collect();

            let fun = Function {
                name: format!("section_{}", scope.name),
                decl: format!("static void section_{}(VALUE buf, VALUE stack)", scope.name),
                body: children,
                export: None,
            };

            let call = format!("{{ {} section(buf, stack, &path, {}); }}",
                               path_ary(path),
                               fun.name);

            scope.register(fun);
            Some(call)
        }
        Statement::Inverted(ref path, ref block) => {
            let children = block.statements
                .iter()
                .filter_map(|stmt| transform(scope.next(), stmt))
                .collect();

            let fun = Function {
                name: format!("section_{}", scope.name),
                decl: format!("static void section_{}(VALUE buf, VALUE stack)", scope.name),
                body: children,
                export: None,
            };

            let call = format!("{{ {} inverted(buf, stack, &path, {}); }}",
                               path_ary(path),
                               fun.name);

            scope.register(fun);
            Some(call)
        }
        Statement::Partial(ref name, ref padding) => {
            let name = Name::new(name);
            Some(format!("render_{}(buf, stack);", name.id()))
        }
        Statement::Comment(_) => None,
        Statement::Content(ref text) => {
            let content = clean(text);
            let append = format!("{{
                static const char *content = \"{}\";
                rb_str_buf_append(buf, rb_str_new_static(content, {}));
            }}",
                                 content,
                                 text.len());
            Some(append)
        }
        Statement::Variable(ref path) => {
            let path = path_ary(path);
            Some(format!("{{ {} append_value(buf, stack, &path, true); }}", path))
        }
        Statement::Html(ref path) => {
            let path = path_ary(path);
            Some(format!("{{ {} append_value(buf, stack, &path, false); }}", path))
        }
    }
}

/// Transforms the AST of each parsed template into a source code tree
/// and links each template together into a single executable program.
pub fn link(templates: &Vec<Template>) -> Result<Program, ParseError> {
    validate(templates)?;

    let mut program = Program::new();
    templates.iter()
        .map(|template| {
            let mut scope = Scope::new(template.name());
            transform(&mut scope, &template.tree);
            scope
        })
        .fold(&mut program, |program, scope| program.merge(scope));

    Ok(program)
}

/// Ensures all templates may be linked together into an executable.
///
/// This method checks that all partial template paths are provided by
/// another template. For example, a `{{>include/header}}` partial invocation
/// must be provided by an `include/header.mustache` template file.
///
/// Partials can be considered function calls, so the function must be defined.
fn validate(templates: &Vec<Template>) -> Result<(), ParseError> {
    let all: HashSet<_> = templates.iter().map(|temp| &temp.name).collect();

    for template in templates {
        let names: HashSet<_> = template.tree.partials().into_iter().collect();
        let missing = &names - &all;
        if !missing.is_empty() {
            let name = missing.into_iter().next().unwrap();
            return Err(ParseError::UnknownPartial(name.clone(), template.path.clone()));
        }
    }

    Ok(())
}

/// Replaces string literal characters considered invalid inside a cstr with
/// their escaped counterparts.
fn clean(text: &str) -> String {
    let re = Regex::new(r"\\").unwrap();
    let text = re.replace_all(&text, "\\\\");

    let re = Regex::new(r"\r").unwrap();
    let text = re.replace_all(&text, "\\r");

    let re = Regex::new(r"\n").unwrap();
    let text = re.replace_all(&text, "\\n");

    let re = Regex::new(r#"["]"#).unwrap();
    re.replace_all(&text, "\\\"").into_owned()
}

/// Transforms a Mustache variable key path into the source code to build a
/// Ruby array. At runtime, each key in the array is recursively processed to
/// find the replacement text for a Mustache expression.
fn path_ary(path: &Path) -> String {
    let args = path.keys
        .iter()
        .map(|key| format!("\"{}\"", key))
        .collect::<Vec<String>>()
        .join(", ");

    format!("static const struct path path = {{ .keys = {{ {} }}, .length = {} }};",
            args,
            path.keys.len())
}

#[cfg(test)]
mod tests {
    use super::{link, transform, Scope};
    use super::super::{Name, ParseError, Statement, Template};
    use std::path::{Path, PathBuf};

    #[test]
    fn validates_valid_partial_reference() {
        let base = PathBuf::from("app/templates");
        let path = PathBuf::from("app/templates/machines/robots.mustache");
        let tree = Statement::Partial(String::from("machines/robot"), None);
        let master = Template::new(&base, path, tree);

        let path = PathBuf::from("app/templates/machines/robot.mustache");
        let tree = Statement::Content(String::from("hubot"));
        let detail = Template::new(&base, path, tree);

        let templates = vec![master, detail];
        match link(&templates) {
            Ok(_) => (),
            Err(e) => panic!("Must link valid partials: {}", e),
        }
    }

    #[test]
    fn validates_invalid_partial_reference() {
        let base = PathBuf::from("app/templates");
        let path = PathBuf::from("app/templates/machines/robots.mustache");
        let tree = Statement::Partial(String::from("machines/unknown"), None);

        let master = Template::new(&base, path, tree);

        let path = PathBuf::from("app/templates/machines/robot.mustache");
        let tree = Statement::Content(String::from("hubot"));
        let detail = Template::new(&base, path, tree);

        let templates = vec![master, detail];
        match link(&templates) {
            Err(ParseError::UnknownPartial(ref name, ref path)) => {
                assert_eq!("machines/unknown", name);
                assert_eq!(Path::new("app/templates/machines/robots.mustache"), path);
            }
            _ => panic!("Must enforce partial references"),
        }
    }

    #[test]
    fn transforms_tree_into_functions() {
        let text = "
            {{> includes/header }}

                {{# robots}}

{{ name.first }}

                {{/ robots}}
                {{^ robots}}
                    {{! else clause }}
                    No robots
                {{/ robots}}

            {{> includes/footer }}
            {{{ unescaped.html }}}
        ";

        match Statement::parse(text) {
            Ok(tree) => {
                let mut scope = Scope::new(Name::new("machines/robot"));
                transform(&mut scope, &tree);

                // One for each section, private render, and exported template function.
                let names: Vec<_> = scope.functions.iter().map(|fun| &fun.name).collect();
                assert_eq!(vec!["section_machines_robot7",
                                "section_machines_robot10",
                                "render_machines_robot"],
                           names);

                // Single exported function name.
                let exports: Vec<_> =
                    scope.functions.iter().filter_map(|fun| fun.export.as_ref()).collect();
                assert_eq!(vec!["machines/robot"], exports);
            }
            Err(e) => panic!("Failed to parse tree: {}", e),
        }
    }
}
<|endoftext|>


<commit_before>//! This module defines the metadata on devices and services.
//!
//! Note that all the data structures in this module represent
//! snapshots of subsets of the devices available. None of these data
//! structures are live, so there is always the possibility that
//! devices may have been added or removed from the FoxBox by the time
//! these data structures are read.

use values::*;
use util::Id;

use serde::ser::{Serialize, Serializer};
use serde::de::{Deserialize, Deserializer, Error};

use std::hash::{Hash, Hasher};
use std::collections::{HashSet, HashMap};

/// A marker for Id.
/// Only useful for writing `Id<ServiceId>`.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Hash, Eq)]
pub struct ServiceId;

/// Metadata on a service. A service is a device or collection of devices
/// that may offer services. The FoxBox itself is a service offering
/// services such as a clock, communicating with the user through her
/// smart devices, etc.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Service {
    /// Tags describing the service.
    ///
    /// These tags can be set by the user, adapters or
    /// applications. They are used by applications to find services and
    /// services.
    ///
    /// For instance, a user may set tag "entrance" to all services
    /// placed in the entrance of his house, or a tag "blue" to a service
    /// controlling blue lights. An adapter may set tags "plugged" or
    /// "battery" to devices that respectively depend on a plugged
    /// power source or on a battery.
    pub tags: HashSet<String>,

    /// An id unique to this service.
    pub id: Id<ServiceId>,

    /// Channels connected directly to this service.
    pub getters: HashMap<Id<Getter>, Channel<Getter>>,
    pub setters: HashMap<Id<Setter>, Channel<Setter>>,
}

/// The kind of the service, i.e. a strongly-typed description of
/// _what_ the service can do. Used both for locating services
/// (e.g. "I need a clock" or "I need something that can provide
/// pictures") and for determining the data structure that these
/// services can provide or consume.
///
/// A number of service kinds are standardized, and provided as a set
/// of strongly-typed enum constructors. It is clear, however, that
/// many devices will offer services that cannot be described by
/// pre-existing constructors. For this purpose, this enumeration
/// offers a constructor `Extension`, designed to describe novel
/// services.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum ChannelKind {
    ///
    /// # No payload
    ///

    /// The service is ready. Used for instance once a countdown has
    /// reached completion.
    Ready,

    ///
    /// # Boolean
    ///

    /// The service is used to detect or decide whether some device
    /// is on or off.
    OnOff,

    /// The service is used to detect or decide whether some device
    /// is open or closed.
    OpenClosed,

    ///
    /// # Time
    ///

    /// The service is used to read or set the current absolute time.
    /// Used for instance to wait until a specific time and day before
    /// triggering an action, or to set the appropriate time on a new
    /// device.
    CurrentTime,

    /// The service is used to read or set the current time of day.
    /// Used for instance to trigger an action at a specific hour
    /// every day.
    CurrentTimeOfDay,

    /// The service is part of a countdown. This is the time
    /// remaining until the countdown is elapsed.
    RemainingTime,

    ///
    /// # Temperature
    ///

    Thermostat,
    ActualTemperature,

    /// TODO: Add more

    /// An operation of a kind that has not been standardized yet.
    Extension {
        /// The vendor. Used for namespacing purposes, to avoid
        /// confusing two incompatible extensions with similar
        /// names. For instance, "foxlink@mozilla.com".
        vendor: String,

        /// Identification of the adapter introducing this operation.
        /// Designed to aid with tracing and debugging.
        adapter: String,

        /// A string describing the nature of the value, designed to
        /// let applications discover the devices.
        ///
        /// Examples: `"GroundHumidity"`.
        kind: String,

        /// The data type of the value.
        typ: Type
    }
}

impl ChannelKind {
    /// Get the type of values used to communicate with this service.
    pub fn get_type(&self) -> Type {
        use self::ChannelKind::*;
        use values::Type;
        match *self {
            Ready => Type::Unit,
            OnOff => Type::OnOff,
            OpenClosed => Type::OpenClosed,
            CurrentTime => Type::TimeStamp,
            CurrentTimeOfDay | RemainingTime => Type::Duration,
            Thermostat | ActualTemperature => Type::Temperature,
            Extension { ref typ, ..} => typ.clone(),
        }
    }
}

/// A getter operation available on a channel.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Getter {
    /// The kind of value that can be obtained from this channel.
    pub kind: ChannelKind,

    /// If `Some(duration)`, this channel can be polled, i.e. it
    /// will respond when the FoxBox requests the latest value.
    /// Parameter `duration` indicates the smallest interval
    /// between two updates.
    ///
    /// Otherwise, the channel cannot be polled and will push
    /// data to the FoxBox when it is available.
    ///
    /// # Examples
    ///
    /// - Long-running pollution or humidity sensors typically
    ///   do not accept requests and rather send batches of
    ///   data every 24h.
    #[serde(default)]
    pub poll: Option<ValDuration>,

    /// If `Some(duration)`, this channel can send the data to
    /// the FoxBox whenever it is updated. Parameter `duration`
    /// indicates the smallest interval between two updates.
    ///
    /// Otherwise, the channel cannot send data to the FoxBox
    /// and needs to be polled.
    #[serde(default)]
    pub trigger: Option<ValDuration>,

    /// If `true`, this channel supports watching for specific
    /// changes.
    #[serde(default)]
    pub watch: bool,

    /// Date at which the latest value was received, whether through
    /// polling or through a trigger.
    pub updated: Option<TimeStamp>,
}
impl IOMechanism for Getter {
}

/// An setter operation available on an channel.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Setter {
    /// The kind of value that can be sent to this channel.
    pub kind: ChannelKind,

    /// If `Some(duration)`, this channel supports pushing,
    /// i.e. the FoxBox can send values.
    #[serde(default)]
    pub push: Option<ValDuration>,

    /// Date at which the latest value was sent to the channel.
    #[serde(default)]
    pub updated: Option<TimeStamp>,
}
impl IOMechanism for Setter {
}

/// An channel represents a single place where data can enter or
/// leave a device. Note that channels support either a single kind
/// of getter or a single kind of setter. Devices that support both
/// getters or setters, or several kinds of getters, or several kinds of
/// setters, are represented as services containing several channels.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Channel<IO> where IO: IOMechanism {
    /// Tags describing the channel.
    ///
    /// These tags can be set by the user, adapters or
    /// applications. They are used to regroup channels for rules.
    ///
    /// For instance "entrance".
    #[serde(default)]
    pub tags: HashSet<String>,

    /// An id unique to this channel.
    pub id: Id<IO>,

    /// The service owning this channel.
    pub service: Id<ServiceId>,

    /// The update mechanism for this channel.
    pub mechanism: IO,

    /// The last time the device was seen.
    #[serde(default)]
    pub last_seen: Option<TimeStamp>,
}
impl<IO> Eq for Channel<IO> where IO: IOMechanism {
}
impl<IO> PartialEq for Channel<IO> where IO: IOMechanism {
     fn eq(&self, other: &Self) -> bool {
         self.id.eq(&other.id)
     }
}
impl<IO> Hash for Channel<IO> where IO: IOMechanism {
    fn hash<H>(&self, state: &mut H) where H: Hasher {
        self.id.hash(state)
    }
}

/// The communication mechanism used by the channel.
pub trait IOMechanism: Deserialize + Serialize {
}

<commit_msg>Service::empty<commit_after>//! This module defines the metadata on devices and services.
//!
//! Note that all the data structures in this module represent
//! snapshots of subsets of the devices available. None of these data
//! structures are live, so there is always the possibility that
//! devices may have been added or removed from the FoxBox by the time
//! these data structures are read.

use values::*;
use util::Id;

use serde::ser::{Serialize, Serializer};
use serde::de::{Deserialize, Deserializer, Error};

use std::hash::{Hash, Hasher};
use std::collections::{HashSet, HashMap};

/// A marker for Id.
/// Only useful for writing `Id<ServiceId>`.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Hash, Eq)]
pub struct ServiceId;

/// Metadata on a service. A service is a device or collection of devices
/// that may offer services. The FoxBox itself is a service offering
/// services such as a clock, communicating with the user through her
/// smart devices, etc.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Service {
    /// Tags describing the service.
    ///
    /// These tags can be set by the user, adapters or
    /// applications. They are used by applications to find services and
    /// services.
    ///
    /// For instance, a user may set tag "entrance" to all services
    /// placed in the entrance of his house, or a tag "blue" to a service
    /// controlling blue lights. An adapter may set tags "plugged" or
    /// "battery" to devices that respectively depend on a plugged
    /// power source or on a battery.
    pub tags: HashSet<String>,

    /// An id unique to this service.
    pub id: Id<ServiceId>,

    /// Channels connected directly to this service.
    pub getters: HashMap<Id<Getter>, Channel<Getter>>,
    pub setters: HashMap<Id<Setter>, Channel<Setter>>,
}
impl Service {
    /// Create an empty service.
    pub fn empty(id: Id<ServiceId>) -> Self {
        Service {
            tags: HashSet::new(),
            getters: HashMap::new(),
            setters: HashMap::new(),
            id: id,
        }
    }
}

/// The kind of the service, i.e. a strongly-typed description of
/// _what_ the service can do. Used both for locating services
/// (e.g. "I need a clock" or "I need something that can provide
/// pictures") and for determining the data structure that these
/// services can provide or consume.
///
/// A number of service kinds are standardized, and provided as a set
/// of strongly-typed enum constructors. It is clear, however, that
/// many devices will offer services that cannot be described by
/// pre-existing constructors. For this purpose, this enumeration
/// offers a constructor `Extension`, designed to describe novel
/// services.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum ChannelKind {
    ///
    /// # No payload
    ///

    /// The service is ready. Used for instance once a countdown has
    /// reached completion.
    Ready,

    ///
    /// # Boolean
    ///

    /// The service is used to detect or decide whether some device
    /// is on or off.
    OnOff,

    /// The service is used to detect or decide whether some device
    /// is open or closed.
    OpenClosed,

    ///
    /// # Time
    ///

    /// The service is used to read or set the current absolute time.
    /// Used for instance to wait until a specific time and day before
    /// triggering an action, or to set the appropriate time on a new
    /// device.
    CurrentTime,

    /// The service is used to read or set the current time of day.
    /// Used for instance to trigger an action at a specific hour
    /// every day.
    CurrentTimeOfDay,

    /// The service is part of a countdown. This is the time
    /// remaining until the countdown is elapsed.
    RemainingTime,

    ///
    /// # Temperature
    ///

    Thermostat,
    ActualTemperature,

    /// TODO: Add more

    /// An operation of a kind that has not been standardized yet.
    Extension {
        /// The vendor. Used for namespacing purposes, to avoid
        /// confusing two incompatible extensions with similar
        /// names. For instance, "foxlink@mozilla.com".
        vendor: String,

        /// Identification of the adapter introducing this operation.
        /// Designed to aid with tracing and debugging.
        adapter: String,

        /// A string describing the nature of the value, designed to
        /// let applications discover the devices.
        ///
        /// Examples: `"GroundHumidity"`.
        kind: String,

        /// The data type of the value.
        typ: Type
    }
}

impl ChannelKind {
    /// Get the type of values used to communicate with this service.
    pub fn get_type(&self) -> Type {
        use self::ChannelKind::*;
        use values::Type;
        match *self {
            Ready => Type::Unit,
            OnOff => Type::OnOff,
            OpenClosed => Type::OpenClosed,
            CurrentTime => Type::TimeStamp,
            CurrentTimeOfDay | RemainingTime => Type::Duration,
            Thermostat | ActualTemperature => Type::Temperature,
            Extension { ref typ, ..} => typ.clone(),
        }
    }
}

/// A getter operation available on a channel.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Getter {
    /// The kind of value that can be obtained from this channel.
    pub kind: ChannelKind,

    /// If `Some(duration)`, this channel can be polled, i.e. it
    /// will respond when the FoxBox requests the latest value.
    /// Parameter `duration` indicates the smallest interval
    /// between two updates.
    ///
    /// Otherwise, the channel cannot be polled and will push
    /// data to the FoxBox when it is available.
    ///
    /// # Examples
    ///
    /// - Long-running pollution or humidity sensors typically
    ///   do not accept requests and rather send batches of
    ///   data every 24h.
    #[serde(default)]
    pub poll: Option<ValDuration>,

    /// If `Some(duration)`, this channel can send the data to
    /// the FoxBox whenever it is updated. Parameter `duration`
    /// indicates the smallest interval between two updates.
    ///
    /// Otherwise, the channel cannot send data to the FoxBox
    /// and needs to be polled.
    #[serde(default)]
    pub trigger: Option<ValDuration>,

    /// If `true`, this channel supports watching for specific
    /// changes.
    #[serde(default)]
    pub watch: bool,

    /// Date at which the latest value was received, whether through
    /// polling or through a trigger.
    pub updated: Option<TimeStamp>,
}
impl IOMechanism for Getter {
}

/// An setter operation available on an channel.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Setter {
    /// The kind of value that can be sent to this channel.
    pub kind: ChannelKind,

    /// If `Some(duration)`, this channel supports pushing,
    /// i.e. the FoxBox can send values.
    #[serde(default)]
    pub push: Option<ValDuration>,

    /// Date at which the latest value was sent to the channel.
    #[serde(default)]
    pub updated: Option<TimeStamp>,
}
impl IOMechanism for Setter {
}

/// An channel represents a single place where data can enter or
/// leave a device. Note that channels support either a single kind
/// of getter or a single kind of setter. Devices that support both
/// getters or setters, or several kinds of getters, or several kinds of
/// setters, are represented as services containing several channels.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Channel<IO> where IO: IOMechanism {
    /// Tags describing the channel.
    ///
    /// These tags can be set by the user, adapters or
    /// applications. They are used to regroup channels for rules.
    ///
    /// For instance "entrance".
    #[serde(default)]
    pub tags: HashSet<String>,

    /// An id unique to this channel.
    pub id: Id<IO>,

    /// The service owning this channel.
    pub service: Id<ServiceId>,

    /// The update mechanism for this channel.
    pub mechanism: IO,

    /// The last time the device was seen.
    #[serde(default)]
    pub last_seen: Option<TimeStamp>,
}
impl<IO> Eq for Channel<IO> where IO: IOMechanism {
}
impl<IO> PartialEq for Channel<IO> where IO: IOMechanism {
     fn eq(&self, other: &Self) -> bool {
         self.id.eq(&other.id)
     }
}
impl<IO> Hash for Channel<IO> where IO: IOMechanism {
    fn hash<H>(&self, state: &mut H) where H: Hasher {
        self.id.hash(state)
    }
}

/// The communication mechanism used by the channel.
pub trait IOMechanism: Deserialize + Serialize {
}

<|endoftext|>


<commit_before>//! Storage manager that uses BitKeeper.
//!
//! [BitKeeper] is a revision control system, predating git.  Although it's (pre-open-source)
//! existence was some of the inspiration behind git, the way it stores file delta information is
//! quite different.  It is based on the SCCS system, which stores all revisions in a single file
//! in a "weave" format.  Until weaving is directly implemented in rsure, the `BkStore` type allows
//! us to take advantage of this format to store lots of deltas compactly (typically using much
//! less space than a delta-based storage, such as git).
//!
//! Aside from having a requirement that the commandline `bk` program for bitkeeper be installed in
//! the path, trees written to the `BkStore` must always have a tag with the name `name` and a
//! unique value.  This field will be used to retrieve that particular revision later.  Within
//! Bitkeeper, the `name` tag will be used as the commit text, and can be listed, and found later.
//!
//! [BitKeeper]: http://www.bitkeeper.org/

use crate::{
    errors::WeaveError,
    store::{Store, StoreTags, StoreVersion, StoreWriter, TempFile, Version},
    node::SureNode,
    Result, SureTree,
};
use failure::err_msg;
use regex::Regex;
use std::{
    fs::File,
    io::{BufRead, Write},
    path::{Path, PathBuf},
    process::{Command, Stdio},
};

/// A [`Store`] that stores trees as deltas in a Bitkeeper repository.
///
/// [BitKeeper] is a revision control system, predating git.  Although it's (pre-open-source)
/// existence was some of the inspiration behind git, the way it stores file delta information is
/// quite different.  It is based on the SCCS system, which stores all revisions in a single file
/// in a "weave" format.  Until weaving is directly implemented in rsure, the `BkStore` type allows
/// us to take advantage of this format to store lots of deltas compactly (typically using much
/// less space than a delta-based storage, such as git).
///
/// Aside from having a requirement that the commandline `bk` program for bitkeeper be installed in
/// the path, trees written to the `BkStore` must always have a tag with the name `name` and a
/// unique value.  This field will be used to retrieve that particular revision later.  Within
/// Bitkeeper, the `name` tag will be used as the commit text, and can be listed, and found later.
///
/// [`Store`]: trait.Store.html
/// [BitKeeper]: http://www.bitkeeper.org/
pub struct BkStore {
    /// The directory where the bitkeeper repository lives.
    pub base: PathBuf,
    /// The particular filename for this `Store` within that repo.  A given repo can hold multiple
    /// `Store`s.
    pub name: String,
    /// An internal compiled regex used to search for revisions.
    change_re: Regex,
}

impl BkStore {
    /// Construct a new in-memory [`Store`] to refer to a Bitkeeper store.
    ///
    /// The `base` should be the directory containing the Bitkeeper repository itself (which should
    /// have been created by [`bk_setup`].
    ///
    /// The `name` names a single store within this respository.  A given store is intended to hold
    /// tree snapshots over time for a single filesystem.  This should be a plain filename with no
    /// extension.
    ///
    /// [`Store`]: trait.Store.html
    /// [`bk_setup`]: fn.bk_setup.html
    pub fn new(base: &Path, name: &str) -> BkStore {
        BkStore {
            base: base.to_path_buf(),
            name: format!("{}.dat", name),
            change_re: Regex::new(r"^  ([^ ]+) ([\d\.]+) (.*)$").unwrap(),
        }
    }
}

impl Store for BkStore {
    fn write_new(&self, tree: &SureTree, tags: &StoreTags) -> Result<()> {
        let tag_name = name_of_tags(tags);
        let y_arg = format!("-y{}", tag_name);

        // Try checking out the file.  BitKeeper will fail if it doesn't
        // exist.
        let initial = match self.bk_do(&["edit", &self.name]) {
            Ok(_) => false,
            Err(_) => true,
        };

        {
            let mut wr = File::create(&self.base.join(&self.name))?;
            tree.save_to(&mut wr)?;
        }

        if initial {
            self.bk_do(&["ci", "-i", &y_arg, &self.name])?;
        } else {
            self.bk_do(&["ci", "-f", &y_arg, &self.name])?;
        }

        self.bk_do(&["commit", &y_arg, &self.name])?;

        Ok(())
    }

    fn load(&self, version: Version) -> Result<SureTree> {
        let vers = self.get_version(version)?;
        let rev = match vers {
            None => return Err(err_msg("Couldn't find bk version")),
            Some(ref x) => &x.rev[..],
        };

        let mut child = Command::new("bk")
            .args(&["co", "-p", &format!("-r{}", rev), &self.name])
            .current_dir(&self.base)
            .stdout(Stdio::piped())
            .spawn()?;
        let tree = SureTree::load_from(child.stdout.as_mut().unwrap())?;
        let status = child.wait()?;
        if !status.success() {
            return Err(WeaveError::BkError(status, "".into()).into());
        }
        Ok(tree)
    }

    fn get_versions(&self) -> Result<Vec<StoreVersion>> {
        Ok(vec![])
    }

    fn load_iter(&self, _version: Version) -> Result<Box<dyn Iterator<Item = Result<SureNode>>>> {
        unimplemented!()
    }

    fn make_temp(&self) -> Result<Box<dyn TempFile>> {
        unimplemented!()
    }

    fn make_new(&self, _tags: &StoreTags) -> Result<Box<dyn StoreWriter + '_>> {
        unimplemented!()
    }
}

impl BkStore {
    fn bk_do(&self, args: &[&str]) -> Result<()> {
        let status = Command::new("bk")
            .args(args)
            .current_dir(&self.base)
            .status()?;
        if !status.success() {
            return Err(WeaveError::BkError(status, "".into()).into());
        }
        Ok(())
    }

    /// Map a version to version information.
    fn get_version(&self, version: Version) -> Result<Option<BkSureFile>> {
        let versions = self.query()?;
        let mut versions = versions.into_iter().filter(|x| x.file == self.name);
        let index = match version {
            Version::Latest => 0,
            Version::Prior => 1,
            Version::Tagged(vers) => vers.parse()?,
        };
        Ok(versions.nth(index))
    }

    /// Query to determine all file versions that have been saved.  This can be used to list
    /// snapshots that have been taken.
    pub fn query(&self) -> Result<Vec<BkSureFile>> {
        let output = Command::new("bk")
            .args(&["changes", "-v", "-d:INDENT::DPN: :REV: :C:\n"])
            .current_dir(&self.base)
            .output()?;
        if !output.stderr.is_empty() {
            return Err(WeaveError::BkError(
                output.status,
                String::from_utf8_lossy(&output.stderr).into_owned(),
            ).into());
        }
        if !output.status.success() {
            return Err(WeaveError::BkError(output.status, "".into()).into());
        }

        let mut result = vec![];

        for line in (&output.stdout[..]).lines() {
            let line = line?;
            match self.change_re.captures(&line) {
                None => (),
                Some(cap) => {
                    let file = cap.get(1).unwrap().as_str();
                    let rev = cap.get(2).unwrap().as_str();
                    let name = cap.get(3).unwrap().as_str();
                    if !file.ends_with(".dat") {
                        continue;
                    }
                    result.push(BkSureFile {
                        file: file.to_owned(),
                        rev: rev.to_owned(),
                        name: name.to_owned(),
                    });
                }
            }
        }
        Ok(result)
    }
}

/// Information about a single revision stored in a single file in a Bitkeeper repository.
#[derive(Debug)]
pub struct BkSureFile {
    /// The filename of the store file.  In this case, the name contains the ".dat" extension.
    pub file: String,
    /// The textual revision number in Bitkeeper for this particular version.
    pub rev: String,
    /// The name given to the "name=..." tag when this snapshot was taken.
    pub name: String,
}

fn name_of_tags(tags: &StoreTags) -> String {
    if tags.len() != 1 {
        panic!("Must be a single tag name=...");
    }

    match tags.get("name") {
        None => panic!("Must be a single tag name=..."),
        Some(x) => x.clone(),
    }
}

/// Initialize a new BitKeeper-based storage directory.  The path should
/// name a directory that is either empty or can be created with a single
/// `mkdir`.
pub fn bk_setup<P: AsRef<Path>>(base: P) -> Result<()> {
    let base = base.as_ref();

    let mut cmd = Command::new("bk");
    // BAM=off is needed to keep BK from storing large files as just whole
    // files.  Surefiles will often be large, and the delta storage is the
    // whole reason we're using BK.
    // checkout=none frees up some space by not leaving uncompressed copies
    // of the surefiles in the work directory.
    cmd.args(&["setup", "-f", "-FBAM=off", "-Fcheckout=none"]);
    cmd.arg(base.as_os_str());
    let status = cmd.status()?;
    if !status.success() {
        return Err(WeaveError::BkError(status, "".into()).into());
    }

    // Construct a README file in this directory, since there won't appear
    // to be files in it, other than the BitKeeper directory.
    {
        let mut ofd = File::create(base.join("README"))?;
        ofd.write_all(include_bytes!("../../etc/template-bk-readme.txt"))?;
    }

    let status = Command::new("bk")
        .args(&["ci", "-iu", "README"])
        .current_dir(base)
        .status()?;
    if !status.success() {
        return Err(WeaveError::BkError(status, "".into()).into());
    }

    let status = Command::new("bk")
        .args(&["commit", "-yInitial README"])
        .current_dir(base)
        .status()?;
    if !status.success() {
        return Err(WeaveError::BkError(status, "".into()).into());
    }

    Ok(())
}
<commit_msg>store: Remove BitKeeper support<commit_after><|endoftext|>


<commit_before>pub use libc::dev_t;
pub use libc::stat as FileStat;

use {Errno, Result, NixPath};
use fcntl::AtFlags;
use libc::{self, mode_t};
use std::mem;
use std::os::unix::io::RawFd;

libc_bitflags!(
    pub struct SFlag: mode_t {
        S_IFIFO;
        S_IFCHR;
        S_IFDIR;
        S_IFBLK;
        S_IFREG;
        S_IFLNK;
        S_IFSOCK;
        S_IFMT;
    }
);

bitflags! {
    pub struct Mode: mode_t {
        const S_IRWXU = libc::S_IRWXU;
        const S_IRUSR = libc::S_IRUSR;
        const S_IWUSR = libc::S_IWUSR;
        const S_IXUSR = libc::S_IXUSR;

        const S_IRWXG = libc::S_IRWXG;
        const S_IRGRP = libc::S_IRGRP;
        const S_IWGRP = libc::S_IWGRP;
        const S_IXGRP = libc::S_IXGRP;

        const S_IRWXO = libc::S_IRWXO;
        const S_IROTH = libc::S_IROTH;
        const S_IWOTH = libc::S_IWOTH;
        const S_IXOTH = libc::S_IXOTH;

        const S_ISUID = libc::S_ISUID as mode_t;
        const S_ISGID = libc::S_ISGID as mode_t;
        const S_ISVTX = libc::S_ISVTX as mode_t;
    }
}

pub fn mknod<P: ?Sized + NixPath>(path: &P, kind: SFlag, perm: Mode, dev: dev_t) -> Result<()> {
    let res = try!(path.with_nix_path(|cstr| {
        unsafe {
            libc::mknod(cstr.as_ptr(), kind.bits | perm.bits() as mode_t, dev)
        }
    }));

    Errno::result(res).map(drop)
}

#[cfg(target_os = "linux")]
pub fn major(dev: dev_t) -> u64 {
    ((dev >> 32) & 0xfffff000) |
    ((dev >>  8) & 0x00000fff)
}

#[cfg(target_os = "linux")]
pub fn minor(dev: dev_t) -> u64 {
    ((dev >> 12) & 0xffffff00) |
    ((dev      ) & 0x000000ff)
}

#[cfg(target_os = "linux")]
pub fn makedev(major: u64, minor: u64) -> dev_t {
    ((major & 0xfffff000) << 32) |
    ((major & 0x00000fff) <<  8) |
    ((minor & 0xffffff00) << 12) |
    ((minor & 0x000000ff)      )
}

pub fn umask(mode: Mode) -> Mode {
    let prev = unsafe { libc::umask(mode.bits() as mode_t) };
    Mode::from_bits(prev).expect("[BUG] umask returned invalid Mode")
}

pub fn stat<P: ?Sized + NixPath>(path: &P) -> Result<FileStat> {
    let mut dst = unsafe { mem::uninitialized() };
    let res = try!(path.with_nix_path(|cstr| {
        unsafe {
            libc::stat(cstr.as_ptr(), &mut dst as *mut FileStat)
        }
    }));

    try!(Errno::result(res));

    Ok(dst)
}

pub fn lstat<P: ?Sized + NixPath>(path: &P) -> Result<FileStat> {
    let mut dst = unsafe { mem::uninitialized() };
    let res = try!(path.with_nix_path(|cstr| {
        unsafe {
            libc::lstat(cstr.as_ptr(), &mut dst as *mut FileStat)
        }
    }));

    try!(Errno::result(res));

    Ok(dst)
}

pub fn fstat(fd: RawFd) -> Result<FileStat> {
    let mut dst = unsafe { mem::uninitialized() };
    let res = unsafe { libc::fstat(fd, &mut dst as *mut FileStat) };

    try!(Errno::result(res));

    Ok(dst)
}

pub fn fstatat<P: ?Sized + NixPath>(dirfd: RawFd, pathname: &P, f: AtFlags) -> Result<FileStat> {
    let mut dst = unsafe { mem::uninitialized() };
    let res = try!(pathname.with_nix_path(|cstr| {
        unsafe { libc::fstatat(dirfd, cstr.as_ptr(), &mut dst as *mut FileStat, f.bits() as libc::c_int) }
    }));

    try!(Errno::result(res));

    Ok(dst)
}

<commit_msg>Use libc_bitflags for stat::Mode<commit_after>pub use libc::dev_t;
pub use libc::stat as FileStat;

use {Errno, Result, NixPath};
use fcntl::AtFlags;
use libc::{self, mode_t};
use std::mem;
use std::os::unix::io::RawFd;

libc_bitflags!(
    pub struct SFlag: mode_t {
        S_IFIFO;
        S_IFCHR;
        S_IFDIR;
        S_IFBLK;
        S_IFREG;
        S_IFLNK;
        S_IFSOCK;
        S_IFMT;
    }
);

libc_bitflags! {
    pub struct Mode: mode_t {
        S_IRWXU;
        S_IRUSR;
        S_IWUSR;
        S_IXUSR;
        S_IRWXG;
        S_IRGRP;
        S_IWGRP;
        S_IXGRP;
        S_IRWXO;
        S_IROTH;
        S_IWOTH;
        S_IXOTH;
        S_ISUID as mode_t;
        S_ISGID as mode_t;
        S_ISVTX as mode_t;
    }
}

pub fn mknod<P: ?Sized + NixPath>(path: &P, kind: SFlag, perm: Mode, dev: dev_t) -> Result<()> {
    let res = try!(path.with_nix_path(|cstr| {
        unsafe {
            libc::mknod(cstr.as_ptr(), kind.bits | perm.bits() as mode_t, dev)
        }
    }));

    Errno::result(res).map(drop)
}

#[cfg(target_os = "linux")]
pub fn major(dev: dev_t) -> u64 {
    ((dev >> 32) & 0xfffff000) |
    ((dev >>  8) & 0x00000fff)
}

#[cfg(target_os = "linux")]
pub fn minor(dev: dev_t) -> u64 {
    ((dev >> 12) & 0xffffff00) |
    ((dev      ) & 0x000000ff)
}

#[cfg(target_os = "linux")]
pub fn makedev(major: u64, minor: u64) -> dev_t {
    ((major & 0xfffff000) << 32) |
    ((major & 0x00000fff) <<  8) |
    ((minor & 0xffffff00) << 12) |
    ((minor & 0x000000ff)      )
}

pub fn umask(mode: Mode) -> Mode {
    let prev = unsafe { libc::umask(mode.bits() as mode_t) };
    Mode::from_bits(prev).expect("[BUG] umask returned invalid Mode")
}

pub fn stat<P: ?Sized + NixPath>(path: &P) -> Result<FileStat> {
    let mut dst = unsafe { mem::uninitialized() };
    let res = try!(path.with_nix_path(|cstr| {
        unsafe {
            libc::stat(cstr.as_ptr(), &mut dst as *mut FileStat)
        }
    }));

    try!(Errno::result(res));

    Ok(dst)
}

pub fn lstat<P: ?Sized + NixPath>(path: &P) -> Result<FileStat> {
    let mut dst = unsafe { mem::uninitialized() };
    let res = try!(path.with_nix_path(|cstr| {
        unsafe {
            libc::lstat(cstr.as_ptr(), &mut dst as *mut FileStat)
        }
    }));

    try!(Errno::result(res));

    Ok(dst)
}

pub fn fstat(fd: RawFd) -> Result<FileStat> {
    let mut dst = unsafe { mem::uninitialized() };
    let res = unsafe { libc::fstat(fd, &mut dst as *mut FileStat) };

    try!(Errno::result(res));

    Ok(dst)
}

pub fn fstatat<P: ?Sized + NixPath>(dirfd: RawFd, pathname: &P, f: AtFlags) -> Result<FileStat> {
    let mut dst = unsafe { mem::uninitialized() };
    let res = try!(pathname.with_nix_path(|cstr| {
        unsafe { libc::fstatat(dirfd, cstr.as_ptr(), &mut dst as *mut FileStat, f.bits() as libc::c_int) }
    }));

    try!(Errno::result(res));

    Ok(dst)
}

<|endoftext|>