<commit_before>// Copyright (c) 2016-2017 Chef Software Inc. and/or applicable contributors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use std::path::Path;
use std::time::Duration;

use hab_core::util::sys;
use hyper::client::{Client as HyperClient, IntoUrl, RequestBuilder};
use hyper::client::pool::{Config, Pool};
use hyper::header::UserAgent;
use hyper::http::h1::Http11Protocol;
use hyper::net::HttpsConnector;
use hyper_openssl::OpensslClient;
use openssl::ssl::{SslConnectorBuilder, SslConnector, SslMethod, SslOption, SSL_OP_NO_SSLV2,
                   SSL_OP_NO_SSLV3, SSL_OP_NO_COMPRESSION};
use url::Url;

use error::Result;
use net::ProxyHttpsConnector;
use proxy::{ProxyInfo, proxy_unless_domain_exempted};
use ssl;

// Read and write TCP socket timeout for Hyper/HTTP client calls.
const CLIENT_SOCKET_RW_TIMEOUT: u64 = 30;

header! { (ProxyAuthorization, "Proxy-Authorization") => [String] }

/// A generic wrapper around a Hyper HTTP client intended for API-like usage.
///
/// When an `ApiClient` is created, it has a constant URL base which is assumed to be some API
/// endpoint. This allows the underlying Hyper client to load and use any relevant HTTP proxy
/// support and to provide reasonable User-Agent HTTP headers, etc.
#[derive(Debug)]
pub struct ApiClient {
    /// The base URL for the client.
    endpoint: Url,
    /// An instance of a `hyper::Client` which is configured with an SSL context and optionally
    /// using an HTTP proxy.
    inner: HyperClient,
    /// Proxy information, if a proxy is being used.
    proxy: Option<ProxyInfo>,
    /// The URL scheme of the endpoint.
    target_scheme: String,
    /// The `User-Agent` header string to use for HTTP calls.
    user_agent_header: UserAgent,
}

impl ApiClient {
    /// Creates and returns a new `ApiClient` instance.
    ///
    /// # Errors
    ///
    /// * If the underlying Hyper client cannot be created
    /// * If a suitable SSL context cannot be established
    /// * If an HTTP proxy cannot be correctly setup
    /// * If a `User-Agent` HTTP header string cannot be constructed
    pub fn new<T>(
        endpoint: T,
        product: &str,
        version: &str,
        fs_root_path: Option<&Path>,
    ) -> Result<Self>
    where
        T: IntoUrl,
    {
        let endpoint = endpoint.into_url()?;
        Ok(ApiClient {
            inner: new_hyper_client(&endpoint, fs_root_path)?,
            proxy: proxy_unless_domain_exempted(Some(&endpoint))?,
            target_scheme: endpoint.scheme().to_string(),
            endpoint: endpoint,
            user_agent_header: user_agent(product, version)?,
        })
    }

    /// Builds an HTTP GET request for a given path.
    pub fn get(&self, path: &str) -> RequestBuilder {
        self.get_with_custom_url(path, |_| {})
    }

    /// Builds an HTTP GET request for a given path with the ability to customize the target URL.
    pub fn get_with_custom_url<F>(&self, path: &str, mut customize_url: F) -> RequestBuilder
    where
        F: FnMut(&mut Url),
    {
        let mut url = self.url_for(path);
        customize_url(&mut url);
        debug!("GET {} with {:?}", &url, &self);
        self.add_headers(self.inner.get(url))
    }

    /// Builds an HTTP HEAD request for a given path.
    pub fn head(&self, path: &str) -> RequestBuilder {
        self.head_with_custom_url(path, |_| {})
    }

    /// Builds an HTTP HEAD request for a given path with the ability to customize the target URL.
    pub fn head_with_custom_url<F>(&self, path: &str, mut customize_url: F) -> RequestBuilder
    where
        F: FnMut(&mut Url),
    {
        let mut url = self.url_for(path);
        customize_url(&mut url);
        debug!("HEAD {} with {:?}", &url, &self);
        self.add_headers(self.inner.head(url))
    }

    /// Builds an HTTP PATCH request for a given path.
    pub fn patch(&self, path: &str) -> RequestBuilder {
        self.patch_with_custom_url(path, |_| {})
    }

    /// Builds an HTTP PATCH request for a given path with the ability to customize the target URL.
    pub fn patch_with_custom_url<F>(&self, path: &str, mut customize_url: F) -> RequestBuilder
    where
        F: FnMut(&mut Url),
    {
        let mut url = self.url_for(path);
        customize_url(&mut url);
        debug!("PATH {} with {:?}", &url, &self);
        self.add_headers(self.inner.patch(url))
    }

    /// Builds an HTTP POST request for a given path.
    pub fn post(&self, path: &str) -> RequestBuilder {
        self.post_with_custom_url(path, |_| {})
    }

    /// Builds an HTTP POST request for a given path with the ability to customize the target URL.
    pub fn post_with_custom_url<F>(&self, path: &str, mut customize_url: F) -> RequestBuilder
    where
        F: FnMut(&mut Url),
    {
        let mut url = self.url_for(path);
        customize_url(&mut url);
        debug!("POST {} with {:?}", &url, &self);
        self.add_headers(self.inner.post(url))
    }

    /// Builds an HTTP PUT request for a given path.
    pub fn put(&self, path: &str) -> RequestBuilder {
        self.put_with_custom_url(path, |_| {})
    }

    /// Builds an HTTP PUT request for a given path with the ability to customize the target URL.
    pub fn put_with_custom_url<F>(&self, path: &str, mut customize_url: F) -> RequestBuilder
    where
        F: FnMut(&mut Url),
    {
        let mut url = self.url_for(path);
        customize_url(&mut url);
        debug!("PUT {} with {:?}", &url, &self);
        self.add_headers(self.inner.put(url))
    }

    /// Builds an HTTP DELETE request for a given path.
    pub fn delete(&self, path: &str) -> RequestBuilder {
        self.delete_with_custom_url(path, |_| {})
    }

    /// Builds an HTTP DELETE request for a given path with the ability to customize the target URL.
    pub fn delete_with_custom_url<F>(&self, path: &str, mut customize_url: F) -> RequestBuilder
    where
        F: FnMut(&mut Url),
    {
        let mut url = self.url_for(path);
        customize_url(&mut url);
        debug!("DELETE {} with {:?}", &url, &self);
        self.add_headers(self.inner.delete(url))
    }

    fn add_headers<'a>(&'a self, rb: RequestBuilder<'a>) -> RequestBuilder {
        let mut rb = rb.header(self.user_agent_header.clone());
        // If the target URL is an `"http"` scheme and we're using a proxy server, then add the
        // proxy authorization header if appropriate. Note that for `"https"` targets, the proxy
        // server will be operating in TCP tunneling mode and will be authenticated on connection to
        // the proxy server which is why we should not add an additional header in this latter
        // case.
        if self.target_scheme == "http" {
            if let Some(ref info) = self.proxy {
                if let Some(header_value) = info.authorization_header_value() {
                    rb = rb.header(ProxyAuthorization(header_value));
                }
            }
        }
        rb
    }

    fn url_for(&self, path: &str) -> Url {
        let mut url = self.endpoint.clone();
        if path.is_empty() {
            return url;
        }
        if url.path().ends_with("/") {
            url.set_path(&format!("{}{}", self.endpoint.path(), path));
        } else {
            url.set_path(&format!("{}/{}", self.endpoint.path(), path));
        }
        url
    }
}

/// Builds a new hyper HTTP client with appropriate SSL configuration and HTTP/HTTPS proxy support.
///
/// ## Linux Platforms
///
/// We need a set of root certificates when connected to SSL/TLS web endpoints and this usually
/// boiled down to using an all-in-one certificate file (such as a `cacert.pem` file) or a directory
/// of files which are certificates. The strategy to location or use a reasonable set of
/// certificates is as follows:
///
/// 1. If the `SSL_CERT_FILE` environment variable is set, then use its value for the certificates.
///    Internally this is triggering default OpenSSL behavior for this environment variable.
/// 2. If the `SSL_CERT_DIR` environment variable is set, then use its value for the directory
///    containing certificates. Like the `SSL_CERT_FILE` case above, this triggers default OpenSSL
///    behavior for this environment variable.
/// 3. If the `core/cacerts` Habitat package is installed locally, then use the latest release's
///    `cacert.pem` file.
/// 4. If none of the conditions above are met, then a `cacert.pem` will be written in an SSL cache
///    directory (by default `/hab/cache/ssl` for a root user and `$HOME/.hab/cache/ssl` for a
///    non-root user) and this will be used. The contents of this file will be inlined in this
///    crate at build time as a fallback insurance policy, meaning that if the a program using this
///    code is operating in a minimal environment which may not contain system certificates, it can
///    still operate. Once a `core/cacerts` Habitat package is present, the logic would fall back
///    to preferring the package version to the cached/inline file version.
///
/// ## Mac Platforms
///
/// The Mac platform uses a Security Framework to store and find root certificates and the hyper
/// library will default to using this on the Mac. Therefore the behavior on the Mac remains
/// unchanged and will use the system's certificates.
///
fn new_hyper_client(url: &Url, fs_root_path: Option<&Path>) -> Result<HyperClient> {
    let connector = ssl_connector(fs_root_path)?;
    let ssl_client = OpensslClient::from(connector);
    let timeout = Some(Duration::from_secs(CLIENT_SOCKET_RW_TIMEOUT));

    match proxy_unless_domain_exempted(Some(url))? {
        Some(proxy) => {
            debug!("Using proxy {}:{}...", proxy.host(), proxy.port());
            let connector = ProxyHttpsConnector::new(proxy, ssl_client)?;
            let pool = Pool::with_connector(Config::default(), connector);
            let mut client = HyperClient::with_protocol(Http11Protocol::with_connector(pool));
            client.set_read_timeout(timeout);
            client.set_write_timeout(timeout);
            Ok(client)
        }
        None => {
            let connector = HttpsConnector::new(ssl_client);
            let pool = Pool::with_connector(Config::default(), connector);
            let mut client = HyperClient::with_protocol(Http11Protocol::with_connector(pool));
            client.set_read_timeout(timeout);
            client.set_write_timeout(timeout);
            Ok(client)
        }
    }
}

/// Returns an HTTP User-Agent string type for use by Hyper when making HTTP requests.
///
/// The general form for Habitat-related clients are of the following form:
///
/// ```text
/// <PRODUCT>/<VERSION> (<TARGET>; <KERNEL_RELEASE>)
/// ```
///
/// where:
///
/// * `<PRODUCT>`: is the provided product name
/// * `<VERSION>`: is the provided version string which may also include a release number
/// * `<TARGET>`: is the machine architecture and the kernel separated by a dash in lower case
/// * `<KERNEL_RELEASE>`: is the kernel release string from `uname`
///
/// For example:
///
/// ```text
/// hab/0.6.0/20160606153031 (x86_64-darwin; 14.5.0)
/// ```
///
/// # Errors
///
/// * If system information cannot be obtained via `uname`
fn user_agent(product: &str, version: &str) -> Result<UserAgent> {
    let uname = sys::uname()?;
    let ua = format!(
        "{}/{} ({}-{}; {})",
        product.trim(),
        version.trim(),
        uname.machine.trim().to_lowercase(),
        uname.sys_name.trim().to_lowercase(),
        uname.release.trim().to_lowercase()
    );
    debug!("User-Agent: {}", &ua);
    Ok(UserAgent(ua))
}

fn ssl_connector(fs_root_path: Option<&Path>) -> Result<SslConnector> {
    let mut conn = SslConnectorBuilder::new(SslMethod::tls())?;
    let mut options = SslOption::empty();
    options.toggle(SSL_OP_NO_SSLV2);
    options.toggle(SSL_OP_NO_SSLV3);
    options.toggle(SSL_OP_NO_COMPRESSION);
    ssl::set_ca(conn.builder_mut(), fs_root_path)?;
    conn.builder_mut().set_options(options);
    conn.builder_mut().set_cipher_list(
        "ALL!EXPORT!EXPORT40!EXPORT56!aNULL!LOW!RC4@STRENGTH",
    )?;
    Ok(conn.build())
}
<commit_msg>Update http timeout, add retry for key fetch<commit_after>// Copyright (c) 2016-2017 Chef Software Inc. and/or applicable contributors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use std::path::Path;
use std::time::Duration;

use hab_core::util::sys;
use hyper::client::{Client as HyperClient, IntoUrl, RequestBuilder};
use hyper::client::pool::{Config, Pool};
use hyper::header::UserAgent;
use hyper::http::h1::Http11Protocol;
use hyper::net::HttpsConnector;
use hyper_openssl::OpensslClient;
use openssl::ssl::{SslConnectorBuilder, SslConnector, SslMethod, SslOption, SSL_OP_NO_SSLV2,
                   SSL_OP_NO_SSLV3, SSL_OP_NO_COMPRESSION};
use url::Url;

use error::Result;
use net::ProxyHttpsConnector;
use proxy::{ProxyInfo, proxy_unless_domain_exempted};
use ssl;

// Read and write TCP socket timeout for Hyper/HTTP client calls.
const CLIENT_SOCKET_RW_TIMEOUT: u64 = 60;

header! { (ProxyAuthorization, "Proxy-Authorization") => [String] }

/// A generic wrapper around a Hyper HTTP client intended for API-like usage.
///
/// When an `ApiClient` is created, it has a constant URL base which is assumed to be some API
/// endpoint. This allows the underlying Hyper client to load and use any relevant HTTP proxy
/// support and to provide reasonable User-Agent HTTP headers, etc.
#[derive(Debug)]
pub struct ApiClient {
    /// The base URL for the client.
    endpoint: Url,
    /// An instance of a `hyper::Client` which is configured with an SSL context and optionally
    /// using an HTTP proxy.
    inner: HyperClient,
    /// Proxy information, if a proxy is being used.
    proxy: Option<ProxyInfo>,
    /// The URL scheme of the endpoint.
    target_scheme: String,
    /// The `User-Agent` header string to use for HTTP calls.
    user_agent_header: UserAgent,
}

impl ApiClient {
    /// Creates and returns a new `ApiClient` instance.
    ///
    /// # Errors
    ///
    /// * If the underlying Hyper client cannot be created
    /// * If a suitable SSL context cannot be established
    /// * If an HTTP proxy cannot be correctly setup
    /// * If a `User-Agent` HTTP header string cannot be constructed
    pub fn new<T>(
        endpoint: T,
        product: &str,
        version: &str,
        fs_root_path: Option<&Path>,
    ) -> Result<Self>
    where
        T: IntoUrl,
    {
        let endpoint = endpoint.into_url()?;
        Ok(ApiClient {
            inner: new_hyper_client(&endpoint, fs_root_path)?,
            proxy: proxy_unless_domain_exempted(Some(&endpoint))?,
            target_scheme: endpoint.scheme().to_string(),
            endpoint: endpoint,
            user_agent_header: user_agent(product, version)?,
        })
    }

    /// Builds an HTTP GET request for a given path.
    pub fn get(&self, path: &str) -> RequestBuilder {
        self.get_with_custom_url(path, |_| {})
    }

    /// Builds an HTTP GET request for a given path with the ability to customize the target URL.
    pub fn get_with_custom_url<F>(&self, path: &str, mut customize_url: F) -> RequestBuilder
    where
        F: FnMut(&mut Url),
    {
        let mut url = self.url_for(path);
        customize_url(&mut url);
        debug!("GET {} with {:?}", &url, &self);
        self.add_headers(self.inner.get(url))
    }

    /// Builds an HTTP HEAD request for a given path.
    pub fn head(&self, path: &str) -> RequestBuilder {
        self.head_with_custom_url(path, |_| {})
    }

    /// Builds an HTTP HEAD request for a given path with the ability to customize the target URL.
    pub fn head_with_custom_url<F>(&self, path: &str, mut customize_url: F) -> RequestBuilder
    where
        F: FnMut(&mut Url),
    {
        let mut url = self.url_for(path);
        customize_url(&mut url);
        debug!("HEAD {} with {:?}", &url, &self);
        self.add_headers(self.inner.head(url))
    }

    /// Builds an HTTP PATCH request for a given path.
    pub fn patch(&self, path: &str) -> RequestBuilder {
        self.patch_with_custom_url(path, |_| {})
    }

    /// Builds an HTTP PATCH request for a given path with the ability to customize the target URL.
    pub fn patch_with_custom_url<F>(&self, path: &str, mut customize_url: F) -> RequestBuilder
    where
        F: FnMut(&mut Url),
    {
        let mut url = self.url_for(path);
        customize_url(&mut url);
        debug!("PATH {} with {:?}", &url, &self);
        self.add_headers(self.inner.patch(url))
    }

    /// Builds an HTTP POST request for a given path.
    pub fn post(&self, path: &str) -> RequestBuilder {
        self.post_with_custom_url(path, |_| {})
    }

    /// Builds an HTTP POST request for a given path with the ability to customize the target URL.
    pub fn post_with_custom_url<F>(&self, path: &str, mut customize_url: F) -> RequestBuilder
    where
        F: FnMut(&mut Url),
    {
        let mut url = self.url_for(path);
        customize_url(&mut url);
        debug!("POST {} with {:?}", &url, &self);
        self.add_headers(self.inner.post(url))
    }

    /// Builds an HTTP PUT request for a given path.
    pub fn put(&self, path: &str) -> RequestBuilder {
        self.put_with_custom_url(path, |_| {})
    }

    /// Builds an HTTP PUT request for a given path with the ability to customize the target URL.
    pub fn put_with_custom_url<F>(&self, path: &str, mut customize_url: F) -> RequestBuilder
    where
        F: FnMut(&mut Url),
    {
        let mut url = self.url_for(path);
        customize_url(&mut url);
        debug!("PUT {} with {:?}", &url, &self);
        self.add_headers(self.inner.put(url))
    }

    /// Builds an HTTP DELETE request for a given path.
    pub fn delete(&self, path: &str) -> RequestBuilder {
        self.delete_with_custom_url(path, |_| {})
    }

    /// Builds an HTTP DELETE request for a given path with the ability to customize the target URL.
    pub fn delete_with_custom_url<F>(&self, path: &str, mut customize_url: F) -> RequestBuilder
    where
        F: FnMut(&mut Url),
    {
        let mut url = self.url_for(path);
        customize_url(&mut url);
        debug!("DELETE {} with {:?}", &url, &self);
        self.add_headers(self.inner.delete(url))
    }

    fn add_headers<'a>(&'a self, rb: RequestBuilder<'a>) -> RequestBuilder {
        let mut rb = rb.header(self.user_agent_header.clone());
        // If the target URL is an `"http"` scheme and we're using a proxy server, then add the
        // proxy authorization header if appropriate. Note that for `"https"` targets, the proxy
        // server will be operating in TCP tunneling mode and will be authenticated on connection to
        // the proxy server which is why we should not add an additional header in this latter
        // case.
        if self.target_scheme == "http" {
            if let Some(ref info) = self.proxy {
                if let Some(header_value) = info.authorization_header_value() {
                    rb = rb.header(ProxyAuthorization(header_value));
                }
            }
        }
        rb
    }

    fn url_for(&self, path: &str) -> Url {
        let mut url = self.endpoint.clone();
        if path.is_empty() {
            return url;
        }
        if url.path().ends_with("/") {
            url.set_path(&format!("{}{}", self.endpoint.path(), path));
        } else {
            url.set_path(&format!("{}/{}", self.endpoint.path(), path));
        }
        url
    }
}

/// Builds a new hyper HTTP client with appropriate SSL configuration and HTTP/HTTPS proxy support.
///
/// ## Linux Platforms
///
/// We need a set of root certificates when connected to SSL/TLS web endpoints and this usually
/// boiled down to using an all-in-one certificate file (such as a `cacert.pem` file) or a directory
/// of files which are certificates. The strategy to location or use a reasonable set of
/// certificates is as follows:
///
/// 1. If the `SSL_CERT_FILE` environment variable is set, then use its value for the certificates.
///    Internally this is triggering default OpenSSL behavior for this environment variable.
/// 2. If the `SSL_CERT_DIR` environment variable is set, then use its value for the directory
///    containing certificates. Like the `SSL_CERT_FILE` case above, this triggers default OpenSSL
///    behavior for this environment variable.
/// 3. If the `core/cacerts` Habitat package is installed locally, then use the latest release's
///    `cacert.pem` file.
/// 4. If none of the conditions above are met, then a `cacert.pem` will be written in an SSL cache
///    directory (by default `/hab/cache/ssl` for a root user and `$HOME/.hab/cache/ssl` for a
///    non-root user) and this will be used. The contents of this file will be inlined in this
///    crate at build time as a fallback insurance policy, meaning that if the a program using this
///    code is operating in a minimal environment which may not contain system certificates, it can
///    still operate. Once a `core/cacerts` Habitat package is present, the logic would fall back
///    to preferring the package version to the cached/inline file version.
///
/// ## Mac Platforms
///
/// The Mac platform uses a Security Framework to store and find root certificates and the hyper
/// library will default to using this on the Mac. Therefore the behavior on the Mac remains
/// unchanged and will use the system's certificates.
///
fn new_hyper_client(url: &Url, fs_root_path: Option<&Path>) -> Result<HyperClient> {
    let connector = ssl_connector(fs_root_path)?;
    let ssl_client = OpensslClient::from(connector);
    let timeout = Some(Duration::from_secs(CLIENT_SOCKET_RW_TIMEOUT));

    match proxy_unless_domain_exempted(Some(url))? {
        Some(proxy) => {
            debug!("Using proxy {}:{}...", proxy.host(), proxy.port());
            let connector = ProxyHttpsConnector::new(proxy, ssl_client)?;
            let pool = Pool::with_connector(Config::default(), connector);
            let mut client = HyperClient::with_protocol(Http11Protocol::with_connector(pool));
            client.set_read_timeout(timeout);
            client.set_write_timeout(timeout);
            Ok(client)
        }
        None => {
            let connector = HttpsConnector::new(ssl_client);
            let pool = Pool::with_connector(Config::default(), connector);
            let mut client = HyperClient::with_protocol(Http11Protocol::with_connector(pool));
            client.set_read_timeout(timeout);
            client.set_write_timeout(timeout);
            Ok(client)
        }
    }
}

/// Returns an HTTP User-Agent string type for use by Hyper when making HTTP requests.
///
/// The general form for Habitat-related clients are of the following form:
///
/// ```text
/// <PRODUCT>/<VERSION> (<TARGET>; <KERNEL_RELEASE>)
/// ```
///
/// where:
///
/// * `<PRODUCT>`: is the provided product name
/// * `<VERSION>`: is the provided version string which may also include a release number
/// * `<TARGET>`: is the machine architecture and the kernel separated by a dash in lower case
/// * `<KERNEL_RELEASE>`: is the kernel release string from `uname`
///
/// For example:
///
/// ```text
/// hab/0.6.0/20160606153031 (x86_64-darwin; 14.5.0)
/// ```
///
/// # Errors
///
/// * If system information cannot be obtained via `uname`
fn user_agent(product: &str, version: &str) -> Result<UserAgent> {
    let uname = sys::uname()?;
    let ua = format!(
        "{}/{} ({}-{}; {})",
        product.trim(),
        version.trim(),
        uname.machine.trim().to_lowercase(),
        uname.sys_name.trim().to_lowercase(),
        uname.release.trim().to_lowercase()
    );
    debug!("User-Agent: {}", &ua);
    Ok(UserAgent(ua))
}

fn ssl_connector(fs_root_path: Option<&Path>) -> Result<SslConnector> {
    let mut conn = SslConnectorBuilder::new(SslMethod::tls())?;
    let mut options = SslOption::empty();
    options.toggle(SSL_OP_NO_SSLV2);
    options.toggle(SSL_OP_NO_SSLV3);
    options.toggle(SSL_OP_NO_COMPRESSION);
    ssl::set_ca(conn.builder_mut(), fs_root_path)?;
    conn.builder_mut().set_options(options);
    conn.builder_mut().set_cipher_list(
        "ALL!EXPORT!EXPORT40!EXPORT56!aNULL!LOW!RC4@STRENGTH",
    )?;
    Ok(conn.build())
}
<|endoftext|>


<commit_before>extern mod std;

#[test]
fn test_tokenize() {
    assert tokenize( ~"(1 2 3)" ) == ~[~"(", ~"1", ~"2", ~"3", ~")"];
}

#[test]
fn test_tokenize_empty() {
    assert tokenize( ~"" ) == ~[];
}

#[test]
fn test_tokenize_no_spaces() {
    assert tokenize( ~"one" ) == ~[~"one"];
}

#[test]
fn test_addspaces() {
    assert addspaces( ~"(1 2 3)" ) == ~" ( 1 2 3 ) "
}

fn addspaces( input:&str ) -> ~str {
    str::replace(str::replace(input, ")", " ) "), "(", " ( ")
}

fn tokenize( input:&str ) -> ~[~str] {
    str::split_str_nonempty(addspaces(input), " ")
}

enum Expression {
    Value(int),
    Symbol(~str),
    List(~[Expression])
    }

fn main() {
    let Blah:Expression = List(~[Value(1), List(~[Value(1), Symbol(~"xyz")])]);
    io::println( "(begin 1 2)" )
}
<commit_msg>Refactor tokenize to use str::words<commit_after>extern mod std;

#[test]
fn test_tokenize() {
    assert tokenize( ~"(1 2 3)" ) == ~[~"(", ~"1", ~"2", ~"3", ~")"];
}

#[test]
fn test_tokenize_empty() {
    assert tokenize( ~"" ) == ~[];
}

#[test]
fn test_tokenize_no_spaces() {
    assert tokenize( ~"one" ) == ~[~"one"];
}

#[test]
fn test_addspaces() {
    assert addspaces( ~"(1 2 3)" ) == ~" ( 1 2 3 ) "
}

fn addspaces( input:&str ) -> ~str {
    str::replace(str::replace(input, ")", " ) "), "(", " ( ")
}

fn tokenize( input:&str ) -> ~[~str] {
    str::words(addspaces(input))
}

enum Expression {
    Value(int),
    Symbol(~str),
    List(~[Expression])
    }

fn main() {
    let Blah:Expression = List(~[Value(1), List(~[Value(1), Symbol(~"xyz")])]);
    io::println( "(begin 1 2)" )
}
<|endoftext|>


<commit_before>
<commit_msg>Delete a.rs<commit_after><|endoftext|>


<commit_before>use lsp_types::{SemanticTokenType, SemanticTokens, SemanticTokensLegend};

pub fn legend() -> SemanticTokensLegend {
    SemanticTokensLegend {
        token_types: vec![
            SemanticTokenType::COMMENT,        // 0
            SemanticTokenType::KEYWORD,        // 1
            SemanticTokenType::NAMESPACE,      // 2
            SemanticTokenType::TYPE,           // 3
            SemanticTokenType::TYPE_PARAMETER, // 4
            SemanticTokenType::ENUM_MEMBER,    // 5
            SemanticTokenType::STRING,         // 6
            SemanticTokenType::NUMBER,         // 7
            SemanticTokenType::MACRO,          // 8
        ],
        token_modifiers: vec![
            // TODO
        ],
    }
}

#[derive(Debug, Clone, Copy)]
enum TokenType {
    // Keep these in sync with indices of `token_types` above!
    Comment = 0,
    Keyword = 1,
    Namespace = 2,
    Type = 3,
    TypeVariable = 4,
    Constructor = 5,
    String = 6,
    Number = 7,
    Special = 8,
}

pub fn get_tokens(tree: &tree_sitter::Tree, source: &str) -> SemanticTokens {
    let mut tokens_builder = TokensBuilder::new();
    tokens_builder.build(tree, source.as_bytes());
    SemanticTokens {
        result_id: None,
        data: tokens_builder.into_tokens(),
    }
}

struct TokensBuilder(Vec<Node>);

#[derive(Debug)]
struct Node {
    start_line: usize,
    start_col: usize,
    token_type: TokenType,
    length: usize,
}

impl TokensBuilder {
    fn new() -> Self {
        Self(Vec::new())
    }

    fn push_node(&mut self, node: tree_sitter::Node, token_type: TokenType) {
        let tree_sitter::Point { row, column } = node.start_position();
        let length = node.byte_range().len();
        self.0.push(Node {
            start_line: row,
            start_col: column,
            length,
            token_type,
        })
    }

    fn into_tokens(mut self) -> Vec<lsp_types::SemanticToken> {
        let mut tokens = Vec::new();
        self.0.sort_by_key(|node| (node.start_line, node.start_col));
        let mut current_line = 0;
        let mut current_col = 0;
        for node in self.0 {
            let delta_line: u32 = (node.start_line - current_line).try_into().unwrap();
            let delta_start: u32 = if delta_line > 0 {
                node.start_col.try_into().unwrap()
            } else {
                (node.start_col - current_col).try_into().unwrap()
            };
            tokens.push(lsp_types::SemanticToken {
                delta_line,
                delta_start,
                token_type: node.token_type as u32,
                token_modifiers_bitset: 0,
                length: node.length as u32,
            });
            current_line = node.start_line;
            current_col = node.start_col;
        }
        tokens
    }

    fn build(&mut self, tree: &tree_sitter::Tree, source: &[u8]) {
        // NOTE: could just expose the highlights.scm in the tree-sitter-ditto
        // crate but relying on those indices feels brittle/wrong...
        static QUERY: &str = r#"
            ; 0
            (comment) @comment

            ; 1
            [
              "if"
              "then"
              "else"
              "module"
              "exports"
              "import"
              "as"
              "type"
              "foreign"
              "match"
              "with"
            ] @keyword

            ; 2, 3, 4
            (module_name) @namespace
            (module_import_alias) @namespace
            (qualifier) @namespace

            ; 5, 6, 7, 8
            (exposing_type_name) @type
            (type_declaration_name) @type
            (type_constructor) @type
            (type_function ("->" @type))

            ; 9, 10
            (type_variable) @type_variable
            (type_declaration_variable) @type_variable

            ; 11, 12
            (type_declaration_constructor_name) @constructor
            (expression_constructor_proper_name) @constructor

            ; 13
            (expression_string) @string

            ; 14, 15
            (expression_int) @number
            (expression_float) @number

            ; 16, 17
            (expression_true) @boolean
            (expression_false) @boolean

            ; 18
            (expression_unit) @builtin

            ; 19
            ("..") @special

        "#;
        let mut query_cursor = tree_sitter::QueryCursor::new();
        let query = tree_sitter::Query::new(tree_sitter_ditto::language(), QUERY).unwrap();
        let matches = query_cursor.matches(&query, tree.root_node(), source);
        for query_match in matches {
            let token_type = match query_match.pattern_index {
                0 => Some(TokenType::Comment),
                1 => Some(TokenType::Keyword),
                2 | 3 | 4 => Some(TokenType::Namespace),
                5 | 6 | 7 | 8 => Some(TokenType::Type),
                9 | 10 => Some(TokenType::TypeVariable),
                11 | 12 => Some(TokenType::Constructor),
                13 => Some(TokenType::String),
                14 | 15 => Some(TokenType::Number),
                // no boolean token type?
                // https://code.visualstudio.com/api/language-extensions/semantic-highlight-guide#standard-token-types-and-modifiers
                //
                // Might be worth "contributing" one?
                // https://code.visualstudio.com/api/language-extensions/semantic-highlight-guide#custom-token-types-and-modifiers
                16 | 17 | 18 => Some(TokenType::Keyword),
                19 => Some(TokenType::Special),
                _ => None,
            };
            if let Some(token_type) = token_type {
                for capture in query_match.captures {
                    self.push_node(capture.node, token_type)
                }
            }
        }
    }
}
<commit_msg>Add `do` and `return` keywords to lsp semantic highlighting<commit_after>use lsp_types::{SemanticTokenType, SemanticTokens, SemanticTokensLegend};

pub fn legend() -> SemanticTokensLegend {
    SemanticTokensLegend {
        token_types: vec![
            SemanticTokenType::COMMENT,        // 0
            SemanticTokenType::KEYWORD,        // 1
            SemanticTokenType::NAMESPACE,      // 2
            SemanticTokenType::TYPE,           // 3
            SemanticTokenType::TYPE_PARAMETER, // 4
            SemanticTokenType::ENUM_MEMBER,    // 5
            SemanticTokenType::STRING,         // 6
            SemanticTokenType::NUMBER,         // 7
            SemanticTokenType::MACRO,          // 8
        ],
        token_modifiers: vec![
            // TODO
        ],
    }
}

#[derive(Debug, Clone, Copy)]
enum TokenType {
    // Keep these in sync with indices of `token_types` above!
    Comment = 0,
    Keyword = 1,
    Namespace = 2,
    Type = 3,
    TypeVariable = 4,
    Constructor = 5,
    String = 6,
    Number = 7,
    Special = 8,
}

pub fn get_tokens(tree: &tree_sitter::Tree, source: &str) -> SemanticTokens {
    let mut tokens_builder = TokensBuilder::new();
    tokens_builder.build(tree, source.as_bytes());
    SemanticTokens {
        result_id: None,
        data: tokens_builder.into_tokens(),
    }
}

struct TokensBuilder(Vec<Node>);

#[derive(Debug)]
struct Node {
    start_line: usize,
    start_col: usize,
    token_type: TokenType,
    length: usize,
}

impl TokensBuilder {
    fn new() -> Self {
        Self(Vec::new())
    }

    fn push_node(&mut self, node: tree_sitter::Node, token_type: TokenType) {
        let tree_sitter::Point { row, column } = node.start_position();
        let length = node.byte_range().len();
        self.0.push(Node {
            start_line: row,
            start_col: column,
            length,
            token_type,
        })
    }

    fn into_tokens(mut self) -> Vec<lsp_types::SemanticToken> {
        let mut tokens = Vec::new();
        self.0.sort_by_key(|node| (node.start_line, node.start_col));
        let mut current_line = 0;
        let mut current_col = 0;
        for node in self.0 {
            let delta_line: u32 = (node.start_line - current_line).try_into().unwrap();
            let delta_start: u32 = if delta_line > 0 {
                node.start_col.try_into().unwrap()
            } else {
                (node.start_col - current_col).try_into().unwrap()
            };
            tokens.push(lsp_types::SemanticToken {
                delta_line,
                delta_start,
                token_type: node.token_type as u32,
                token_modifiers_bitset: 0,
                length: node.length as u32,
            });
            current_line = node.start_line;
            current_col = node.start_col;
        }
        tokens
    }

    fn build(&mut self, tree: &tree_sitter::Tree, source: &[u8]) {
        // NOTE: could just expose the highlights.scm in the tree-sitter-ditto
        // crate but relying on those indices feels brittle/wrong...
        static QUERY: &str = r#"
            ; 0
            (comment) @comment

            ; 1
            [
              "if"
              "then"
              "else"
              "module"
              "exports"
              "import"
              "as"
              "type"
              "foreign"
              "match"
              "with"
              "do"
              "return"
            ] @keyword

            ; 2, 3, 4
            (module_name) @namespace
            (module_import_alias) @namespace
            (qualifier) @namespace

            ; 5, 6, 7, 8
            (exposing_type_name) @type
            (type_declaration_name) @type
            (type_constructor) @type
            (type_function ("->" @type))

            ; 9, 10
            (type_variable) @type_variable
            (type_declaration_variable) @type_variable

            ; 11, 12
            (type_declaration_constructor_name) @constructor
            (expression_constructor_proper_name) @constructor

            ; 13
            (expression_string) @string

            ; 14, 15
            (expression_int) @number
            (expression_float) @number

            ; 16, 17
            (expression_true) @boolean
            (expression_false) @boolean

            ; 18
            (expression_unit) @builtin

            ; 19
            ("..") @special

        "#;
        let mut query_cursor = tree_sitter::QueryCursor::new();
        let query = tree_sitter::Query::new(tree_sitter_ditto::language(), QUERY).unwrap();
        let matches = query_cursor.matches(&query, tree.root_node(), source);
        for query_match in matches {
            let token_type = match query_match.pattern_index {
                0 => Some(TokenType::Comment),
                1 => Some(TokenType::Keyword),
                2 | 3 | 4 => Some(TokenType::Namespace),
                5 | 6 | 7 | 8 => Some(TokenType::Type),
                9 | 10 => Some(TokenType::TypeVariable),
                11 | 12 => Some(TokenType::Constructor),
                13 => Some(TokenType::String),
                14 | 15 => Some(TokenType::Number),
                // no boolean token type?
                // https://code.visualstudio.com/api/language-extensions/semantic-highlight-guide#standard-token-types-and-modifiers
                //
                // Might be worth "contributing" one?
                // https://code.visualstudio.com/api/language-extensions/semantic-highlight-guide#custom-token-types-and-modifiers
                16 | 17 | 18 => Some(TokenType::Keyword),
                19 => Some(TokenType::Special),
                _ => None,
            };
            if let Some(token_type) = token_type {
                for capture in query_match.captures {
                    self.push_node(capture.node, token_type)
                }
            }
        }
    }
}
<|endoftext|>


<commit_before>/// The query resolver that operates on the AST and the TOML object

use std::ops::Deref;
use std::ops::IndexMut;

use toml::Value;
use tokenizer::Token;
use error::*;

fn resolve<'doc>(toml: &'doc mut Value, tokens: &Token) -> Result<&'doc mut Value> {
    match toml {
        &mut Value::Table(ref mut t) => {
            match tokens {
                &Token::Identifier { ident: ref ident, .. } => {
                    match t.get_mut(ident) {
                        None    => {
                            let ek = ErrorKind::IdentifierNotFoundInDocument(ident.clone());
                            Err(Error::from(ek))
                        },
                        Some(sub_document) => {
                            if tokens.has_next() {
                                resolve(sub_document, tokens.next().unwrap().deref())
                            } else {
                                Ok(sub_document)
                            }
                        },
                    }
                },

                &Token::Index { idx: i, .. } => {
                    let kind = ErrorKind::NoIndexInTable(i);
                    Err(Error::from(kind))
                },
            }
        },

        &mut Value::Array(ref mut ary) => {
            match tokens {
                &Token::Index { idx: i, .. } => {
                    if tokens.has_next() {
                        resolve(ary.get_mut(i).unwrap(), tokens.next().unwrap().deref())
                    } else {
                        Ok(ary.index_mut(i))
                    }
                },
                &Token::Identifier { ident: ref ident, .. } => {
                    let kind = ErrorKind::NoIdentifierInArray(ident.clone());
                    Err(Error::from(kind))
                },
            }
        },

        _ => {
            match tokens {
                &Token::Identifier { ident: ref ident, .. } => {
                    Err(Error::from(ErrorKind::IdentifierNotFoundInDocument(ident.clone())))
                },

                &Token::Index { .. } => unimplemented!(),
            }
        }
    }
}

#[cfg(test)]
mod test {
    use toml::from_str as toml_from_str;
    use toml::Value;
    use tokenizer::*;
    use error::*;
    use super::resolve;

    macro_rules! do_resolve {
        ( $toml:ident => $query:expr ) => {
            resolve(&mut $toml, &tokenize_with_seperator(&String::from($query), '.').unwrap())
        }
    }

    #[test]
    fn test_resolve_empty_toml_simple_query() {
        let mut toml = toml_from_str("").unwrap();
        let result = do_resolve!(toml => "example");

        assert!(result.is_err());
        let result = result.unwrap_err();

        let errkind = result.kind();
        assert!(is_match!(errkind, &ErrorKind::IdentifierNotFoundInDocument { .. }));
    }

    #[test]
    fn test_resolve_present_bool() {
        let mut toml = toml_from_str("example = true").unwrap();
        let result = do_resolve!(toml => "example");

        assert!(result.is_ok());
        let result = result.unwrap();

        assert!(is_match!(result, &mut Value::Boolean(true)));
    }

    #[test]
    fn test_resolve_present_integer() {
        let mut toml = toml_from_str("example = 1").unwrap();
        let result = do_resolve!(toml => "example");

        assert!(result.is_ok());
        let result = result.unwrap();

        assert!(is_match!(result, &mut Value::Integer(1)));
    }

    #[test]
    fn test_resolve_present_float() {
        let mut toml = toml_from_str("example = 1.0").unwrap();
        let result = do_resolve!(toml => "example");

        assert!(result.is_ok());
        let result = result.unwrap();

        assert!(is_match!(result, &mut Value::Float(1.0)));
    }

    #[test]
    fn test_resolve_present_string() {
        let mut toml = toml_from_str("example = 'string'").unwrap();
        let result = do_resolve!(toml => "example");

        assert!(result.is_ok());
        let result = result.unwrap();

        assert!(is_match!(result, &mut Value::String(_)));
        match result {
            &mut Value::String(ref s) => assert_eq!("string", s),
            _ => panic!("What just happened?"),
        }
    }

    #[test]
    fn test_resolve_present_array_bools() {
        let mut toml = toml_from_str("example = [ true, false ]").unwrap();
        let result = do_resolve!(toml => "example");

        assert!(result.is_ok());
        let result = result.unwrap();

        assert!(is_match!(result, &mut Value::Array(_)));
        match result {
            &mut Value::Array(ref ary) => {
                assert_eq!(ary[0], Value::Boolean(true));
                assert_eq!(ary[1], Value::Boolean(false));
            },
            _ => panic!("What just happened?"),
        }
    }

    #[test]
    fn test_resolve_present_array_integers() {
        let mut toml = toml_from_str("example = [ 1, 1337 ]").unwrap();
        let result = do_resolve!(toml => "example");

        assert!(result.is_ok());
        let result = result.unwrap();

        assert!(is_match!(result, &mut Value::Array(_)));
        match result {
            &mut Value::Array(ref ary) => {
                assert_eq!(ary[0], Value::Integer(1));
                assert_eq!(ary[1], Value::Integer(1337));
            },
            _ => panic!("What just happened?"),
        }
    }

    #[test]
    fn test_resolve_present_array_floats() {
        let mut toml = toml_from_str("example = [ 1.0, 133.25 ]").unwrap();
        let result = do_resolve!(toml => "example");

        assert!(result.is_ok());
        let result = result.unwrap();

        assert!(is_match!(result, &mut Value::Array(_)));
        match result {
            &mut Value::Array(ref ary) => {
                assert_eq!(ary[0], Value::Float(1.0));
                assert_eq!(ary[1], Value::Float(133.25));
            },
            _ => panic!("What just happened?"),
        }
    }

    #[test]
    fn test_resolve_array_index_query_1() {
        let mut toml = toml_from_str("example = [ 1 ]").unwrap();
        let result = do_resolve!(toml => "example.[0]");

        assert!(result.is_ok());
        let result = result.unwrap();

        assert!(is_match!(result, &mut Value::Integer(1)));
    }

    #[test]
    fn test_resolve_array_index_query_2() {
        let mut toml = toml_from_str("example = [ 1, 2, 3, 4, 5 ]").unwrap();
        let result = do_resolve!(toml => "example.[4]");

        assert!(result.is_ok());
        let result = result.unwrap();

        assert!(is_match!(result, &mut Value::Integer(5)));
    }

    #[test]
    fn test_resolve_table_element_query() {
        let mut toml = toml_from_str(r#"
        [table]
        value = 42
        "#).unwrap();
        let result = do_resolve!(toml => "table.value");

        assert!(result.is_ok());
        let result = result.unwrap();

        assert!(is_match!(result, &mut Value::Integer(42)));
    }

    #[test]
    fn test_resolve_table_with_many_elements_element_query() {
        let mut toml = toml_from_str(r#"
        [table]
        value1 = 42
        value2 = 43
        value3 = 44
        value4 = 45
        value5 = 46
        "#).unwrap();
        let result = do_resolve!(toml => "table.value1");

        assert!(result.is_ok());
        let result = result.unwrap();

        assert!(is_match!(result, &mut Value::Integer(42)));
    }

    #[test]
    fn test_resolve_table_array_query() {
        let mut toml = toml_from_str(r#"
        [table]
        value1 = [ 42.0, 50.0 ]
        "#).unwrap();
        let result = do_resolve!(toml => "table.value1");

        assert!(result.is_ok());
        let result = result.unwrap();

        assert!(is_match!(result, &mut Value::Array(_)));
        match result {
            &mut Value::Array(ref ary) => {
                assert_eq!(ary[0], Value::Float(42.0));
                assert_eq!(ary[1], Value::Float(50.0));
            },
            _ => panic!("What just happened?"),
        }
    }

    #[test]
    fn test_resolve_table_array_element_query() {
        let mut toml = toml_from_str(r#"
        [table]
        value1 = [ 42 ]
        "#).unwrap();
        let result = do_resolve!(toml => "table.value1.[0]");

        assert!(result.is_ok());
        let result = result.unwrap();

        assert!(is_match!(result, &mut Value::Integer(42)));
    }

    #[test]
    fn test_resolve_multi_table_query() {
        let mut toml = toml_from_str(r#"
        [table0]
        value = [ 1 ]
        [table1]
        value = [ "Foo" ]
        [table2]
        value = [ 42.0 ]
        [table3]
        value = [ true ]
        "#).unwrap();
        let result = do_resolve!(toml => "table1.value.[0]");

        assert!(result.is_ok());
        let result = result.unwrap();

        assert!(is_match!(result, &mut Value::String(_)));
        match result {
            &mut Value::String(ref s) => assert_eq!("Foo", s),
            _ => panic!("What just happened?"),
        }
    }

    static FRUIT_TABLE : &'static str = r#"
    [[fruit.blah]]
      name = "apple"

      [fruit.blah.physical]
        color = "red"
        shape = "round"

    [[fruit.blah]]
      name = "banana"

      [fruit.blah.physical]
        color = "yellow"
        shape = "bent"
    "#;

    #[test]
    fn test_resolve_array_table_query_1() {
        let mut toml = toml_from_str(FRUIT_TABLE).unwrap();
        let result = do_resolve!(toml => "fruit.blah.[0].name");

        assert!(result.is_ok());
        let result = result.unwrap();

        assert!(is_match!(result, &mut Value::String(_)));
        match result {
            &mut Value::String(ref s) => assert_eq!("apple", s),
            _ => panic!("What just happened?"),
        }
    }

    #[test]
    fn test_resolve_array_table_query_2() {
        let mut toml = toml_from_str(FRUIT_TABLE).unwrap();
        let result = do_resolve!(toml => "fruit.blah.[0].physical");

        assert!(result.is_ok());
        let result = result.unwrap();

        assert!(is_match!(result, &mut Value::Table(_)));
        match result {
            &mut Value::Table(ref tab) => {
                match tab.get("color") {
                    Some(&Value::String(ref s)) => assert_eq!("red", s),
                    _ => assert!(false),
                }
                match tab.get("shape") {
                    Some(&Value::String(ref s)) => assert_eq!("round", s),
                    _ => assert!(false),
                }
            },
            _ => panic!("What just happened?"),
        }
    }

    #[test]
    fn test_resolve_query_on_result() {
        let mut toml = toml_from_str(FRUIT_TABLE).unwrap();
        let result = do_resolve!(toml => "fruit.blah.[1].physical");

        assert!(result.is_ok());
        let mut result = result.unwrap();

        let tokens = tokenize_with_seperator(&String::from("color"), '.').unwrap();
        let result = resolve(result, &tokens);

        assert!(result.is_ok());
        let result = result.unwrap();

        assert!(is_match!(result, &mut Value::String(_)));
        match result {
            &mut Value::String(ref s) => assert_eq!("yellow", s),
            _ => panic!("What just happened?"),
        }
    }

    #[test]
    fn test_resolve_query_empty_table() {
        let mut toml = toml_from_str(r#"
        [example]
        "#).unwrap();
        let result = do_resolve!(toml => "example");

        assert!(result.is_ok());
        let result = result.unwrap();

        assert!(is_match!(result, &mut Value::Table(_)));
        match result {
            &mut Value::Table(ref t) => assert!(t.is_empty()),
            _ => panic!("What just happened?"),
        }
    }

    #[test]
    fn test_resolve_query_member_of_empty_table() {
        let mut toml = toml_from_str(r#"
        [example]
        "#).unwrap();
        let result = do_resolve!(toml => "example.foo");

        assert!(result.is_err());
        let result = result.unwrap_err();

        let errkind = result.kind();
        assert!(is_match!(errkind, &ErrorKind::IdentifierNotFoundInDocument { .. }));
    }

    #[test]
    fn test_resolve_query_index_in_table() {
        let mut toml = toml_from_str(r#"
        [example]
        "#).unwrap();
        let result = do_resolve!(toml => "example.[0]");

        assert!(result.is_err());
        let result = result.unwrap_err();

        let errkind = result.kind();
        assert!(is_match!(errkind, &ErrorKind::NoIndexInTable { .. }));
    }

    #[test]
    fn test_resolve_query_identifier_in_array() {
        let mut toml = toml_from_str(r#"
        [example]
        foo = [ 1, 2, 3 ]
        "#).unwrap();
        let result = do_resolve!(toml => "example.foo.bar");

        assert!(result.is_err());
        let result = result.unwrap_err();

        let errkind = result.kind();
        assert!(is_match!(errkind, &ErrorKind::NoIdentifierInArray { .. }));
    }

}
<commit_msg>Add more tests for failing queries<commit_after>/// The query resolver that operates on the AST and the TOML object

use std::ops::Deref;
use std::ops::IndexMut;

use toml::Value;
use tokenizer::Token;
use error::*;

fn resolve<'doc>(toml: &'doc mut Value, tokens: &Token) -> Result<&'doc mut Value> {
    match toml {
        &mut Value::Table(ref mut t) => {
            match tokens {
                &Token::Identifier { ident: ref ident, .. } => {
                    match t.get_mut(ident) {
                        None    => {
                            let ek = ErrorKind::IdentifierNotFoundInDocument(ident.clone());
                            Err(Error::from(ek))
                        },
                        Some(sub_document) => {
                            if tokens.has_next() {
                                resolve(sub_document, tokens.next().unwrap().deref())
                            } else {
                                Ok(sub_document)
                            }
                        },
                    }
                },

                &Token::Index { idx: i, .. } => {
                    let kind = ErrorKind::NoIndexInTable(i);
                    Err(Error::from(kind))
                },
            }
        },

        &mut Value::Array(ref mut ary) => {
            match tokens {
                &Token::Index { idx: i, .. } => {
                    if tokens.has_next() {
                        resolve(ary.get_mut(i).unwrap(), tokens.next().unwrap().deref())
                    } else {
                        Ok(ary.index_mut(i))
                    }
                },
                &Token::Identifier { ident: ref ident, .. } => {
                    let kind = ErrorKind::NoIdentifierInArray(ident.clone());
                    Err(Error::from(kind))
                },
            }
        },

        _ => {
            match tokens {
                &Token::Identifier { ident: ref ident, .. } => {
                    Err(Error::from(ErrorKind::IdentifierNotFoundInDocument(ident.clone())))
                },

                &Token::Index { .. } => unimplemented!(),
            }
        }
    }
}

#[cfg(test)]
mod test {
    use toml::from_str as toml_from_str;
    use toml::Value;
    use tokenizer::*;
    use error::*;
    use super::resolve;

    macro_rules! do_resolve {
        ( $toml:ident => $query:expr ) => {
            resolve(&mut $toml, &tokenize_with_seperator(&String::from($query), '.').unwrap())
        }
    }

    #[test]
    fn test_resolve_empty_toml_simple_query() {
        let mut toml = toml_from_str("").unwrap();
        let result = do_resolve!(toml => "example");

        assert!(result.is_err());
        let result = result.unwrap_err();

        let errkind = result.kind();
        assert!(is_match!(errkind, &ErrorKind::IdentifierNotFoundInDocument { .. }));
    }

    #[test]
    fn test_resolve_present_bool() {
        let mut toml = toml_from_str("example = true").unwrap();
        let result = do_resolve!(toml => "example");

        assert!(result.is_ok());
        let result = result.unwrap();

        assert!(is_match!(result, &mut Value::Boolean(true)));
    }

    #[test]
    fn test_resolve_present_integer() {
        let mut toml = toml_from_str("example = 1").unwrap();
        let result = do_resolve!(toml => "example");

        assert!(result.is_ok());
        let result = result.unwrap();

        assert!(is_match!(result, &mut Value::Integer(1)));
    }

    #[test]
    fn test_resolve_present_float() {
        let mut toml = toml_from_str("example = 1.0").unwrap();
        let result = do_resolve!(toml => "example");

        assert!(result.is_ok());
        let result = result.unwrap();

        assert!(is_match!(result, &mut Value::Float(1.0)));
    }

    #[test]
    fn test_resolve_present_string() {
        let mut toml = toml_from_str("example = 'string'").unwrap();
        let result = do_resolve!(toml => "example");

        assert!(result.is_ok());
        let result = result.unwrap();

        assert!(is_match!(result, &mut Value::String(_)));
        match result {
            &mut Value::String(ref s) => assert_eq!("string", s),
            _ => panic!("What just happened?"),
        }
    }

    #[test]
    fn test_resolve_present_array_bools() {
        let mut toml = toml_from_str("example = [ true, false ]").unwrap();
        let result = do_resolve!(toml => "example");

        assert!(result.is_ok());
        let result = result.unwrap();

        assert!(is_match!(result, &mut Value::Array(_)));
        match result {
            &mut Value::Array(ref ary) => {
                assert_eq!(ary[0], Value::Boolean(true));
                assert_eq!(ary[1], Value::Boolean(false));
            },
            _ => panic!("What just happened?"),
        }
    }

    #[test]
    fn test_resolve_present_array_integers() {
        let mut toml = toml_from_str("example = [ 1, 1337 ]").unwrap();
        let result = do_resolve!(toml => "example");

        assert!(result.is_ok());
        let result = result.unwrap();

        assert!(is_match!(result, &mut Value::Array(_)));
        match result {
            &mut Value::Array(ref ary) => {
                assert_eq!(ary[0], Value::Integer(1));
                assert_eq!(ary[1], Value::Integer(1337));
            },
            _ => panic!("What just happened?"),
        }
    }

    #[test]
    fn test_resolve_present_array_floats() {
        let mut toml = toml_from_str("example = [ 1.0, 133.25 ]").unwrap();
        let result = do_resolve!(toml => "example");

        assert!(result.is_ok());
        let result = result.unwrap();

        assert!(is_match!(result, &mut Value::Array(_)));
        match result {
            &mut Value::Array(ref ary) => {
                assert_eq!(ary[0], Value::Float(1.0));
                assert_eq!(ary[1], Value::Float(133.25));
            },
            _ => panic!("What just happened?"),
        }
    }

    #[test]
    fn test_resolve_array_index_query_1() {
        let mut toml = toml_from_str("example = [ 1 ]").unwrap();
        let result = do_resolve!(toml => "example.[0]");

        assert!(result.is_ok());
        let result = result.unwrap();

        assert!(is_match!(result, &mut Value::Integer(1)));
    }

    #[test]
    fn test_resolve_array_index_query_2() {
        let mut toml = toml_from_str("example = [ 1, 2, 3, 4, 5 ]").unwrap();
        let result = do_resolve!(toml => "example.[4]");

        assert!(result.is_ok());
        let result = result.unwrap();

        assert!(is_match!(result, &mut Value::Integer(5)));
    }

    #[test]
    fn test_resolve_table_element_query() {
        let mut toml = toml_from_str(r#"
        [table]
        value = 42
        "#).unwrap();
        let result = do_resolve!(toml => "table.value");

        assert!(result.is_ok());
        let result = result.unwrap();

        assert!(is_match!(result, &mut Value::Integer(42)));
    }

    #[test]
    fn test_resolve_table_with_many_elements_element_query() {
        let mut toml = toml_from_str(r#"
        [table]
        value1 = 42
        value2 = 43
        value3 = 44
        value4 = 45
        value5 = 46
        "#).unwrap();
        let result = do_resolve!(toml => "table.value1");

        assert!(result.is_ok());
        let result = result.unwrap();

        assert!(is_match!(result, &mut Value::Integer(42)));
    }

    #[test]
    fn test_resolve_table_array_query() {
        let mut toml = toml_from_str(r#"
        [table]
        value1 = [ 42.0, 50.0 ]
        "#).unwrap();
        let result = do_resolve!(toml => "table.value1");

        assert!(result.is_ok());
        let result = result.unwrap();

        assert!(is_match!(result, &mut Value::Array(_)));
        match result {
            &mut Value::Array(ref ary) => {
                assert_eq!(ary[0], Value::Float(42.0));
                assert_eq!(ary[1], Value::Float(50.0));
            },
            _ => panic!("What just happened?"),
        }
    }

    #[test]
    fn test_resolve_table_array_element_query() {
        let mut toml = toml_from_str(r#"
        [table]
        value1 = [ 42 ]
        "#).unwrap();
        let result = do_resolve!(toml => "table.value1.[0]");

        assert!(result.is_ok());
        let result = result.unwrap();

        assert!(is_match!(result, &mut Value::Integer(42)));
    }

    #[test]
    fn test_resolve_multi_table_query() {
        let mut toml = toml_from_str(r#"
        [table0]
        value = [ 1 ]
        [table1]
        value = [ "Foo" ]
        [table2]
        value = [ 42.0 ]
        [table3]
        value = [ true ]
        "#).unwrap();
        let result = do_resolve!(toml => "table1.value.[0]");

        assert!(result.is_ok());
        let result = result.unwrap();

        assert!(is_match!(result, &mut Value::String(_)));
        match result {
            &mut Value::String(ref s) => assert_eq!("Foo", s),
            _ => panic!("What just happened?"),
        }
    }

    static FRUIT_TABLE : &'static str = r#"
    [[fruit.blah]]
      name = "apple"

      [fruit.blah.physical]
        color = "red"
        shape = "round"

    [[fruit.blah]]
      name = "banana"

      [fruit.blah.physical]
        color = "yellow"
        shape = "bent"
    "#;

    #[test]
    fn test_resolve_array_table_query_1() {
        let mut toml = toml_from_str(FRUIT_TABLE).unwrap();
        let result = do_resolve!(toml => "fruit.blah.[0].name");

        assert!(result.is_ok());
        let result = result.unwrap();

        assert!(is_match!(result, &mut Value::String(_)));
        match result {
            &mut Value::String(ref s) => assert_eq!("apple", s),
            _ => panic!("What just happened?"),
        }
    }

    #[test]
    fn test_resolve_array_table_query_2() {
        let mut toml = toml_from_str(FRUIT_TABLE).unwrap();
        let result = do_resolve!(toml => "fruit.blah.[0].physical");

        assert!(result.is_ok());
        let result = result.unwrap();

        assert!(is_match!(result, &mut Value::Table(_)));
        match result {
            &mut Value::Table(ref tab) => {
                match tab.get("color") {
                    Some(&Value::String(ref s)) => assert_eq!("red", s),
                    _ => assert!(false),
                }
                match tab.get("shape") {
                    Some(&Value::String(ref s)) => assert_eq!("round", s),
                    _ => assert!(false),
                }
            },
            _ => panic!("What just happened?"),
        }
    }

    #[test]
    fn test_resolve_query_on_result() {
        let mut toml = toml_from_str(FRUIT_TABLE).unwrap();
        let result = do_resolve!(toml => "fruit.blah.[1].physical");

        assert!(result.is_ok());
        let mut result = result.unwrap();

        let tokens = tokenize_with_seperator(&String::from("color"), '.').unwrap();
        let result = resolve(result, &tokens);

        assert!(result.is_ok());
        let result = result.unwrap();

        assert!(is_match!(result, &mut Value::String(_)));
        match result {
            &mut Value::String(ref s) => assert_eq!("yellow", s),
            _ => panic!("What just happened?"),
        }
    }

    #[test]
    fn test_resolve_query_empty_table() {
        let mut toml = toml_from_str(r#"
        [example]
        "#).unwrap();
        let result = do_resolve!(toml => "example");

        assert!(result.is_ok());
        let result = result.unwrap();

        assert!(is_match!(result, &mut Value::Table(_)));
        match result {
            &mut Value::Table(ref t) => assert!(t.is_empty()),
            _ => panic!("What just happened?"),
        }
    }

    #[test]
    fn test_resolve_query_member_of_empty_table() {
        let mut toml = toml_from_str(r#"
        [example]
        "#).unwrap();
        let result = do_resolve!(toml => "example.foo");

        assert!(result.is_err());
        let result = result.unwrap_err();

        let errkind = result.kind();
        assert!(is_match!(errkind, &ErrorKind::IdentifierNotFoundInDocument { .. }));
    }

    #[test]
    fn test_resolve_query_index_in_table() {
        let mut toml = toml_from_str(r#"
        [example]
        "#).unwrap();
        let result = do_resolve!(toml => "example.[0]");

        assert!(result.is_err());
        let result = result.unwrap_err();

        let errkind = result.kind();
        assert!(is_match!(errkind, &ErrorKind::NoIndexInTable { .. }));
    }

    #[test]
    fn test_resolve_query_identifier_in_array() {
        let mut toml = toml_from_str(r#"
        [example]
        foo = [ 1, 2, 3 ]
        "#).unwrap();
        let result = do_resolve!(toml => "example.foo.bar");

        assert!(result.is_err());
        let result = result.unwrap_err();

        let errkind = result.kind();
        assert!(is_match!(errkind, &ErrorKind::NoIdentifierInArray { .. }));
    }

    #[test]
    fn test_resolve_query_value_as_table() {
        let mut toml = toml_from_str(r#"
        [example]
        foo = 1
        "#).unwrap();
        let result = do_resolve!(toml => "example.foo.bar");

        assert!(result.is_err());
        let result = result.unwrap_err();

        let errkind = result.kind();
        assert!(is_match!(errkind, &ErrorKind::QueryingValueAsTable { .. }));
    }

    #[test]
    fn test_resolve_query_value_as_array() {
        let mut toml = toml_from_str(r#"
        [example]
        foo = 1
        "#).unwrap();
        let result = do_resolve!(toml => "example.foo.[0]");

        assert!(result.is_err());
        let result = result.unwrap_err();

        let errkind = result.kind();
        assert!(is_match!(errkind, &ErrorKind::QueryingValueAsArray { .. }));
    }

}
<|endoftext|>


<commit_before>//! Iron's HTTP Response representation and associated methods.

use std::io::{self, Write};
use std::fmt::{self, Debug};
use std::fs::File;

use typemap::TypeMap;
use plugin::Extensible;
use modifier::{Set, Modifier};
use hyper::header::Headers;

use status::{self, Status};
use {Plugin, headers};

pub use hyper::server::response::Response as HttpResponse;
use hyper::net::Fresh;

/// A `Write`r of HTTP response bodies.
pub struct ResponseBody<'a>(Box<Write + 'a>);

impl<'a> ResponseBody<'a> {
    /// Create a new ResponseBody, mostly for use in mocking.
    pub fn new<W: Write + 'a>(writer: W) -> ResponseBody<'a> {
        ResponseBody(Box::new(writer))
    }
}

impl<'a> Write for ResponseBody<'a> {
    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
        self.0.write(buf)
    }

    fn flush(&mut self) -> io::Result<()> {
        self.0.flush()
    }
}

/// A trait which writes the body of an HTTP response.
pub trait WriteBody {
    /// Writes the body to the provided `ResponseBody`.
    fn write_body(&mut self, res: &mut ResponseBody) -> io::Result<()>;
}

impl WriteBody for String {
    fn write_body(&mut self, res: &mut ResponseBody) -> io::Result<()> {
        self.as_bytes().write_body(res)
    }
}

impl<'a> WriteBody for &'a str {
    fn write_body(&mut self, res: &mut ResponseBody) -> io::Result<()> {
        self.as_bytes().write_body(res)
    }
}

impl WriteBody for Vec<u8> {
    fn write_body(&mut self, res: &mut ResponseBody) -> io::Result<()> {
        res.write_all(self)
    }
}

impl<'a> WriteBody for &'a [u8] {
    fn write_body(&mut self, res: &mut ResponseBody) -> io::Result<()> {
        res.write_all(self)
    }
}

impl WriteBody for File {
    fn write_body(&mut self, res: &mut ResponseBody) -> io::Result<()> {
        io::copy(self, res).map(|_| ())
    }
}

/* Needs specialization :(
impl<R: Read> WriteBody for R {
    fn write_body(&mut self, res: &mut ResponseBody) -> io::Result<()> {
        io::copy(self, res)
    }
}
*/

/// The response representation given to `Middleware`
pub struct Response {
    /// The response status-code.
    pub status: Option<Status>,

    /// The headers of the response.
    pub headers: Headers,

    /// A TypeMap to be used as an extensible storage for data
    /// associated with this Response.
    pub extensions: TypeMap,

    /// The body of the response.
    ///
    /// This is a Reader for generality, most data should
    /// be sent using either `serve` or `serve_file`.
    ///
    /// Arbitrary Readers can be sent by assigning to body.
    pub body: Option<Box<WriteBody + Send>>
}

impl Response {
    /// Construct a blank Response
    pub fn new() -> Response {
        Response {
            status: None, // Start with no response code.
            body: None, // Start with no body.
            headers: Headers::new(),
            extensions: TypeMap::new()
        }
    }

    /// Construct a Response with the specified modifier pre-applied.
    pub fn with<M: Modifier<Response>>(m: M) -> Response {
        Response::new().set(m)
    }

    // `write_back` is used to put all the data added to `self`
    // back onto an `HttpResponse` so that it is sent back to the
    // client.
    //
    // `write_back` consumes the `Response`.
    #[doc(hidden)]
    pub fn write_back(self, mut http_res: HttpResponse<Fresh>) {
        *http_res.headers_mut() = self.headers;

        // Default to a 404 if no response code was set
        *http_res.status_mut() = self.status.clone().unwrap_or(status::NotFound);

        let out = match self.body {
            Some(body) => write_with_body(http_res, body),
            None => {
                http_res.headers_mut().set(headers::ContentLength(0));
                http_res.start().and_then(|res| res.end())
            }
        };

        match out {
            Err(e) => {
                error!("Error writing response: {}", e);
            },
            _ => {}
        }
    }
}

fn write_with_body(mut res: HttpResponse<Fresh>, mut body: Box<WriteBody + Send>)
                   -> io::Result<()> {
    let content_type = res.headers().get::<headers::ContentType>()
                           .map(|cx| cx.clone())
                           .unwrap_or_else(|| headers::ContentType("text/plain".parse().unwrap()));
    res.headers_mut().set(content_type);

    let mut raw_res = try!(res.start());
    try!(body.write_body(&mut ResponseBody::new(&mut raw_res)));
    raw_res.end()
}

impl Debug for Response {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        writeln!(f, "HTTP/1.1 {} {}\n{}",
            self.status.unwrap_or(status::NotFound),
            self.status.unwrap_or(status::NotFound).canonical_reason().unwrap(),
            self.headers
        )
    }
}

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

// Allow plugins to attach to responses.
impl Extensible for Response {
    fn extensions(&self) -> &TypeMap {
        &self.extensions
    }

    fn extensions_mut(&mut self) -> &mut TypeMap {
        &mut self.extensions
    }
}

impl Plugin for Response {}
impl Set for Response {}

<commit_msg>Add WriteBody impl for Box<Read><commit_after>//! Iron's HTTP Response representation and associated methods.

use std::io::{self, Write};
use std::fmt::{self, Debug};
use std::fs::File;

use typemap::TypeMap;
use plugin::Extensible;
use modifier::{Set, Modifier};
use hyper::header::Headers;

use status::{self, Status};
use {Plugin, headers};

pub use hyper::server::response::Response as HttpResponse;
use hyper::net::Fresh;

/// A `Write`r of HTTP response bodies.
pub struct ResponseBody<'a>(Box<Write + 'a>);

impl<'a> ResponseBody<'a> {
    /// Create a new ResponseBody, mostly for use in mocking.
    pub fn new<W: Write + 'a>(writer: W) -> ResponseBody<'a> {
        ResponseBody(Box::new(writer))
    }
}

impl<'a> Write for ResponseBody<'a> {
    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
        self.0.write(buf)
    }

    fn flush(&mut self) -> io::Result<()> {
        self.0.flush()
    }
}

/// A trait which writes the body of an HTTP response.
pub trait WriteBody {
    /// Writes the body to the provided `ResponseBody`.
    fn write_body(&mut self, res: &mut ResponseBody) -> io::Result<()>;
}

impl WriteBody for String {
    fn write_body(&mut self, res: &mut ResponseBody) -> io::Result<()> {
        self.as_bytes().write_body(res)
    }
}

impl<'a> WriteBody for &'a str {
    fn write_body(&mut self, res: &mut ResponseBody) -> io::Result<()> {
        self.as_bytes().write_body(res)
    }
}

impl WriteBody for Vec<u8> {
    fn write_body(&mut self, res: &mut ResponseBody) -> io::Result<()> {
        res.write_all(self)
    }
}

impl<'a> WriteBody for &'a [u8] {
    fn write_body(&mut self, res: &mut ResponseBody) -> io::Result<()> {
        res.write_all(self)
    }
}

impl WriteBody for File {
    fn write_body(&mut self, res: &mut ResponseBody) -> io::Result<()> {
        io::copy(self, res).map(|_| ())
    }
}

impl WriteBody for Box<io::Read> {
    fn write_body(&mut self, res: &mut ResponseBody) -> io::Result<()> {
        io::copy(self, res).map(|_| ())
    }
}

/* Needs specialization :(
impl<R: Read> WriteBody for R {
    fn write_body(&mut self, res: &mut ResponseBody) -> io::Result<()> {
        io::copy(self, res)
    }
}
*/

/// The response representation given to `Middleware`
pub struct Response {
    /// The response status-code.
    pub status: Option<Status>,

    /// The headers of the response.
    pub headers: Headers,

    /// A TypeMap to be used as an extensible storage for data
    /// associated with this Response.
    pub extensions: TypeMap,

    /// The body of the response.
    ///
    /// This is a Reader for generality, most data should
    /// be sent using either `serve` or `serve_file`.
    ///
    /// Arbitrary Readers can be sent by assigning to body.
    pub body: Option<Box<WriteBody + Send>>
}

impl Response {
    /// Construct a blank Response
    pub fn new() -> Response {
        Response {
            status: None, // Start with no response code.
            body: None, // Start with no body.
            headers: Headers::new(),
            extensions: TypeMap::new()
        }
    }

    /// Construct a Response with the specified modifier pre-applied.
    pub fn with<M: Modifier<Response>>(m: M) -> Response {
        Response::new().set(m)
    }

    // `write_back` is used to put all the data added to `self`
    // back onto an `HttpResponse` so that it is sent back to the
    // client.
    //
    // `write_back` consumes the `Response`.
    #[doc(hidden)]
    pub fn write_back(self, mut http_res: HttpResponse<Fresh>) {
        *http_res.headers_mut() = self.headers;

        // Default to a 404 if no response code was set
        *http_res.status_mut() = self.status.clone().unwrap_or(status::NotFound);

        let out = match self.body {
            Some(body) => write_with_body(http_res, body),
            None => {
                http_res.headers_mut().set(headers::ContentLength(0));
                http_res.start().and_then(|res| res.end())
            }
        };

        match out {
            Err(e) => {
                error!("Error writing response: {}", e);
            },
            _ => {}
        }
    }
}

fn write_with_body(mut res: HttpResponse<Fresh>, mut body: Box<WriteBody + Send>)
                   -> io::Result<()> {
    let content_type = res.headers().get::<headers::ContentType>()
                           .map(|cx| cx.clone())
                           .unwrap_or_else(|| headers::ContentType("text/plain".parse().unwrap()));
    res.headers_mut().set(content_type);

    let mut raw_res = try!(res.start());
    try!(body.write_body(&mut ResponseBody::new(&mut raw_res)));
    raw_res.end()
}

impl Debug for Response {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        writeln!(f, "HTTP/1.1 {} {}\n{}",
            self.status.unwrap_or(status::NotFound),
            self.status.unwrap_or(status::NotFound).canonical_reason().unwrap(),
            self.headers
        )
    }
}

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

// Allow plugins to attach to responses.
impl Extensible for Response {
    fn extensions(&self) -> &TypeMap {
        &self.extensions
    }

    fn extensions_mut(&mut self) -> &mut TypeMap {
        &mut self.extensions
    }
}

impl Plugin for Response {}
impl Set for Response {}

<|endoftext|>


<commit_before>//! Iron's HTTP Response representation and associated methods.

use std::io::{IoResult, File, MemReader};
use std::path::BytesContainer;

use http::status::{Status, InternalServerError, NotFound};
use OkStatus = http::status::Ok;
use http::headers::response::HeaderCollection;
use http::headers::content_type::MediaType;

pub use HttpResponse = http::server::response::ResponseWriter;

use contenttype::get_content_type;

/// The response representation given to `Middleware`
pub struct Response<'a, 'b> {
    http_res: &'a mut HttpResponse<'b>,

    /// The body of the response.
    ///
    /// This is a Reader for generality, most data should
    /// be sent using either `serve` or `serve_file`.
    ///
    /// Arbitrary Readers can be sent by assigning to body.
    pub body: Box<Reader>,

    /// The headers of the response.
    pub headers: Box<HeaderCollection>,

    /// The response status-code.
    pub status: Option<Status>
}

impl<'a, 'b> Response<'a, 'b> {
    /// Construct a Response from an HttpResponse reference
    pub fn from_http(http_res: &'a mut HttpResponse<'b>) -> Response<'a, 'b> {
        Response {
            headers: http_res.headers.clone(),
            status: None, // Start with no response code.
            http_res: http_res,
            body: box MemReader::new(vec![]) as Box<Reader>
        }
    }

    /// Write the `Status` and data to the `Response`.
    pub fn serve<S: BytesContainer>(&mut self, status: Status, body: S) {
        self.status = Some(status);
        self.body = box MemReader::new(body.container_as_bytes().to_vec()) as Box<Reader>;
    }

    /// Serve the file located at `path`.
    ///
    /// This usually means a request has been handled, and `Middleware`
    /// may want to `Unwind` after a file is served. If the status should be
    /// anything other than `200`, `Middleware` must set it, including in
    /// the case of an `Err`.
    ///
    /// `serve_file` will error if the file does not exist, the process
    /// does not have correct permissions, or it has other issues in reading
    /// from the file. `Middleware` should handle this gracefully.
    pub fn serve_file(&mut self, path: &Path) -> IoResult<()> {
        let file = try!(File::open(path));
        self.headers.content_type = path.extension_str().and_then(get_content_type);
        self.body = box file as Box<Reader>;
        self.status = Some(OkStatus);
        Ok(())
    }

    // `write_back` is used to put all the data added to `self`
    // back onto an `HttpResponse` so that it is sent back to the
    // client.
    //
    // `write_back` consumes the `Response`.
    #[doc(hidden)]
    pub fn write_back(mut self) {
        self.http_res.headers = self.headers.clone();

        // Default to a 404 if no response code was set
        self.http_res.status = self.status.clone().unwrap_or(NotFound);

        // Read the body into the http_res body
        let _ = match self.body.read_to_string() {
            Ok(body) => {
                let plain_txt: MediaType = get_content_type("txt").unwrap();
                self.http_res.write_content_auto(
                    self.headers.content_type.clone().unwrap_or(plain_txt),
                    body
                )
            },
            Err(e) => Err(e)
        // Catch errors from reading + writing
        }.map_err(|e| {
            error!("Error reading/writing body: {}", e);
            self.http_res.status = InternalServerError;
            let _ = self.http_res.write(b"Internal Server Error")
                .map_err(|e| error!("Error writing error message: {}", e));
        });
    }
}

#[test]
fn matches_content_type () {
    let path = &Path::new("test.txt");
    let content_type = path.extension_str().and_then(get_content_type).unwrap();

    assert_eq!(content_type.type_.as_slice(), "text");
    assert_eq!(content_type.subtype.as_slice(), "plain");
}
<commit_msg>(fix) Read to a Vec<u8> instead of a String.<commit_after>//! Iron's HTTP Response representation and associated methods.

use std::io::{IoResult, File, MemReader};
use std::path::BytesContainer;

use http::status::{Status, InternalServerError, NotFound};
use OkStatus = http::status::Ok;
use http::headers::response::HeaderCollection;
use http::headers::content_type::MediaType;

pub use HttpResponse = http::server::response::ResponseWriter;

use contenttype::get_content_type;

/// The response representation given to `Middleware`
pub struct Response<'a, 'b> {
    http_res: &'a mut HttpResponse<'b>,

    /// The body of the response.
    ///
    /// This is a Reader for generality, most data should
    /// be sent using either `serve` or `serve_file`.
    ///
    /// Arbitrary Readers can be sent by assigning to body.
    pub body: Box<Reader>,

    /// The headers of the response.
    pub headers: Box<HeaderCollection>,

    /// The response status-code.
    pub status: Option<Status>
}

impl<'a, 'b> Response<'a, 'b> {
    /// Construct a Response from an HttpResponse reference
    pub fn from_http(http_res: &'a mut HttpResponse<'b>) -> Response<'a, 'b> {
        Response {
            headers: http_res.headers.clone(),
            status: None, // Start with no response code.
            http_res: http_res,
            body: box MemReader::new(vec![]) as Box<Reader>
        }
    }

    /// Write the `Status` and data to the `Response`.
    pub fn serve<S: BytesContainer>(&mut self, status: Status, body: S) {
        self.status = Some(status);
        self.body = box MemReader::new(body.container_as_bytes().to_vec()) as Box<Reader>;
    }

    /// Serve the file located at `path`.
    ///
    /// This usually means a request has been handled, and `Middleware`
    /// may want to `Unwind` after a file is served. If the status should be
    /// anything other than `200`, `Middleware` must set it, including in
    /// the case of an `Err`.
    ///
    /// `serve_file` will error if the file does not exist, the process
    /// does not have correct permissions, or it has other issues in reading
    /// from the file. `Middleware` should handle this gracefully.
    pub fn serve_file(&mut self, path: &Path) -> IoResult<()> {
        let file = try!(File::open(path));
        self.headers.content_type = path.extension_str().and_then(get_content_type);
        self.body = box file as Box<Reader>;
        self.status = Some(OkStatus);
        Ok(())
    }

    // `write_back` is used to put all the data added to `self`
    // back onto an `HttpResponse` so that it is sent back to the
    // client.
    //
    // `write_back` consumes the `Response`.
    #[doc(hidden)]
    pub fn write_back(mut self) {
        self.http_res.headers = self.headers.clone();

        // Default to a 404 if no response code was set
        self.http_res.status = self.status.clone().unwrap_or(NotFound);

        // Read the body into the http_res body
        let _ = match self.body.read_to_end() {
            Ok(body) => {
                let plain_txt: MediaType = get_content_type("txt").unwrap();

                // Set content length and type
                self.http_res.headers.content_length =
                    Some(body.len());
                self.http_res.headers.content_type =
                    Some(self.http_res.headers.content_type.clone().unwrap_or(plain_txt));

                // Write the body
                self.http_res.write(body.as_slice())
            },
            Err(e) => Err(e)
        // Catch errors from reading + writing
        }.map_err(|e| {
            error!("Error reading/writing body: {}", e);
            self.http_res.status = InternalServerError;
            let _ = self.http_res.write(b"Internal Server Error")
                .map_err(|e| error!("Error writing error message: {}", e));
        });
    }
}

#[test]
fn matches_content_type () {
    let path = &Path::new("test.txt");
    let content_type = path.extension_str().and_then(get_content_type).unwrap();

    assert_eq!(content_type.type_.as_slice(), "text");
    assert_eq!(content_type.subtype.as_slice(), "plain");
}
<|endoftext|>


<commit_before>use Error;
use Year;

use reqwest;

#[derive(Debug, Deserialize)]
pub struct SemesterPlan {
    pub year: Year,
    #[serde(rename = "type")]
    pub semester_type: String,
    pub period: Period,
    #[serde(rename = "freeDays")]
    pub holidays: Vec<Holiday>,
    #[serde(rename = "lecturePeriod")]
    pub lecture_period: Period,
    #[serde(rename = "examsPeriod")]
    pub exam_period: Period,
    #[serde(rename = "reregistration")]
    pub reregistration_period: Period,
}

#[derive(Debug, Deserialize)]
pub struct Period {
    #[serde(rename = "beginDay")]
    begin_day: String,
    #[serde(rename = "endDay")]
    end_day: String,
}

#[derive(Debug, Deserialize)]
pub struct Holiday {
    name: String,
    #[serde(rename = "beginDay")]
    begin_day: String,
    #[serde(rename = "endDay")]
    end_day: String,
}

const BASE_URL: &'static str = "https://www2.htw-dresden.de/~app/API/semesterplan.json";

impl SemesterPlan {
    /// Returns all currently available `SemesterPlan`s.
    ///
    /// # Example
    ///
    /// ```
    /// use htwdresden::SemesterPlan;
    ///
    /// fn main() {
    ///     let semesters = SemesterPlan::get();
    /// }
    /// ```
    pub fn get() -> Result<Vec<SemesterPlan>, Error> {
        let client = reqwest::Client::new().expect("failed to create new reqwest client o.O");
        // Working around a possible bug in reqwest/hyper on macOS, see https://github.com/seanmonstar/reqwest/issues/26
        let mut headers = reqwest::header::Headers::new();
        headers.set(reqwest::header::Connection::close());

        let semesters = client.get(BASE_URL)
            .headers(headers)
            .send()?
            .json()
            .map(|response: Vec<SemesterPlan>| response)?;
        Ok(semesters)
    }
}
<commit_msg>make Holiday and Period fields public (oops)<commit_after>use Error;
use Year;

use reqwest;

#[derive(Debug, Deserialize)]
pub struct SemesterPlan {
    pub year: Year,
    #[serde(rename = "type")]
    pub semester_type: String,
    pub period: Period,
    #[serde(rename = "freeDays")]
    pub holidays: Vec<Holiday>,
    #[serde(rename = "lecturePeriod")]
    pub lecture_period: Period,
    #[serde(rename = "examsPeriod")]
    pub exam_period: Period,
    #[serde(rename = "reregistration")]
    pub reregistration_period: Period,
}

#[derive(Debug, Deserialize)]
pub struct Period {
    #[serde(rename = "beginDay")]
    pub begin_day: String,
    #[serde(rename = "endDay")]
    pub end_day: String,
}

#[derive(Debug, Deserialize)]
pub struct Holiday {
    name: String,
    #[serde(rename = "beginDay")]
    pub begin_day: String,
    #[serde(rename = "endDay")]
    pub end_day: String,
}

const BASE_URL: &'static str = "https://www2.htw-dresden.de/~app/API/semesterplan.json";

impl SemesterPlan {
    /// Returns all currently available `SemesterPlan`s.
    ///
    /// # Example
    ///
    /// ```
    /// use htwdresden::SemesterPlan;
    ///
    /// fn main() {
    ///     let semesters = SemesterPlan::get();
    /// }
    /// ```
    pub fn get() -> Result<Vec<SemesterPlan>, Error> {
        let client = reqwest::Client::new().expect("failed to create new reqwest client o.O");
        // Working around a possible bug in reqwest/hyper on macOS, see https://github.com/seanmonstar/reqwest/issues/26
        let mut headers = reqwest::header::Headers::new();
        headers.set(reqwest::header::Connection::close());

        let semesters = client.get(BASE_URL)
            .headers(headers)
            .send()?
            .json()
            .map(|response: Vec<SemesterPlan>| response)?;
        Ok(semesters)
    }
}
<|endoftext|>