<commit_before>use hyper::client::{Client, HttpConnector};
use hyper;
use tokio_core::reactor::{Core, Handle, Timeout};
use futures::future::{Future, result};
use futures::Stream;
use serde_json;
use serde::de::{Deserialize, Deserializer};

use std::cell::RefCell;
use std::str;
use std::time::Duration;

use exchange::{Currency, Exchange, Rates};
use future::FixerioFuture;
use errors::{Error, ErrorKind};

/// Configuration for requests.
pub struct Config {
    base: Currency,
    symbols: Option<Vec<Currency>>,
    date: Option<String>,
}

impl Config {
    /// Create a new Config struct.
    pub fn new(curr: Currency) -> Self {
        Self {
            base: curr,
            symbols: None,
            date: None,
        }
    }

    /// Set the symbols (List of specific exchange rates for the base currency, will retrieve all if none as specified).
    pub fn set_symbols(&mut self, symbols: Vec<Currency>) {
        self.symbols = Some(symbols);
    }

    /// Set the date (The date for which to retrieve exchange rates from, will retrieve latest if a date is not specified).
    pub fn set_date<T: Into<String>>(&mut self, date: T) {
        self.date = Some(date.into());
    }
}

fn get_url(conf: &Config) -> hyper::Uri {
    let mut url = String::from("http://api.fixer.io/");

    url.push_str(conf.date.as_ref().map(String::as_str).unwrap_or("latest"));
    url.push_str("?base=");
    url.push_str(conf.base.string());

    if let Some(ref currencies) = conf.symbols {
        let string_vec: Vec<&str> = currencies.iter().map(|x| x.string()).collect();
        let sep_string = string_vec.join(",");

        url.push_str("&symbols=");
        url.push_str(&sep_string);
    }

    url.parse::<hyper::Uri>().unwrap()
}

/// Asynchronous API for sending requests
pub struct AsyncApi {
    client: Client<HttpConnector>,
    handle: Handle,
}

impl AsyncApi {
    /// Create a new instance.
    pub fn new(handle: &Handle) -> Self {
        Self {
            client: Client::new(&handle),
            handle: handle.clone(),
        }
    }

    /// Perform request with timeout.
    ///
    /// # Examples
    ///
    /// ```rust,no_run
    ///
    /// # extern crate fixerio;
    /// # extern crate tokio_core;
    /// use fixerio::{Config, Currency, AsyncApi};
    /// use tokio_core::reactor::Core;
    /// use std::time::Duration;
    ///
    /// fn main() {
    ///     let mut core = Core::new().expect("Error creating core");
    ///     let handle = core.handle();
    ///
    ///     let api = AsyncApi::new(&handle);
    ///
    ///     let config = Config::new(Currency::USD);
    ///
    ///     let work = api.get_timeout(&config, Duration::from_secs(5));
    ///
    ///     let rates = core.run(work).expect("Error retrieving rates");
    ///
    ///     match rates {
    ///         Some(x) => println!("{:?}", x),
    ///         _ => println!("Request timed out")
    ///     };
    /// }
    /// ```
    pub fn get_timeout(&self,
                       conf: &Config,
                       duration: Duration)
                       -> FixerioFuture<Option<Exchange>> {
        let timeout_future = result(Timeout::new(duration, &self.handle))
            .flatten()
            .map_err(From::from)
            .map(|_| None);

        let get = self.get(conf).map(|x| Some(x));

        let future = timeout_future
            .select(get)
            .map(|(item, _next)| item)
            .map_err(|(item, _next)| item);

        FixerioFuture::new(Box::new(future))
    }

    /// Perform request.
    ///
    /// # Examples
    ///
    /// ```rust,no_run
    ///
    /// # extern crate fixerio;
    /// # extern crate tokio_core;
    /// use fixerio::{Config, Currency, AsyncApi};
    /// use tokio_core::reactor::Core;
    /// use std::time::Duration;
    ///
    /// fn main() {
    ///     let mut core = Core::new().expect("Error creating core");
    ///     let handle = core.handle();
    ///
    ///     // Get the latest exchange rates for USD
    ///     let api = AsyncApi::new(&handle);
    ///
    ///     let config = Config::new(Currency::USD);
    ///     let work = api.get(&config);
    ///
    ///     println!("{:?}", core.run(work).expect("Error retrieving rates"));
    /// }
    /// ```
    pub fn get(&self, conf: &Config) -> FixerioFuture<Exchange> {
        let url = get_url(conf);

        let response = self.client
            .get(url)
            .and_then(|res| res.body().concat2())
            .map_err(From::from);

        let future = response.and_then(|bytes| {
            result(serde_json::from_slice(&bytes)
                       .map_err(From::from)
                       .and_then(|value| match value {
                                     Response::Success(exchange) => Ok(exchange),
                                     Response::Error(err) => {
                                         Err(ErrorKind::FixerioError { description: err }.into())
                                     }
                                 }))
        });

        FixerioFuture::new(Box::new(future))
    }
}

/// Synchronous API for sending requests.
pub struct SyncApi {
    core: RefCell<Core>,
    api: AsyncApi,
}

impl SyncApi {
    /// Create a new instance.
    pub fn new() -> Result<Self, Error> {
        let core = Core::new()?;
        let handle = core.handle();

        Ok(Self {
               core: RefCell::new(core),
               api: AsyncApi::new(&handle),
           })
    }

    /// Perform request.
    ///
    /// # Examples
    ///
    /// ```rust,no_run
    ///
    /// # extern crate fixerio;
    /// use fixerio::{Config, Currency, SyncApi};
    /// use std::time::Duration;
    ///
    /// fn main() {
    ///     let api = SyncApi::new().expect("Error creating API");
    ///
    ///     let config = Config::new(Currency::USD);
    ///
    ///     let rates = api.get(&config).expect("Error retrieving rates");
    ///
    ///     println!("{:?}", rates);
    /// }
    /// ```
    pub fn get(&self, conf: &Config) -> Result<Exchange, Error> {
        let future = self.api.get(conf);

        self.core.borrow_mut().run(future)
    }

    /// Perform request with timeout.
    ///
    /// # Examples
    ///
    /// ```rust,no_run
    ///
    /// # extern crate fixerio;
    /// use fixerio::{Config, Currency, SyncApi};
    /// use std::time::Duration;
    ///
    /// fn main() {
    ///     let api = SyncApi::new().expect("Error creating API");
    ///
    ///     let config = Config::new(Currency::USD);
    ///
    ///     let rates = api.get_timeout(&config, Duration::from_secs(5)).expect("Error retrieving rates");
    ///
    ///     match rates {
    ///         Some(x) => println!("{:?}", x),
    ///         _ => println!("Request timed out")
    ///     };
    /// }
    /// ```
    pub fn get_timeout(&self,
                       conf: &Config,
                       duration: Duration)
                       -> Result<Option<Exchange>, Error> {
        let future = self.api.get_timeout(conf, duration);

        self.core.borrow_mut().run(future)
    }
}

#[derive(Deserialize)]
struct RawResponse {
    error: Option<String>,
    base: Option<String>,
    date: Option<String>,
    rates: Option<Rates>,
}

enum Response {
    Error(String),
    Success(Exchange),
}

impl<'de> Deserialize<'de> for Response {
    fn deserialize<D>(deserializer: D) -> Result<Response, D::Error>
        where D: Deserializer<'de>
    {
        let raw: RawResponse = Deserialize::deserialize(deserializer)?;

        if let Some(err) = raw.error {
            Ok(Response::Error(err))
        } else {
            Ok(Response::Success(Exchange {
                                     base: raw.base.unwrap(),
                                     date: raw.date.unwrap(),
                                     rates: raw.rates.unwrap(),
                                 }))
        }
    }
}

#[cfg(test)]
mod tests {
    use client::{Config, get_url};
    use exchange::Currency;

    #[test]
    fn get_url_test() {
        let tests = vec![("http://api.fixer.io/latest?base=AUD",
                          Config {
                              base: Currency::AUD,
                              symbols: None,
                              date: None,
                          }),
                         ("http://api.fixer.io/latest?base=EUR",
                          Config {
                              base: Currency::EUR,
                              symbols: None,
                              date: None,
                          }),
                         ("http://api.fixer.io/2000-01-03?base=EUR",
                          Config {
                              base: Currency::EUR,
                              symbols: None,
                              date: Some(String::from("2000-01-03")),
                          }),
                         ("http://api.fixer.io/latest?base=EUR&symbols=AUD",
                          Config {
                              base: Currency::EUR,
                              symbols: Some(vec![Currency::AUD]),
                              date: None,
                          }),
                         ("http://api.fixer.io/latest?base=EUR&symbols=AUD,USD",
                          Config {
                              base: Currency::EUR,
                              symbols: Some(vec![Currency::AUD, Currency::USD]),
                              date: None,
                          })];

        for test in tests {
            assert_eq!(test.0, get_url(&test.1));
        }
    }
}<commit_msg>Amended code recommended by clippy.<commit_after>use hyper::client::{Client, HttpConnector};
use hyper;
use tokio_core::reactor::{Core, Handle, Timeout};
use futures::future::{Future, result};
use futures::Stream;
use serde_json;
use serde::de::{Deserialize, Deserializer};

use std::cell::RefCell;
use std::str;
use std::time::Duration;

use exchange::{Currency, Exchange, Rates};
use future::FixerioFuture;
use errors::{Error, ErrorKind};

/// Configuration for requests.
pub struct Config {
    base: Currency,
    symbols: Option<Vec<Currency>>,
    date: Option<String>,
}

impl Config {
    /// Create a new Config struct.
    pub fn new(curr: Currency) -> Self {
        Self {
            base: curr,
            symbols: None,
            date: None,
        }
    }

    /// Set the symbols (List of specific exchange rates for the base currency, will retrieve all if none as specified).
    pub fn set_symbols(&mut self, symbols: Vec<Currency>) {
        self.symbols = Some(symbols);
    }

    /// Set the date (The date for which to retrieve exchange rates from, will retrieve latest if a date is not specified).
    pub fn set_date<T: Into<String>>(&mut self, date: T) {
        self.date = Some(date.into());
    }
}

fn get_url(conf: &Config) -> hyper::Uri {
    let mut url = String::from("http://api.fixer.io/");

    url.push_str(conf.date.as_ref().map(String::as_str).unwrap_or("latest"));
    url.push_str("?base=");
    url.push_str(conf.base.string());

    if let Some(ref currencies) = conf.symbols {
        let string_vec: Vec<&str> = currencies.iter().map(|x| x.string()).collect();
        let sep_string = string_vec.join(",");

        url.push_str("&symbols=");
        url.push_str(&sep_string);
    }

    url.parse::<hyper::Uri>().unwrap()
}

/// Asynchronous API for sending requests
pub struct AsyncApi {
    client: Client<HttpConnector>,
    handle: Handle,
}

impl AsyncApi {
    /// Create a new instance.
    pub fn new(handle: &Handle) -> Self {
        Self {
            client: Client::new(handle),
            handle: handle.clone(),
        }
    }

    /// Perform request with timeout.
    ///
    /// # Examples
    ///
    /// ```rust,no_run
    ///
    /// # extern crate fixerio;
    /// # extern crate tokio_core;
    /// use fixerio::{Config, Currency, AsyncApi};
    /// use tokio_core::reactor::Core;
    /// use std::time::Duration;
    ///
    /// fn main() {
    ///     let mut core = Core::new().expect("Error creating core");
    ///     let handle = core.handle();
    ///
    ///     let api = AsyncApi::new(&handle);
    ///
    ///     let config = Config::new(Currency::USD);
    ///
    ///     let work = api.get_timeout(&config, Duration::from_secs(5));
    ///
    ///     let rates = core.run(work).expect("Error retrieving rates");
    ///
    ///     match rates {
    ///         Some(x) => println!("{:?}", x),
    ///         _ => println!("Request timed out")
    ///     };
    /// }
    /// ```
    pub fn get_timeout(&self,
                       conf: &Config,
                       duration: Duration)
                       -> FixerioFuture<Option<Exchange>> {
        let timeout_future = result(Timeout::new(duration, &self.handle))
            .flatten()
            .map_err(From::from)
            .map(|_| None);

        let get = self.get(conf).map(Some);

        let future = timeout_future
            .select(get)
            .map(|(item, _next)| item)
            .map_err(|(item, _next)| item);

        FixerioFuture::new(Box::new(future))
    }

    /// Perform request.
    ///
    /// # Examples
    ///
    /// ```rust,no_run
    ///
    /// # extern crate fixerio;
    /// # extern crate tokio_core;
    /// use fixerio::{Config, Currency, AsyncApi};
    /// use tokio_core::reactor::Core;
    /// use std::time::Duration;
    ///
    /// fn main() {
    ///     let mut core = Core::new().expect("Error creating core");
    ///     let handle = core.handle();
    ///
    ///     // Get the latest exchange rates for USD
    ///     let api = AsyncApi::new(&handle);
    ///
    ///     let config = Config::new(Currency::USD);
    ///     let work = api.get(&config);
    ///
    ///     println!("{:?}", core.run(work).expect("Error retrieving rates"));
    /// }
    /// ```
    pub fn get(&self, conf: &Config) -> FixerioFuture<Exchange> {
        let url = get_url(conf);

        let response = self.client
            .get(url)
            .and_then(|res| res.body().concat2())
            .map_err(From::from);

        let future = response.and_then(|bytes| {
            result(serde_json::from_slice(&bytes)
                       .map_err(From::from)
                       .and_then(|value| match value {
                                     Response::Success(exchange) => Ok(exchange),
                                     Response::Error(err) => {
                                         Err(ErrorKind::FixerioError { description: err }.into())
                                     }
                                 }))
        });

        FixerioFuture::new(Box::new(future))
    }
}

/// Synchronous API for sending requests.
pub struct SyncApi {
    core: RefCell<Core>,
    api: AsyncApi,
}

impl SyncApi {
    /// Create a new instance.
    pub fn new() -> Result<Self, Error> {
        let core = Core::new()?;
        let handle = core.handle();

        Ok(Self {
               core: RefCell::new(core),
               api: AsyncApi::new(&handle),
           })
    }

    /// Perform request.
    ///
    /// # Examples
    ///
    /// ```rust,no_run
    ///
    /// # extern crate fixerio;
    /// use fixerio::{Config, Currency, SyncApi};
    /// use std::time::Duration;
    ///
    /// fn main() {
    ///     let api = SyncApi::new().expect("Error creating API");
    ///
    ///     let config = Config::new(Currency::USD);
    ///
    ///     let rates = api.get(&config).expect("Error retrieving rates");
    ///
    ///     println!("{:?}", rates);
    /// }
    /// ```
    pub fn get(&self, conf: &Config) -> Result<Exchange, Error> {
        let future = self.api.get(conf);

        self.core.borrow_mut().run(future)
    }

    /// Perform request with timeout.
    ///
    /// # Examples
    ///
    /// ```rust,no_run
    ///
    /// # extern crate fixerio;
    /// use fixerio::{Config, Currency, SyncApi};
    /// use std::time::Duration;
    ///
    /// fn main() {
    ///     let api = SyncApi::new().expect("Error creating API");
    ///
    ///     let config = Config::new(Currency::USD);
    ///
    ///     let rates = api.get_timeout(&config, Duration::from_secs(5)).expect("Error retrieving rates");
    ///
    ///     match rates {
    ///         Some(x) => println!("{:?}", x),
    ///         _ => println!("Request timed out")
    ///     };
    /// }
    /// ```
    pub fn get_timeout(&self,
                       conf: &Config,
                       duration: Duration)
                       -> Result<Option<Exchange>, Error> {
        let future = self.api.get_timeout(conf, duration);

        self.core.borrow_mut().run(future)
    }
}

#[derive(Deserialize)]
struct RawResponse {
    error: Option<String>,
    base: Option<String>,
    date: Option<String>,
    rates: Option<Rates>,
}

enum Response {
    Error(String),
    Success(Exchange),
}

impl<'de> Deserialize<'de> for Response {
    fn deserialize<D>(deserializer: D) -> Result<Response, D::Error>
        where D: Deserializer<'de>
    {
        let raw: RawResponse = Deserialize::deserialize(deserializer)?;

        if let Some(err) = raw.error {
            Ok(Response::Error(err))
        } else {
            Ok(Response::Success(Exchange {
                                     base: raw.base.unwrap(),
                                     date: raw.date.unwrap(),
                                     rates: raw.rates.unwrap(),
                                 }))
        }
    }
}

#[cfg(test)]
mod tests {
    use client::{Config, get_url};
    use exchange::Currency;

    #[test]
    fn get_url_test() {
        let tests = vec![("http://api.fixer.io/latest?base=AUD",
                          Config {
                              base: Currency::AUD,
                              symbols: None,
                              date: None,
                          }),
                         ("http://api.fixer.io/latest?base=EUR",
                          Config {
                              base: Currency::EUR,
                              symbols: None,
                              date: None,
                          }),
                         ("http://api.fixer.io/2000-01-03?base=EUR",
                          Config {
                              base: Currency::EUR,
                              symbols: None,
                              date: Some(String::from("2000-01-03")),
                          }),
                         ("http://api.fixer.io/latest?base=EUR&symbols=AUD",
                          Config {
                              base: Currency::EUR,
                              symbols: Some(vec![Currency::AUD]),
                              date: None,
                          }),
                         ("http://api.fixer.io/latest?base=EUR&symbols=AUD,USD",
                          Config {
                              base: Currency::EUR,
                              symbols: Some(vec![Currency::AUD, Currency::USD]),
                              date: None,
                          })];

        for test in tests {
            assert_eq!(test.0, get_url(&test.1));
        }
    }
}<|endoftext|>


<commit_before>extern crate capnp;
extern crate capnp_rpc;

use capnp_rpc::ez_rpc::EzRpcClient;
use capnp_rpc::capability::{InitRequest, LocalClient, WaitForContent};

pub mod ramp_capnp {
  include!(concat!(env!("OUT_DIR"), "/ramp_capnp.rs"));
}

fn main() {
    println!("Starting up test");
}
<commit_msg>punisher client test debug<commit_after>extern crate capnp;
extern crate capnp_rpc;

use capnp_rpc::ez_rpc::EzRpcClient;
use capnp_rpc::capability::{InitRequest, LocalClient, WaitForContent};

pub mod ramp_capnp {
  include!(concat!(env!("OUT_DIR"), "/ramp_capnp.rs"));
}

fn main() {
    println!("Starting up test");

    let mut rpc_client = EzRpcClient::new("127.0.0.1:6000").unwrap();
    println!("Punisher client test ended");
}
<|endoftext|>


<commit_before>//! The client module contains the Client and ClientList struct.
//!
//! They are representations of a TS3 client and clientlist.

use rustc_serialize::json;
use std::fmt;
use std::cmp;
use std::ops::Deref;
use std::str::FromStr;
use map::*;
use escaping::*;
use error;

/// Client contains a TeamSpeak 3 client.
/// # Example
/// ```
/// use sqlib::client;
///
/// let client = client::Client {
///     clid: 1,
///     cid: 1,
///     client_database_id: 1,
///     client_nickname: "John Doe".to_string(),
///     client_type: 0,
///     connection_connected_time: 0
/// };
///
/// assert!(client.is_client());
///
/// let client_print = format!("{}", client);
///
/// assert_eq!("John Doe (0)".to_string(), client_print);
/// ```
#[derive(Debug, Clone, RustcDecodable, RustcEncodable)]
pub struct Client {
    /// client id
    pub clid: i64,
    /// channel id
    pub cid: i64,
    /// client database id
    pub client_database_id: i64,
    /// client nickname
    pub client_nickname: String,
    /// client type: 0 is client and 1 is query client
    pub client_type: i64,
    /// connection time in milliseconds
    pub connection_connected_time: i64,
}

impl Default for Client {
    fn default() -> Client {
        Client {
            clid: 0,
            cid: 0,
            client_database_id: 0,
            client_nickname: String::new(),
            client_type: 0,
            connection_connected_time: 0,
        }
    }
}

impl Client {
    /// creates a new client from a client id and a nickname
    pub fn new(client_id: i64, nickname: String) -> Client {
        let mut client = Client::default();
        client.clid = client_id;
        client.client_nickname = nickname;
        client.unescape();
        client
    }
    /// checks if it is a real client
    pub fn is_client(&self) -> bool {
        self.client_type == 0
    }

    fn unescape(&mut self) {
        self.client_nickname = unescape(&self.client_nickname);
    }

    /// creates a Client from a given map.
    pub fn from_map(map: &StringMap) -> Client {
        let mut client = Client::default();
        client.mut_from_map(map);
        client
    }

    /// updates a given Client from a given map.
    pub fn update_from_map(client: Client, map: &StringMap) -> Client {
        let mut client = client.clone();
        client.mut_from_map(map);
        client
    }

    /// mutates self from a given map.
    pub fn mut_from_map(&mut self, map: &StringMap) {
        update_from_map(map, "clid", &mut self.clid);
        update_from_map(map, "cid", &mut self.cid);
        update_from_map(map, "client_database_id", &mut self.client_database_id);
        update_from_map(map, "client_nickname", &mut self.client_nickname);
        update_from_map(map, "client_type", &mut self.client_type);
        update_from_map(map,
                        "connection_connected_time",
                        &mut self.connection_connected_time);
        self.unescape();
    }

    fn connection_connected_time_string(&self) -> String {
        let mut raw_seconds = self.connection_connected_time / 1000;
        let hours = raw_seconds / 3600;
        raw_seconds = raw_seconds % 3600;
        let minutes = raw_seconds / 60;
        let seconds = raw_seconds % 60;
        if hours > 0 {
            format!("{:02}:{:02}:{:02}", hours, minutes, seconds)
        } else if minutes > 0 {
            format!("{:02}:{:02}", minutes, seconds)
        } else {
            format!("{}", seconds)
        }
    }

    /// creates a JSON String from self.
    pub fn as_json(&self) -> String {
        json::encode(self).unwrap_or(String::new())
    }
}

impl FromStr for Client {
    type Err = error::Error;
    fn from_str(s: &str) -> error::Result<Self> {
        let map = to_map(s);
        Ok(Client::from_map(&map))
    }
}

impl fmt::Display for Client {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f,
               "{} ({})",
               &self.client_nickname,
               self.connection_connected_time_string())
    }
}

impl PartialEq for Client {
    fn eq(&self, other: &Client) -> bool {
        self.clid.eq(&other.clid)
    }
}

impl Eq for Client {}

impl PartialOrd for Client {
    fn partial_cmp(&self, other: &Client) -> Option<cmp::Ordering> {
        self.clid.partial_cmp(&other.clid)
    }
}

impl Ord for Client {
    fn cmp(&self, other: &Client) -> cmp::Ordering {
        self.clid.cmp(&other.clid)
    }
}

/// ClientList contains a list of Clients.
///
/// # Example
/// ```
/// use sqlib::client::{Client, ClientList};
///
/// let client1 = Client::new(1, "test1".to_string());
/// let client2 = Client::new(2, "test2".to_string());
///
/// let clients = vec![client1, client2];
/// let clientlist = ClientList::from(clients.clone());
///
/// assert_eq!(&clients, clientlist.as_ref());
///
/// let clients2: Vec<_> = clientlist.into();
/// assert_eq!(clients, clients2);
/// ```
#[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Clone, RustcDecodable, RustcEncodable)]
pub struct ClientList(Vec<Client>);

impl ClientList {
    /// creates an empty ClientList
    pub fn new() -> ClientList {
        ClientList(Vec::new())
    }

    /// converts a ClientList into a vector of Client's
    pub fn into_inner(self) -> Vec<Client> {
        let ClientList(clients) = self;
        clients
    }

    /// filter_clients borrows a ClientList and creates a new one only with real
    /// clients.
    pub fn filter_clients(&self) -> ClientList {
        let new_vec = self.iter().map(Clone::clone).filter(|c| c.is_client()).collect();
        ClientList(new_vec)
    }

    /// creates a ClientList from a Vec of StringMaps
    pub fn from_maps(maps: &Vec<StringMap>) -> ClientList {
        let mut vec = Vec::new();
        for map in maps.iter() {
            let client = Client::from_map(map);
            vec.push(client);
        }
        ClientList(vec)
    }

    /// creats a JSON String from a ClientList
    pub fn as_json(&self) -> String {
        json::encode(self).unwrap_or(String::new())
    }
}

impl FromStr for ClientList {
    type Err = error::Error;
    fn from_str(s: &str) -> error::Result<Self> {
        let maps = s.split('|').map(to_map).collect();
        Ok(ClientList::from_maps(&maps))
    }
}

impl From<Vec<Client>> for ClientList {
    fn from(clients: Vec<Client>) -> ClientList {
        ClientList(clients)
    }
}

impl From<ClientList> for Vec<Client> {
    fn from(clients: ClientList) -> Self {
        clients.into_inner()
    }
}

impl Deref for ClientList {
    type Target = Vec<Client>;
    fn deref(&self) -> &Vec<Client> {
        self.as_ref()
    }
}

impl AsRef<Vec<Client>> for ClientList {
    fn as_ref(&self) -> &Vec<Client> {
        let &ClientList(ref clients) = self;
        clients
    }
}

impl AsMut<Vec<Client>> for ClientList {
    fn as_mut(&mut self) -> &mut Vec<Client> {
        let &mut ClientList(ref mut clients) = self;
        clients
    }
}

impl fmt::Display for ClientList {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        try!(write!(f, "[ "));
        let mut cls = self.iter();
        if cls.len() > 0 {
            try!(write!(f, "{}", cls.next().unwrap()));
        }
        for client in cls {
            try!(write!(f, ", {}", client));
        }
        write!(f, " ]")
    }
}
<commit_msg>fixing typo in documentation of sqlib::client<commit_after>//! The client module contains the Client and ClientList structs.
//!
//! They are representations of a TS3 client and clientlist.

use rustc_serialize::json;
use std::fmt;
use std::cmp;
use std::ops::Deref;
use std::str::FromStr;
use map::*;
use escaping::*;
use error;

/// Client contains a TeamSpeak 3 client.
/// # Example
/// ```
/// use sqlib::client;
///
/// let client = client::Client {
///     clid: 1,
///     cid: 1,
///     client_database_id: 1,
///     client_nickname: "John Doe".to_string(),
///     client_type: 0,
///     connection_connected_time: 0
/// };
///
/// assert!(client.is_client());
///
/// let client_print = format!("{}", client);
///
/// assert_eq!("John Doe (0)".to_string(), client_print);
/// ```
#[derive(Debug, Clone, RustcDecodable, RustcEncodable)]
pub struct Client {
    /// client id
    pub clid: i64,
    /// channel id
    pub cid: i64,
    /// client database id
    pub client_database_id: i64,
    /// client nickname
    pub client_nickname: String,
    /// client type: 0 is client and 1 is query client
    pub client_type: i64,
    /// connection time in milliseconds
    pub connection_connected_time: i64,
}

impl Default for Client {
    fn default() -> Client {
        Client {
            clid: 0,
            cid: 0,
            client_database_id: 0,
            client_nickname: String::new(),
            client_type: 0,
            connection_connected_time: 0,
        }
    }
}

impl Client {
    /// creates a new client from a client id and a nickname
    pub fn new(client_id: i64, nickname: String) -> Client {
        let mut client = Client::default();
        client.clid = client_id;
        client.client_nickname = nickname;
        client.unescape();
        client
    }
    /// checks if it is a real client
    pub fn is_client(&self) -> bool {
        self.client_type == 0
    }

    fn unescape(&mut self) {
        self.client_nickname = unescape(&self.client_nickname);
    }

    /// creates a Client from a given map.
    pub fn from_map(map: &StringMap) -> Client {
        let mut client = Client::default();
        client.mut_from_map(map);
        client
    }

    /// updates a given Client from a given map.
    pub fn update_from_map(client: Client, map: &StringMap) -> Client {
        let mut client = client.clone();
        client.mut_from_map(map);
        client
    }

    /// mutates self from a given map.
    pub fn mut_from_map(&mut self, map: &StringMap) {
        update_from_map(map, "clid", &mut self.clid);
        update_from_map(map, "cid", &mut self.cid);
        update_from_map(map, "client_database_id", &mut self.client_database_id);
        update_from_map(map, "client_nickname", &mut self.client_nickname);
        update_from_map(map, "client_type", &mut self.client_type);
        update_from_map(map,
                        "connection_connected_time",
                        &mut self.connection_connected_time);
        self.unescape();
    }

    fn connection_connected_time_string(&self) -> String {
        let mut raw_seconds = self.connection_connected_time / 1000;
        let hours = raw_seconds / 3600;
        raw_seconds = raw_seconds % 3600;
        let minutes = raw_seconds / 60;
        let seconds = raw_seconds % 60;
        if hours > 0 {
            format!("{:02}:{:02}:{:02}", hours, minutes, seconds)
        } else if minutes > 0 {
            format!("{:02}:{:02}", minutes, seconds)
        } else {
            format!("{}", seconds)
        }
    }

    /// creates a JSON String from self.
    pub fn as_json(&self) -> String {
        json::encode(self).unwrap_or(String::new())
    }
}

impl FromStr for Client {
    type Err = error::Error;
    fn from_str(s: &str) -> error::Result<Self> {
        let map = to_map(s);
        Ok(Client::from_map(&map))
    }
}

impl fmt::Display for Client {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f,
               "{} ({})",
               &self.client_nickname,
               self.connection_connected_time_string())
    }
}

impl PartialEq for Client {
    fn eq(&self, other: &Client) -> bool {
        self.clid.eq(&other.clid)
    }
}

impl Eq for Client {}

impl PartialOrd for Client {
    fn partial_cmp(&self, other: &Client) -> Option<cmp::Ordering> {
        self.clid.partial_cmp(&other.clid)
    }
}

impl Ord for Client {
    fn cmp(&self, other: &Client) -> cmp::Ordering {
        self.clid.cmp(&other.clid)
    }
}

/// ClientList contains a list of Clients.
///
/// # Example
/// ```
/// use sqlib::client::{Client, ClientList};
///
/// let client1 = Client::new(1, "test1".to_string());
/// let client2 = Client::new(2, "test2".to_string());
///
/// let clients = vec![client1, client2];
/// let clientlist = ClientList::from(clients.clone());
///
/// assert_eq!(&clients, clientlist.as_ref());
///
/// let clients2: Vec<_> = clientlist.into();
/// assert_eq!(clients, clients2);
/// ```
#[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Clone, RustcDecodable, RustcEncodable)]
pub struct ClientList(Vec<Client>);

impl ClientList {
    /// creates an empty ClientList
    pub fn new() -> ClientList {
        ClientList(Vec::new())
    }

    /// converts a ClientList into a vector of Client's
    pub fn into_inner(self) -> Vec<Client> {
        let ClientList(clients) = self;
        clients
    }

    /// filter_clients borrows a ClientList and creates a new one only with real
    /// clients.
    pub fn filter_clients(&self) -> ClientList {
        let new_vec = self.iter().map(Clone::clone).filter(|c| c.is_client()).collect();
        ClientList(new_vec)
    }

    /// creates a ClientList from a Vec of StringMaps
    pub fn from_maps(maps: &Vec<StringMap>) -> ClientList {
        let mut vec = Vec::new();
        for map in maps.iter() {
            let client = Client::from_map(map);
            vec.push(client);
        }
        ClientList(vec)
    }

    /// creats a JSON String from a ClientList
    pub fn as_json(&self) -> String {
        json::encode(self).unwrap_or(String::new())
    }
}

impl FromStr for ClientList {
    type Err = error::Error;
    fn from_str(s: &str) -> error::Result<Self> {
        let maps = s.split('|').map(to_map).collect();
        Ok(ClientList::from_maps(&maps))
    }
}

impl From<Vec<Client>> for ClientList {
    fn from(clients: Vec<Client>) -> ClientList {
        ClientList(clients)
    }
}

impl From<ClientList> for Vec<Client> {
    fn from(clients: ClientList) -> Self {
        clients.into_inner()
    }
}

impl Deref for ClientList {
    type Target = Vec<Client>;
    fn deref(&self) -> &Vec<Client> {
        self.as_ref()
    }
}

impl AsRef<Vec<Client>> for ClientList {
    fn as_ref(&self) -> &Vec<Client> {
        let &ClientList(ref clients) = self;
        clients
    }
}

impl AsMut<Vec<Client>> for ClientList {
    fn as_mut(&mut self) -> &mut Vec<Client> {
        let &mut ClientList(ref mut clients) = self;
        clients
    }
}

impl fmt::Display for ClientList {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        try!(write!(f, "[ "));
        let mut cls = self.iter();
        if cls.len() > 0 {
            try!(write!(f, "{}", cls.next().unwrap()));
        }
        for client in cls {
            try!(write!(f, ", {}", client));
        }
        write!(f, " ]")
    }
}
<|endoftext|>


<commit_before>use std::string::CowString;
use std::fmt;
use std::collections::HashMap;
use std::rc::Rc;
use std::ops::Deref;

use error::{CompileError, CompileErrorKind, RuntimeError};
use datum::Datum;
use runtime::{Inst, MemRef, RDatum, RuntimeData, Closure};

/// Syntax variables
#[derive(Copy, Clone, PartialEq)]
pub enum Syntax {
    /// `lambda` syntax
    Lambda
}

/// Environment variables in the global environment
pub enum EnvVar {
    /// Syntax variables
    Syntax(Syntax),

    /// Primitive functions
    PrimFunc(&'static str, Rc<fn(&[RDatum]) -> Result<RDatum, RuntimeError>>),

    /// Compiled library functions
    Procedure(Rc<Vec<Inst>>)
}

/// Compiler compiles Datum into a bytecode evaluates it
pub struct Compiler<'g> {
    /// Global environment
    global_env: &'g HashMap<CowString<'static>, EnvVar>
}

struct CodeGenContext {
    code: Vec<Inst>,
    link_size: usize
}

impl<'g> Compiler<'g> {
    /// Creates a new compiler with given environment
    pub fn new<'a>(global_env: &'a HashMap<CowString<'static>, EnvVar>) -> Compiler<'a> {
        Compiler {
            global_env: global_env
        }
    }

    /// Compiles the datum into a bytecode evaluates it
    pub fn compile(&mut self, datum: &RDatum) -> Result<Vec<Inst>, CompileError> {
        let mut ctx = CodeGenContext {
            code: Vec::new(),
            link_size: 0
        };
        try!(self.compile_expr(&[], &[], &mut ctx, datum));
        ctx.code.push(Inst::Return);
        return Ok(ctx.code);
    }

    fn compile_call(&mut self, static_scope: &[Vec<CowString<'static>>], args: &[CowString<'static>],
                    ctx: &mut CodeGenContext, datum: &RDatum)
            -> Result<(), CompileError>
    {
        let mut arg_count = 0;
        for d in datum.iter() {
            match d {
                Ok(d) => {
                    try!(self.compile_expr(static_scope, args, ctx, &d));
                    arg_count += 1;
                },
                Err(()) => return Err(CompileError { kind: CompileErrorKind::DottedEval })
            }
        }

        if arg_count == 0 {
            Err(CompileError { kind: CompileErrorKind::NullEval })
        } else {
            ctx.code.push(Inst::Call(arg_count - 1));
            Ok(())
        }
    }

    fn compile_block(&mut self, static_scope: &[Vec<CowString<'static>>],
                      args: &[CowString<'static>], body: &RDatum)
            -> Result<CodeGenContext, CompileError>
    {
        let mut ctx = CodeGenContext {
            code: Vec::new(),
            link_size: 0
        };
        for expr in body.iter() {
            if let Ok(e) = expr {
                try!(self.compile_expr(static_scope, args, &mut ctx, &e));
            } else {
                return Err(CompileError { kind: CompileErrorKind::DottedBody });
            }
        }

        ctx.code.push(Inst::Return);

        return Ok(ctx);
    }

    fn compile_expr(&mut self, static_scope: &[Vec<CowString<'static>>], args: &[CowString<'static>],
                    ctx: &mut CodeGenContext, datum: &RDatum)
            -> Result<(), CompileError>
    {
        match datum {
            &Datum::Cons(ref h, ref t) =>
                if let &Datum::Sym(ref n) = h.borrow().deref() {
                    if let Some(&EnvVar::Syntax(Syntax::Lambda)) = self.global_env.get(n) {
                        if let &Datum::Cons(ref cur_args, ref body) = t.borrow().deref() {
                            let new_scope = {
                                let mut nenv = static_scope.to_vec();
                                nenv.push(args.to_vec());
                                nenv
                            };

                            let new_args = {
                                let mut nargs = Vec::new();
                                for arg in cur_args.borrow().iter() {
                                    if let Ok(Datum::Sym(s)) = arg {
                                        nargs.push(s)
                                    } else {
                                        return Err(CompileError { kind: CompileErrorKind::BadLambdaSyntax });
                                    }
                                }
                                nargs
                            };

                            let block_ctx = try!(self.compile_block(
                                    new_scope.as_slice(), new_args.as_slice(), &*body.borrow()
                            ));

                            ctx.code.push(Inst::PushArg(MemRef::Closure(
                                        Rc::new(block_ctx.code),
                                        block_ctx.link_size
                            )));
                            return Ok(());
                        } else {
                            return Err(CompileError { kind: CompileErrorKind::BadLambdaSyntax })
                        }
                    } else {
                        self.compile_call(static_scope, args, ctx, datum)
                    }
                } else {
                    self.compile_call(static_scope, args, ctx, datum)
                },
            &Datum::Nil => Err(CompileError { kind: CompileErrorKind::NullEval }),
            &Datum::Sym(ref sym) => {
                if let Some(i) = range(0, args.len()).find(|&i| args[i] == *sym) {
                    ctx.code.push(Inst::PushArg(MemRef::Arg(i)));
                    return Ok(());
                }

                // (0, static_scope[-1]), (1, static_scope[-2]), (2, static_scope[-3]), ...
                for (i, up_args) in static_scope.iter().rev().enumerate() {
                    for (j, arg) in up_args.iter().enumerate() {
                        if *arg == *sym {
                            if ctx.link_size < i+1 {
                                ctx.link_size = i+1;
                            }
                            ctx.code.push(Inst::PushArg(MemRef::UpValue(i, j)));
                            return Ok(());
                        }
                    }
                }

                match self.global_env.get(sym) {
                    Some(data) => match data {
                        &EnvVar::Syntax(_) =>
                            Err(CompileError { kind: CompileErrorKind::SyntaxReference }),
                        &EnvVar::PrimFunc(ref name, ref func) => {
                            ctx.code.push(Inst::PushArg(MemRef::Const(Datum::Ext(RuntimeData::PrimFunc(
                                                name.clone(),
                                                func.clone()
                            )))));
                            Ok(())
                        },
                        &EnvVar::Procedure(ref code) => {
                            ctx.code.push(Inst::PushArg(MemRef::Const(Datum::Ext(RuntimeData::Closure(Closure::new(
                                                code.clone(),
                                                None
                            ))))));
                            Ok(())
                        }
                    },
                    None =>
                        Err(CompileError { kind: CompileErrorKind::UnboundVariable })
                }
            },
            _ => {
                ctx.code.push(Inst::PushArg(MemRef::Const(datum.clone())));
                Ok(())
            }
        }
    }
}

impl fmt::Show for Syntax {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "lambda")
    }
}

#[cfg(test)]
mod test {
    use std::borrow::Cow;
    use std::rc::Rc;
    use datum::Datum;
    use runtime::{Inst, MemRef, RuntimeData};
    use base::libbase;
    use primitive::PRIM_ADD;
    use super::Compiler;

    #[test]
    fn test_simple_expr() {
        let env = libbase();
        let mut compiler = Compiler::new(&env);
        let expected = Ok(vec![
            Inst::PushArg(MemRef::Const(Datum::Ext(RuntimeData::PrimFunc("+", Rc::new(PRIM_ADD))))),
            Inst::PushArg(MemRef::Const(Datum::Num(1))),
            Inst::PushArg(MemRef::Const(Datum::Num(2))),
            Inst::Call(2),
            Inst::Return
        ]);
        let code = compiler.compile(&list![sym!("+"), num!(1), num!(2)]);
        assert_eq!(expected, code);
    }

    #[test]
    fn test_nested_expr() {
        let env = libbase();
        let mut compiler = Compiler::new(&env);
        let expected = Ok(vec![
            Inst::PushArg(MemRef::Const(Datum::Ext(RuntimeData::PrimFunc("+", Rc::new(PRIM_ADD))))),
            Inst::PushArg(MemRef::Const(Datum::Num(3))),
            Inst::PushArg(MemRef::Const(Datum::Ext(RuntimeData::PrimFunc("+", Rc::new(PRIM_ADD))))),
            Inst::PushArg(MemRef::Const(Datum::Num(1))),
            Inst::PushArg(MemRef::Const(Datum::Num(2))),
            Inst::Call(2),
            Inst::Call(2),
            Inst::Return
        ]);
        let code = compiler.compile(&list![sym!("+"), num!(3), list![sym!("+"), num!(1), num!(2)]]);
        assert_eq!(expected, code);
    }

    #[test]
    fn test_lambda() {
        let env = libbase();
        let mut compiler = Compiler::new(&env);

        let f = vec![
            Inst::PushArg(MemRef::Const(Datum::Ext(RuntimeData::PrimFunc("+", Rc::new(PRIM_ADD))))),
            Inst::PushArg(MemRef::Arg(0)),
            Inst::PushArg(MemRef::Const(Datum::Num(2))),
            Inst::Call(2),
            Inst::Return
        ];
        let expected = Ok(vec![
            Inst::PushArg(MemRef::Closure(Rc::new(f), 0)),
            Inst::PushArg(MemRef::Const(Datum::Num(1))),
            Inst::Call(1),
            Inst::Return
        ]);
        let code = compiler.compile(&list![
                           list![sym!("lambda"), list![sym!("x")],
                                list![sym!("+"), sym!("x"), num!(2)]],
                            num!(1)]);
        assert_eq!(expected, code);
    }

    #[test]
    fn test_upvalue() {
        let env = libbase();
        let mut compiler = Compiler::new(&env);

        let f = vec![
            Inst::PushArg(MemRef::Const(Datum::Ext(RuntimeData::PrimFunc("+", Rc::new(PRIM_ADD))))),
            Inst::PushArg(MemRef::UpValue(0, 0)),
            Inst::PushArg(MemRef::Arg(0)),
            Inst::Call(2),
            Inst::Return
        ];
        let g = vec![
            Inst::PushArg(MemRef::Closure(Rc::new(f), 1)),
            Inst::Return
        ];
        let expected = Ok(vec![
            Inst::PushArg(MemRef::Closure(Rc::new(g), 0)),
            Inst::PushArg(MemRef::Const(Datum::Num(2))),
            Inst::Call(1),
            Inst::PushArg(MemRef::Const(Datum::Num(3))),
            Inst::Call(1),
            Inst::Return
        ]);

        // ((
        //   (lambda (x)            # = g
        //     (lambda (y) (+ x y)) # = f
        //   ) 2) 3)
        let code = compiler.compile(&list![
            list![
                list![sym!("lambda"), list![sym!("x")],
                    list![sym!("lambda"), list![sym!("y")],
                        list![sym!("+"), sym!("x"), sym!("y")]]],
                num!(2)],
            num!(3)
        ]);
        assert_eq!(expected, code)
    }
}
<commit_msg>Simpler closure reference<commit_after>use std::string::CowString;
use std::fmt;
use std::collections::HashMap;
use std::rc::Rc;
use std::ops::Deref;

use error::{CompileError, CompileErrorKind, RuntimeError};
use datum::Datum;
use runtime::{Inst, MemRef, RDatum, RuntimeData};

/// Syntax variables
#[derive(Copy, Clone, PartialEq)]
pub enum Syntax {
    /// `lambda` syntax
    Lambda
}

/// Environment variables in the global environment
pub enum EnvVar {
    /// Syntax variables
    Syntax(Syntax),

    /// Primitive functions
    PrimFunc(&'static str, Rc<fn(&[RDatum]) -> Result<RDatum, RuntimeError>>),

    /// Compiled library functions
    Procedure(Rc<Vec<Inst>>)
}

/// Compiler compiles Datum into a bytecode evaluates it
pub struct Compiler<'g> {
    /// Global environment
    global_env: &'g HashMap<CowString<'static>, EnvVar>
}

struct CodeGenContext {
    code: Vec<Inst>,
    link_size: usize
}

impl<'g> Compiler<'g> {
    /// Creates a new compiler with given environment
    pub fn new<'a>(global_env: &'a HashMap<CowString<'static>, EnvVar>) -> Compiler<'a> {
        Compiler {
            global_env: global_env
        }
    }

    /// Compiles the datum into a bytecode evaluates it
    pub fn compile(&mut self, datum: &RDatum) -> Result<Vec<Inst>, CompileError> {
        let mut ctx = CodeGenContext {
            code: Vec::new(),
            link_size: 0
        };
        try!(self.compile_expr(&[], &[], &mut ctx, datum));
        ctx.code.push(Inst::Return);
        return Ok(ctx.code);
    }

    fn compile_call(&mut self, static_scope: &[Vec<CowString<'static>>], args: &[CowString<'static>],
                    ctx: &mut CodeGenContext, datum: &RDatum)
            -> Result<(), CompileError>
    {
        let mut arg_count = 0;
        for d in datum.iter() {
            match d {
                Ok(d) => {
                    try!(self.compile_expr(static_scope, args, ctx, &d));
                    arg_count += 1;
                },
                Err(()) => return Err(CompileError { kind: CompileErrorKind::DottedEval })
            }
        }

        if arg_count == 0 {
            Err(CompileError { kind: CompileErrorKind::NullEval })
        } else {
            ctx.code.push(Inst::Call(arg_count - 1));
            Ok(())
        }
    }

    fn compile_block(&mut self, static_scope: &[Vec<CowString<'static>>],
                      args: &[CowString<'static>], body: &RDatum)
            -> Result<CodeGenContext, CompileError>
    {
        let mut ctx = CodeGenContext {
            code: Vec::new(),
            link_size: 0
        };
        for expr in body.iter() {
            if let Ok(e) = expr {
                try!(self.compile_expr(static_scope, args, &mut ctx, &e));
            } else {
                return Err(CompileError { kind: CompileErrorKind::DottedBody });
            }
        }

        ctx.code.push(Inst::Return);

        return Ok(ctx);
    }

    fn compile_expr(&mut self, static_scope: &[Vec<CowString<'static>>], args: &[CowString<'static>],
                    ctx: &mut CodeGenContext, datum: &RDatum)
            -> Result<(), CompileError>
    {
        match datum {
            &Datum::Cons(ref h, ref t) =>
                if let &Datum::Sym(ref n) = h.borrow().deref() {
                    if let Some(&EnvVar::Syntax(Syntax::Lambda)) = self.global_env.get(n) {
                        if let &Datum::Cons(ref cur_args, ref body) = t.borrow().deref() {
                            let new_scope = {
                                let mut nenv = static_scope.to_vec();
                                nenv.push(args.to_vec());
                                nenv
                            };

                            let new_args = {
                                let mut nargs = Vec::new();
                                for arg in cur_args.borrow().iter() {
                                    if let Ok(Datum::Sym(s)) = arg {
                                        nargs.push(s)
                                    } else {
                                        return Err(CompileError { kind: CompileErrorKind::BadLambdaSyntax });
                                    }
                                }
                                nargs
                            };

                            let block_ctx = try!(self.compile_block(
                                    new_scope.as_slice(), new_args.as_slice(), &*body.borrow()
                            ));

                            ctx.code.push(Inst::PushArg(MemRef::Closure(
                                        Rc::new(block_ctx.code),
                                        block_ctx.link_size
                            )));
                            return Ok(());
                        } else {
                            return Err(CompileError { kind: CompileErrorKind::BadLambdaSyntax })
                        }
                    } else {
                        self.compile_call(static_scope, args, ctx, datum)
                    }
                } else {
                    self.compile_call(static_scope, args, ctx, datum)
                },
            &Datum::Nil => Err(CompileError { kind: CompileErrorKind::NullEval }),
            &Datum::Sym(ref sym) => {
                if let Some(i) = range(0, args.len()).find(|&i| args[i] == *sym) {
                    ctx.code.push(Inst::PushArg(MemRef::Arg(i)));
                    return Ok(());
                }

                // (0, static_scope[-1]), (1, static_scope[-2]), (2, static_scope[-3]), ...
                for (i, up_args) in static_scope.iter().rev().enumerate() {
                    for (j, arg) in up_args.iter().enumerate() {
                        if *arg == *sym {
                            if ctx.link_size < i+1 {
                                ctx.link_size = i+1;
                            }
                            ctx.code.push(Inst::PushArg(MemRef::UpValue(i, j)));
                            return Ok(());
                        }
                    }
                }

                match self.global_env.get(sym) {
                    Some(data) => match data {
                        &EnvVar::Syntax(_) =>
                            Err(CompileError { kind: CompileErrorKind::SyntaxReference }),
                        &EnvVar::PrimFunc(ref name, ref func) => {
                            ctx.code.push(Inst::PushArg(MemRef::Const(Datum::Ext(RuntimeData::PrimFunc(
                                                name.clone(),
                                                func.clone()
                            )))));
                            Ok(())
                        },
                        &EnvVar::Procedure(ref code) => {
                            ctx.code.push(Inst::PushArg(MemRef::Closure(code.clone(), 0)));
                            Ok(())
                        }
                    },
                    None =>
                        Err(CompileError { kind: CompileErrorKind::UnboundVariable })
                }
            },
            _ => {
                ctx.code.push(Inst::PushArg(MemRef::Const(datum.clone())));
                Ok(())
            }
        }
    }
}

impl fmt::Show for Syntax {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "lambda")
    }
}

#[cfg(test)]
mod test {
    use std::borrow::Cow;
    use std::rc::Rc;
    use datum::Datum;
    use runtime::{Inst, MemRef, RuntimeData};
    use base::libbase;
    use primitive::PRIM_ADD;
    use super::Compiler;

    #[test]
    fn test_simple_expr() {
        let env = libbase();
        let mut compiler = Compiler::new(&env);
        let expected = Ok(vec![
            Inst::PushArg(MemRef::Const(Datum::Ext(RuntimeData::PrimFunc("+", Rc::new(PRIM_ADD))))),
            Inst::PushArg(MemRef::Const(Datum::Num(1))),
            Inst::PushArg(MemRef::Const(Datum::Num(2))),
            Inst::Call(2),
            Inst::Return
        ]);
        let code = compiler.compile(&list![sym!("+"), num!(1), num!(2)]);
        assert_eq!(expected, code);
    }

    #[test]
    fn test_nested_expr() {
        let env = libbase();
        let mut compiler = Compiler::new(&env);
        let expected = Ok(vec![
            Inst::PushArg(MemRef::Const(Datum::Ext(RuntimeData::PrimFunc("+", Rc::new(PRIM_ADD))))),
            Inst::PushArg(MemRef::Const(Datum::Num(3))),
            Inst::PushArg(MemRef::Const(Datum::Ext(RuntimeData::PrimFunc("+", Rc::new(PRIM_ADD))))),
            Inst::PushArg(MemRef::Const(Datum::Num(1))),
            Inst::PushArg(MemRef::Const(Datum::Num(2))),
            Inst::Call(2),
            Inst::Call(2),
            Inst::Return
        ]);
        let code = compiler.compile(&list![sym!("+"), num!(3), list![sym!("+"), num!(1), num!(2)]]);
        assert_eq!(expected, code);
    }

    #[test]
    fn test_lambda() {
        let env = libbase();
        let mut compiler = Compiler::new(&env);

        let f = vec![
            Inst::PushArg(MemRef::Const(Datum::Ext(RuntimeData::PrimFunc("+", Rc::new(PRIM_ADD))))),
            Inst::PushArg(MemRef::Arg(0)),
            Inst::PushArg(MemRef::Const(Datum::Num(2))),
            Inst::Call(2),
            Inst::Return
        ];
        let expected = Ok(vec![
            Inst::PushArg(MemRef::Closure(Rc::new(f), 0)),
            Inst::PushArg(MemRef::Const(Datum::Num(1))),
            Inst::Call(1),
            Inst::Return
        ]);
        let code = compiler.compile(&list![
                           list![sym!("lambda"), list![sym!("x")],
                                list![sym!("+"), sym!("x"), num!(2)]],
                            num!(1)]);
        assert_eq!(expected, code);
    }

    #[test]
    fn test_upvalue() {
        let env = libbase();
        let mut compiler = Compiler::new(&env);

        let f = vec![
            Inst::PushArg(MemRef::Const(Datum::Ext(RuntimeData::PrimFunc("+", Rc::new(PRIM_ADD))))),
            Inst::PushArg(MemRef::UpValue(0, 0)),
            Inst::PushArg(MemRef::Arg(0)),
            Inst::Call(2),
            Inst::Return
        ];
        let g = vec![
            Inst::PushArg(MemRef::Closure(Rc::new(f), 1)),
            Inst::Return
        ];
        let expected = Ok(vec![
            Inst::PushArg(MemRef::Closure(Rc::new(g), 0)),
            Inst::PushArg(MemRef::Const(Datum::Num(2))),
            Inst::Call(1),
            Inst::PushArg(MemRef::Const(Datum::Num(3))),
            Inst::Call(1),
            Inst::Return
        ]);

        // ((
        //   (lambda (x)            # = g
        //     (lambda (y) (+ x y)) # = f
        //   ) 2) 3)
        let code = compiler.compile(&list![
            list![
                list![sym!("lambda"), list![sym!("x")],
                    list![sym!("lambda"), list![sym!("y")],
                        list![sym!("+"), sym!("x"), sym!("y")]]],
                num!(2)],
            num!(3)
        ]);
        assert_eq!(expected, code)
    }
}
<|endoftext|>


<commit_before>pub const ME: &'static str      = "kic";
pub const VERSION: &'static str = env!("CARGO_PKG_VERSION");

pub const MAIN_DIR: &'static str = ".";

#[cfg(target_os="linux")]
pub const BANNED_DIRS: [&'static str; 20] = [
    "/",
    "/bin",
    "/boot",
    "/dev",
    "/etc",
    "/home",
    "/lib",
    "/lib64",
    "/lost+found",
    "/media",
    "/mnt",
    "/opt",
    "/proc",
    "/root",
    "/run",
    "/sbin",
    "/srv",
    "/sys",
    "/usr",
    "/var",
];
#[cfg(target_os="macos")]
pub const BANNED_DIRS: [&'static str; 20] = [
    "/",
    "/Applications",
    "/System",
    "/bin",
    "/etc",
    "/net",
    "/sbin",
    "/var",
    "/Library",
    "/Users",
    "/cores",
    "/home",
    "/opt",
    "/tmp",
    "/Network",
    "/Volumes",
    "/dev",
    "/installer.failurerequests",
    "/private",
    "/usr",
];

pub const WORKING_DIR_NAME: &'static str = ".kic";
pub const STORAGE_DIR_NAME: &'static str = "warehouse";
pub const CONFIG_FILE_NAME: &'static str = "config.toml";
pub const IGNORE_FILE_NAME: &'static str = "ignore";
<commit_msg>[Bug] Fix "target_os"<commit_after>pub const ME: &'static str      = "kic";
pub const VERSION: &'static str = env!("CARGO_PKG_VERSION");

pub const MAIN_DIR: &'static str = ".";

#[cfg(not(target_os="macos"))]
pub const BANNED_DIRS: [&'static str; 20] = [
    "/",
    "/bin",
    "/boot",
    "/dev",
    "/etc",
    "/home",
    "/lib",
    "/lib64",
    "/lost+found",
    "/media",
    "/mnt",
    "/opt",
    "/proc",
    "/root",
    "/run",
    "/sbin",
    "/srv",
    "/sys",
    "/usr",
    "/var",
];
#[cfg(target_os="macos")]
pub const BANNED_DIRS: [&'static str; 20] = [
    "/",
    "/Applications",
    "/System",
    "/bin",
    "/etc",
    "/net",
    "/sbin",
    "/var",
    "/Library",
    "/Users",
    "/cores",
    "/home",
    "/opt",
    "/tmp",
    "/Network",
    "/Volumes",
    "/dev",
    "/installer.failurerequests",
    "/private",
    "/usr",
];

pub const WORKING_DIR_NAME: &'static str = ".kic";
pub const STORAGE_DIR_NAME: &'static str = "warehouse";
pub const CONFIG_FILE_NAME: &'static str = "config.toml";
pub const IGNORE_FILE_NAME: &'static str = "ignore";
<|endoftext|>


<commit_before>use crossbeam;

use std::sync::Arc;
use std::fs::{self, File};
use std::io::Read;
use std::path::Path;
use std::collections::HashMap;
use std::ffi::OsStr;
use liquid::Value;
use walkdir::WalkDir;
use document::Document;
use error::Result;

/// The primary build function that tranforms a directory into a site
pub fn build(source: &Path, dest: &Path, layout_str: &str, posts_str: &str) -> Result<()> {
    // TODO make configurable
    let template_extensions = [OsStr::new("tpl"), OsStr::new("md")];

    let layouts_path = source.join(layout_str);
    let posts_path = source.join(posts_str);

    let layouts = try!(get_layouts(&layouts_path));

    let mut documents = vec![];
    let mut post_data = vec![];

    let walker = WalkDir::new(&source).into_iter();

    for entry in walker.filter_map(|e| e.ok()).filter(|e| e.file_type().is_file()) {
        if template_extensions.contains(&entry.path()
                                              .extension()
                                              .unwrap_or(OsStr::new(""))) &&
           entry.path().parent() != Some(layouts_path.as_path()) {
            let doc = try!(parse_document(&entry.path(), source));
            if entry.path().parent() == Some(posts_path.as_path()) {
                post_data.push(Value::Object(doc.get_attributes()));
            }
            documents.push(doc);
        }
    }

    let mut handles = vec![];

    // generate documents (in parallel)
    // TODO I'm probably underutilizing crossbeam
    crossbeam::scope(|scope| {
        let post_data = Arc::new(post_data);
        let layouts = Arc::new(layouts);
        for doc in &documents {
            let post_data = post_data.clone();
            let layouts = layouts.clone();
            let handle = scope.spawn(move || doc.create_file(dest, &layouts, &post_data));
            handles.push(handle);
        }
    });

    for handle in handles {
        try!(handle.join());
    }

    // copy all remaining files in the source to the destination
    if source != dest {
        let source_str = try!(source.to_str()
                                    .ok_or(format!("Cannot convert pathname {:?} to UTF-8",
                                                   source)));

        let walker = WalkDir::new(&source)
                         .into_iter()
                         .filter_map(|e| e.ok())
                         .filter(|f| {
                             let p = f.path();
                             // don't copy hidden files
                             !p.file_name()
                               .and_then(|name| name.to_str())
                               .unwrap_or(".")
                               .starts_with(".") &&
                             !template_extensions.contains(&p.extension()
                                                             .unwrap_or(OsStr::new(""))) &&
                             p != dest && p != layouts_path.as_path()
                         });

        for entry in walker {
            let entry_path = try!(entry.path()
                                       .to_str()
                                       .ok_or(format!("Cannot convert pathname {:?} to UTF-8",
                                                      entry.path())));

            let relative = try!(entry_path.split(source_str)
                                          .last()
                                          .ok_or(format!("Empty path")));

            if try!(entry.metadata()).is_dir() {
                try!(fs::create_dir_all(&dest.join(relative)));
            } else {
                try!(fs::copy(entry.path(), &dest.join(relative)));
            }
        }
    }

    Ok(())
}

/// Gets all layout files from the specified path (usually _layouts/)
/// This walks the specified directory recursively
///
/// Returns a map filename -> content
fn get_layouts(layouts_path: &Path) -> Result<HashMap<String, String>> {
    let mut layouts = HashMap::new();

    let walker = WalkDir::new(layouts_path).into_iter();

    // go through the layout directory and add
    // filename -> text content to the layout map
    for entry in walker.filter_map(|e| e.ok()).filter(|e| e.file_type().is_file()) {
        let mut text = String::new();
        let mut file = try!(File::open(entry.path()));
        try!(file.read_to_string(&mut text));

        let path = try!(entry.path()
                             .file_name()
                             .and_then(|name| name.to_str())
                             .ok_or(format!("Cannot convert pathname {:?} to UTF-8",
                                            entry.path().file_name())));

        layouts.insert(path.to_owned(), text);
    }

    Ok(layouts)
}

fn parse_document(path: &Path, source: &Path) -> Result<Document> {
    let attributes = try!(extract_attributes(path));
    let content = try!(extract_content(path));

    let path_str = try!(path.to_str()
                            .ok_or(format!("Cannot convert pathname {:?} to UTF-8", path)));

    let source_str = try!(source.to_str()
                                .ok_or(format!("Cannot convert pathname {:?} to UTF-8", source)));

    let new_path = try!(path_str.split(source_str)
                                .last()
                                .ok_or(format!("Empty path")));

    let markdown = path.extension().unwrap_or(OsStr::new("")) == OsStr::new("md");

    Ok(Document::new(new_path.to_owned(), attributes, content, markdown))
}

fn parse_file(path: &Path) -> Result<String> {
    let mut file = try!(File::open(path));
    let mut text = String::new();
    try!(file.read_to_string(&mut text));
    Ok(text)
}

fn extract_attributes(path: &Path) -> Result<HashMap<String, String>> {
    let mut attributes = HashMap::new();
    attributes.insert("name".to_owned(),
                      try!(path.file_stem()
                               .and_then(|stem| stem.to_str())
                               .ok_or(format!("Invalid UTF-8 in file stem for {:?}", path)))
                          .to_owned());

    let content = try!(parse_file(path));

    if content.contains("---") {
        let mut content_splits = content.split("---");

        let attribute_string = try!(content_splits.nth(0).ok_or("Empty content"));

        for attribute_line in attribute_string.split("\n") {
            if !attribute_line.contains(':') {
                continue;
            }

            let attribute_split: Vec<&str> = attribute_line.split(':').collect();

            let key = attribute_split[0].trim_matches(' ').to_owned();
            let value = attribute_split[1].trim_matches(' ').to_owned();

            attributes.insert(key, value);
        }
    }

    Ok(attributes)
}

fn extract_content(path: &Path) -> Result<String> {
    let content = try!(parse_file(path));

    if content.contains("---") {
        let mut content_splits = content.split("---");

        return Ok(try!(content_splits.nth(1).ok_or("No content after header")).to_owned());
    }

    return Ok(content);
}
<commit_msg>skip dotfiles using walker macro<commit_after>use crossbeam;

use std::sync::Arc;
use std::fs::{self, File};
use std::io::Read;
use std::path::Path;
use std::collections::HashMap;
use std::ffi::OsStr;
use liquid::Value;
use walkdir::WalkDir;
use document::Document;
use error::Result;

macro_rules! walker {
    ($dir:expr) => {
        WalkDir::new($dir)
            .into_iter()
            .filter_map(|e| e.ok())
            .filter(|f| {
                // skip directories
                f.file_type().is_file()
                &&
                // don't copy hidden files
                !f.path()
                  .file_name()
                  .and_then(|name| name.to_str())
                  .unwrap_or(".")
                  .starts_with(".")
            })
    }
}

/// The primary build function that tranforms a directory into a site
pub fn build(source: &Path, dest: &Path, layout_str: &str, posts_str: &str) -> Result<()> {
    // TODO make configurable
    let template_extensions = [OsStr::new("tpl"), OsStr::new("md")];

    let layouts_path = source.join(layout_str);
    let posts_path = source.join(posts_str);

    let layouts = try!(get_layouts(&layouts_path));

    let mut documents = vec![];
    let mut post_data = vec![];

    for entry in walker!(&source) {
        if template_extensions.contains(&entry.path()
                                              .extension()
                                              .unwrap_or(OsStr::new(""))) &&
           entry.path().parent() != Some(layouts_path.as_path()) {
            let doc = try!(parse_document(&entry.path(), source));
            if entry.path().parent() == Some(posts_path.as_path()) {
                post_data.push(Value::Object(doc.get_attributes()));
            }
            documents.push(doc);
        }
    }

    let mut handles = vec![];

    // generate documents (in parallel)
    // TODO I'm probably underutilizing crossbeam
    crossbeam::scope(|scope| {
        let post_data = Arc::new(post_data);
        let layouts = Arc::new(layouts);
        for doc in &documents {
            let post_data = post_data.clone();
            let layouts = layouts.clone();
            let handle = scope.spawn(move || doc.create_file(dest, &layouts, &post_data));
            handles.push(handle);
        }
    });

    for handle in handles {
        try!(handle.join());
    }

    // copy all remaining files in the source to the destination
    if source != dest {
        let source_str = try!(source.to_str()
                                    .ok_or(format!("Cannot convert pathname {:?} to UTF-8",
                                                   source)));

        for entry in walker!(&source).filter(|f| !template_extensions.contains(&f.path()
                                                                                .extension()
                                                                                .unwrap_or(OsStr::new("")))
                                                 && f.path() != dest
                                                 && f.path() != layouts_path.as_path()) {
            let entry_path = try!(entry.path()
                                       .to_str()
                                       .ok_or(format!("Cannot convert pathname {:?} to UTF-8",
                                                      entry.path())));

            let relative = try!(entry_path.split(source_str)
                                          .last()
                                          .ok_or(format!("Empty path")));

            if try!(entry.metadata()).is_dir() {
                try!(fs::create_dir_all(&dest.join(relative)));
            } else {
                try!(fs::copy(entry.path(), &dest.join(relative)));
            }
        }
    }

    Ok(())
}

/// Gets all layout files from the specified path (usually _layouts/)
/// This walks the specified directory recursively
///
/// Returns a map filename -> content
fn get_layouts(layouts_path: &Path) -> Result<HashMap<String, String>> {
    let mut layouts = HashMap::new();

    // go through the layout directory and add
    // filename -> text content to the layout map
    for entry in walker!(layouts_path) {
        let mut text = String::new();
        let mut file = try!(File::open(entry.path()));
        try!(file.read_to_string(&mut text));

        let path = try!(entry.path()
                             .file_name()
                             .and_then(|name| name.to_str())
                             .ok_or(format!("Cannot convert pathname {:?} to UTF-8",
                                            entry.path().file_name())));

        layouts.insert(path.to_owned(), text);
    }

    Ok(layouts)
}

fn parse_document(path: &Path, source: &Path) -> Result<Document> {
    let attributes = try!(extract_attributes(path));
    let content = try!(extract_content(path));

    let path_str = try!(path.to_str()
                            .ok_or(format!("Cannot convert pathname {:?} to UTF-8", path)));

    let source_str = try!(source.to_str()
                                .ok_or(format!("Cannot convert pathname {:?} to UTF-8", source)));

    let new_path = try!(path_str.split(source_str)
                                .last()
                                .ok_or(format!("Empty path")));

    let markdown = path.extension().unwrap_or(OsStr::new("")) == OsStr::new("md");

    Ok(Document::new(new_path.to_owned(), attributes, content, markdown))
}

fn parse_file(path: &Path) -> Result<String> {
    let mut file = try!(File::open(path));
    let mut text = String::new();
    try!(file.read_to_string(&mut text));
    Ok(text)
}

fn extract_attributes(path: &Path) -> Result<HashMap<String, String>> {
    let mut attributes = HashMap::new();
    attributes.insert("name".to_owned(),
                      try!(path.file_stem()
                               .and_then(|stem| stem.to_str())
                               .ok_or(format!("Invalid UTF-8 in file stem for {:?}", path)))
                          .to_owned());

    let content = try!(parse_file(path));

    if content.contains("---") {
        let mut content_splits = content.split("---");

        let attribute_string = try!(content_splits.nth(0).ok_or("Empty content"));

        for attribute_line in attribute_string.split("\n") {
            if !attribute_line.contains(':') {
                continue;
            }

            let attribute_split: Vec<&str> = attribute_line.split(':').collect();

            let key = attribute_split[0].trim_matches(' ').to_owned();
            let value = attribute_split[1].trim_matches(' ').to_owned();

            attributes.insert(key, value);
        }
    }

    Ok(attributes)
}

fn extract_content(path: &Path) -> Result<String> {
    let content = try!(parse_file(path));

    if content.contains("---") {
        let mut content_splits = content.split("---");

        return Ok(try!(content_splits.nth(1).ok_or("No content after header")).to_owned());
    }

    return Ok(content);
}
<|endoftext|>


<commit_before>use crossbeam;

use std::sync::Arc;
use std::fs::{self, File};
use std::io::{Read, Write};
use std::path::{Path, PathBuf};
use std::collections::HashMap;
use std::ffi::OsStr;
use liquid::Value;
use walkdir::WalkDir;
use document::Document;
use error::{Error, Result};
use config::Config;
use yaml_rust::YamlLoader;
use chrono::{DateTime, UTC, FixedOffset};
use chrono::offset::TimeZone;
use rss::{Channel, Rss};

macro_rules! walker {
    ($dir:expr) => {
        WalkDir::new($dir)
            .into_iter()
            .filter_map(|e| e.ok())
            .filter(|f| {
                // skip directories
                f.file_type().is_file()
                    &&
                    // don't copy hidden files
                    !f.path()
                    .file_name()
                    .and_then(|name| name.to_str())
                    .unwrap_or(".")
                    .starts_with(".")
            })
    }
}

/// The primary build function that tranforms a directory into a site
pub fn build(config: &Config) -> Result<()> {
    trace!("Build configuration: {:?}", config);

    // join("") makes sure path has a trailing slash
    let source = PathBuf::from(&config.source).join("");
    let source = source.as_path();
    let dest = PathBuf::from(&config.dest).join("");
    let dest = dest.as_path();

    let template_extensions: Vec<&OsStr> = config.template_extensions
                                                .iter()
                                                .map(OsStr::new)
                                                .collect();

    let layouts_path = source.join(&config.layouts);
    let posts_path = source.join(&config.posts);

    debug!("Layouts directory: {:?}", layouts_path);
    debug!("Posts directory: {:?}", posts_path);

    let layouts = try!(get_layouts(&layouts_path));

    let mut documents = vec![];

    for entry in walker!(&source) {
        if template_extensions.contains(&entry.path()
                                              .extension()
                                              .unwrap_or(OsStr::new(""))) &&
           entry.path().parent() != Some(layouts_path.as_path()) {
            let mut doc = try!(parse_document(&entry.path(), &source));

            // if the document is in the posts folder it's considered a post
            if entry.path().parent() == Some(posts_path.as_path()) {
                doc.is_post = true;
            }

            documents.push(doc);
        }
    }

    // January 1, 1970 0:00:00 UTC, the beginning of time
    let default_date = UTC.timestamp(0, 0).with_timezone(&FixedOffset::east(0));

    // sort documents by date, if there's no date (none was provided or it couldn't be read) then
    // fall back to the default date
    documents.sort_by(|a, b| {
        b.date.unwrap_or(default_date.clone()).cmp(&a.date.unwrap_or(default_date.clone()))
    });

    // check if we should create an RSS file and create it!
    if let &Some(ref path) = &config.rss {
        try!(create_rss(path, dest, &config, &documents));
    }

    // these are the attributes of all documents that are posts, so that they can be
    // passed to the renderer
    let post_data: Vec<Value> = documents.iter()
                                         .filter(|x| x.is_post)
                                         .map(|x| Value::Object(x.get_attributes()))
                                         .collect();

    // thread handles to join later
    let mut handles = vec![];

    // generate documents (in parallel)
    // TODO I'm probably underutilizing crossbeam
    crossbeam::scope(|scope| {
        let post_data = Arc::new(post_data);
        let layouts = Arc::new(layouts);
        for doc in &documents {
            trace!("Generating {}", doc.path);
            let post_data = post_data.clone();
            let layouts = layouts.clone();
            let handle = scope.spawn(move || doc.create_file(&dest, &layouts, &post_data));
            handles.push(handle);
        }
    });

    for handle in handles {
        try!(handle.join());
    }

    // copy all remaining files in the source to the destination
    if source != dest {
        info!("Copying remaining assets");
        let source_str = try!(source.to_str()
                                    .ok_or(format!("Cannot convert pathname {:?} to UTF-8",
                                                   source)));

        for entry in walker!(&source).filter(|f| {
            !template_extensions.contains(&f.path()
                                            .extension()
                                            .unwrap_or(OsStr::new(""))) &&
            f.path() != dest && f.path() != layouts_path.as_path()
        }) {
            let entry_path = try!(entry.path()
                                       .to_str()
                                       .ok_or(format!("Cannot convert pathname {:?} to UTF-8",
                                                      entry.path())));

            let relative = try!(entry_path.split(source_str)
                                          .last()
                                          .ok_or(format!("Empty path")));

            if try!(entry.metadata()).is_dir() {
                try!(fs::create_dir_all(&dest.join(relative)));
                debug!("Created new directory {:?}", dest.join(relative));
            } else {
                try!(fs::copy(entry.path(), &dest.join(relative))
                         .map_err(|_| format!("Could not copy {:?}", entry.path())));
                debug!("Copied {:?} to {:?}", entry.path(), dest.join(relative));
            }
        }
    }

    Ok(())
}

/// Gets all layout files from the specified path (usually _layouts/)
/// This walks the specified directory recursively
///
/// Returns a map filename -> content
fn get_layouts(layouts_path: &Path) -> Result<HashMap<String, String>> {
    let mut layouts = HashMap::new();

    // go through the layout directory and add
    // filename -> text content to the layout map
    for entry in walker!(layouts_path) {
        let mut text = String::new();
        let mut file = try!(File::open(entry.path()));
        try!(file.read_to_string(&mut text));

        let path = try!(entry.path()
                             .file_name()
                             .and_then(|name| name.to_str())
                             .ok_or(format!("Cannot convert pathname {:?} to UTF-8",
                                            entry.path().file_name())));

        layouts.insert(path.to_owned(), text);
    }

    Ok(layouts)
}

// creates a new RSS file with the contents of the site blog
fn create_rss(path: &str, dest: &Path, config: &Config, documents: &[Document]) -> Result<()> {
    match (&config.name, &config.description, &config.link) {
        // these three fields are mandatory in the RSS standard
        (&Some(ref name), &Some(ref description), &Some(ref link)) => {
            trace!("Generating RSS data");

            let items = documents.iter()
                                 .filter(|x| x.is_post)
                                 .map(|doc| doc.to_rss(link))
                                 .collect();

            let channel = Channel {
                title: name.to_owned(),
                link: link.to_owned(),
                description: description.to_owned(),
                items: items,
                ..Default::default()
            };

            let rss = Rss(channel);
            let rss_string = rss.to_string();
            trace!("RSS data: {}", rss_string);

            let rss_path = dest.join(path);

            let mut rss_file = try!(File::create(&rss_path));
            try!(rss_file.write_all(&rss_string.into_bytes()));

            info!("Created RSS file at {}", rss_path.display());
            Ok(())
        }
        _ => {
            Err(Error::from("name, description and link need to be defined in the config file to \
                             generate RSS"))
        }
    }
}

fn parse_document(path: &Path, source: &Path) -> Result<Document> {
    let mut attributes = HashMap::new();
    let mut content = try!(parse_file(path));

    // if there is front matter, split the file and parse it
    if content.contains("---") {
        let content2 = content.clone();
        let mut content_splits = content2.split("---");

        // above the split are the attributes
        let attribute_string = content_splits.next().unwrap_or("");

        // everything below the split becomes the new content
        content = content_splits.next().unwrap_or("").to_owned();

        let yaml_result = try!(YamlLoader::load_from_str(attribute_string));

        let yaml_attributes = try!(yaml_result[0]
                                       .as_hash()
                                       .ok_or(format!("Incorrect front matter format in {:?}",
                                                      path)));

        for (key, value) in yaml_attributes {
            // TODO is unwrap_or the best way to handle this?
            attributes.insert(key.as_str().unwrap_or("").to_owned(),
                              value.as_str().unwrap_or("").to_owned());
        }
    }

    let date = attributes.get("date")
                         .and_then(|d| DateTime::parse_from_str(d, "%d %B %Y %H:%M:%S %z").ok());

    let path_str = try!(path.to_str()
                            .ok_or(format!("Cannot convert pathname {:?} to UTF-8", path)));

    let source_str = try!(source.to_str()
                                .ok_or(format!("Cannot convert pathname {:?} to UTF-8", source)));

    let new_path = try!(path_str.split(source_str)
                                .last()
                                .ok_or(format!("Empty path")));

    // construct path
    let mut path_buf = PathBuf::from(new_path);
    path_buf.set_extension("html");

    let path_str = try!(path_buf.to_str()
                                .ok_or(format!("Cannot convert pathname {:?} to UTF-8", path_str)));

    let markdown = path.extension().unwrap_or(OsStr::new("")) == OsStr::new("md");

    let name = try!(path.file_stem()
                        .and_then(|stem| stem.to_str())
                        .ok_or(format!("Invalid UTF-8 in file stem for {:?}", path)));

    Ok(Document::new(name.to_owned(),
                     path_str.to_owned(),
                     attributes,
                     content,
                     false,
                     date,
                     markdown))
}

fn parse_file(path: &Path) -> Result<String> {
    let mut file = try!(File::open(path));
    let mut text = String::new();
    try!(file.read_to_string(&mut text));
    Ok(text)
}
<commit_msg>Crossbeam scoped spawn does not require a 'static lifetime<commit_after>use crossbeam;

use std::fs::{self, File};
use std::io::{Read, Write};
use std::path::{Path, PathBuf};
use std::collections::HashMap;
use std::ffi::OsStr;
use liquid::Value;
use walkdir::WalkDir;
use document::Document;
use error::{Error, Result};
use config::Config;
use yaml_rust::YamlLoader;
use chrono::{DateTime, UTC, FixedOffset};
use chrono::offset::TimeZone;
use rss::{Channel, Rss};

macro_rules! walker {
    ($dir:expr) => {
        WalkDir::new($dir)
            .into_iter()
            .filter_map(|e| e.ok())
            .filter(|f| {
                // skip directories
                f.file_type().is_file()
                    &&
                    // don't copy hidden files
                    !f.path()
                    .file_name()
                    .and_then(|name| name.to_str())
                    .unwrap_or(".")
                    .starts_with(".")
            })
    }
}

/// The primary build function that tranforms a directory into a site
pub fn build(config: &Config) -> Result<()> {
    trace!("Build configuration: {:?}", config);

    // join("") makes sure path has a trailing slash
    let source = PathBuf::from(&config.source).join("");
    let source = source.as_path();
    let dest = PathBuf::from(&config.dest).join("");
    let dest = dest.as_path();

    let template_extensions: Vec<&OsStr> = config.template_extensions
                                                .iter()
                                                .map(OsStr::new)
                                                .collect();

    let layouts_path = source.join(&config.layouts);
    let posts_path = source.join(&config.posts);

    debug!("Layouts directory: {:?}", layouts_path);
    debug!("Posts directory: {:?}", posts_path);

    let layouts = try!(get_layouts(&layouts_path));

    let mut documents = vec![];

    for entry in walker!(&source) {
        if template_extensions.contains(&entry.path()
                                              .extension()
                                              .unwrap_or(OsStr::new(""))) &&
           entry.path().parent() != Some(layouts_path.as_path()) {
            let mut doc = try!(parse_document(&entry.path(), &source));

            // if the document is in the posts folder it's considered a post
            if entry.path().parent() == Some(posts_path.as_path()) {
                doc.is_post = true;
            }

            documents.push(doc);
        }
    }

    // January 1, 1970 0:00:00 UTC, the beginning of time
    let default_date = UTC.timestamp(0, 0).with_timezone(&FixedOffset::east(0));

    // sort documents by date, if there's no date (none was provided or it couldn't be read) then
    // fall back to the default date
    documents.sort_by(|a, b| {
        b.date.unwrap_or(default_date.clone()).cmp(&a.date.unwrap_or(default_date.clone()))
    });

    // check if we should create an RSS file and create it!
    if let &Some(ref path) = &config.rss {
        try!(create_rss(path, dest, &config, &documents));
    }

    // these are the attributes of all documents that are posts, so that they can be
    // passed to the renderer
    let post_data: Vec<Value> = documents.iter()
                                         .filter(|x| x.is_post)
                                         .map(|x| Value::Object(x.get_attributes()))
                                         .collect();

    // thread handles to join later
    let mut handles = vec![];

    // generate documents (in parallel)
    // TODO I'm probably underutilizing crossbeam
    crossbeam::scope(|scope| {
        for doc in &documents {
            trace!("Generating {}", doc.path);
            let post_data = post_data.clone();
            let layouts = layouts.clone();
            let handle = scope.spawn(move || doc.create_file(&dest, &layouts, &post_data));
            handles.push(handle);
        }
    });

    for handle in handles {
        try!(handle.join());
    }

    // copy all remaining files in the source to the destination
    if source != dest {
        info!("Copying remaining assets");
        let source_str = try!(source.to_str()
                                    .ok_or(format!("Cannot convert pathname {:?} to UTF-8",
                                                   source)));

        for entry in walker!(&source).filter(|f| {
            !template_extensions.contains(&f.path()
                                            .extension()
                                            .unwrap_or(OsStr::new(""))) &&
            f.path() != dest && f.path() != layouts_path.as_path()
        }) {
            let entry_path = try!(entry.path()
                                       .to_str()
                                       .ok_or(format!("Cannot convert pathname {:?} to UTF-8",
                                                      entry.path())));

            let relative = try!(entry_path.split(source_str)
                                          .last()
                                          .ok_or(format!("Empty path")));

            if try!(entry.metadata()).is_dir() {
                try!(fs::create_dir_all(&dest.join(relative)));
                debug!("Created new directory {:?}", dest.join(relative));
            } else {
                try!(fs::copy(entry.path(), &dest.join(relative))
                         .map_err(|_| format!("Could not copy {:?}", entry.path())));
                debug!("Copied {:?} to {:?}", entry.path(), dest.join(relative));
            }
        }
    }

    Ok(())
}

/// Gets all layout files from the specified path (usually _layouts/)
/// This walks the specified directory recursively
///
/// Returns a map filename -> content
fn get_layouts(layouts_path: &Path) -> Result<HashMap<String, String>> {
    let mut layouts = HashMap::new();

    // go through the layout directory and add
    // filename -> text content to the layout map
    for entry in walker!(layouts_path) {
        let mut text = String::new();
        let mut file = try!(File::open(entry.path()));
        try!(file.read_to_string(&mut text));

        let path = try!(entry.path()
                             .file_name()
                             .and_then(|name| name.to_str())
                             .ok_or(format!("Cannot convert pathname {:?} to UTF-8",
                                            entry.path().file_name())));

        layouts.insert(path.to_owned(), text);
    }

    Ok(layouts)
}

// creates a new RSS file with the contents of the site blog
fn create_rss(path: &str, dest: &Path, config: &Config, documents: &[Document]) -> Result<()> {
    match (&config.name, &config.description, &config.link) {
        // these three fields are mandatory in the RSS standard
        (&Some(ref name), &Some(ref description), &Some(ref link)) => {
            trace!("Generating RSS data");

            let items = documents.iter()
                                 .filter(|x| x.is_post)
                                 .map(|doc| doc.to_rss(link))
                                 .collect();

            let channel = Channel {
                title: name.to_owned(),
                link: link.to_owned(),
                description: description.to_owned(),
                items: items,
                ..Default::default()
            };

            let rss = Rss(channel);
            let rss_string = rss.to_string();
            trace!("RSS data: {}", rss_string);

            let rss_path = dest.join(path);

            let mut rss_file = try!(File::create(&rss_path));
            try!(rss_file.write_all(&rss_string.into_bytes()));

            info!("Created RSS file at {}", rss_path.display());
            Ok(())
        }
        _ => {
            Err(Error::from("name, description and link need to be defined in the config file to \
                             generate RSS"))
        }
    }
}

fn parse_document(path: &Path, source: &Path) -> Result<Document> {
    let mut attributes = HashMap::new();
    let mut content = try!(parse_file(path));

    // if there is front matter, split the file and parse it
    if content.contains("---") {
        let content2 = content.clone();
        let mut content_splits = content2.split("---");

        // above the split are the attributes
        let attribute_string = content_splits.next().unwrap_or("");

        // everything below the split becomes the new content
        content = content_splits.next().unwrap_or("").to_owned();

        let yaml_result = try!(YamlLoader::load_from_str(attribute_string));

        let yaml_attributes = try!(yaml_result[0]
                                       .as_hash()
                                       .ok_or(format!("Incorrect front matter format in {:?}",
                                                      path)));

        for (key, value) in yaml_attributes {
            // TODO is unwrap_or the best way to handle this?
            attributes.insert(key.as_str().unwrap_or("").to_owned(),
                              value.as_str().unwrap_or("").to_owned());
        }
    }

    let date = attributes.get("date")
                         .and_then(|d| DateTime::parse_from_str(d, "%d %B %Y %H:%M:%S %z").ok());

    let path_str = try!(path.to_str()
                            .ok_or(format!("Cannot convert pathname {:?} to UTF-8", path)));

    let source_str = try!(source.to_str()
                                .ok_or(format!("Cannot convert pathname {:?} to UTF-8", source)));

    let new_path = try!(path_str.split(source_str)
                                .last()
                                .ok_or(format!("Empty path")));

    // construct path
    let mut path_buf = PathBuf::from(new_path);
    path_buf.set_extension("html");

    let path_str = try!(path_buf.to_str()
                                .ok_or(format!("Cannot convert pathname {:?} to UTF-8", path_str)));

    let markdown = path.extension().unwrap_or(OsStr::new("")) == OsStr::new("md");

    let name = try!(path.file_stem()
                        .and_then(|stem| stem.to_str())
                        .ok_or(format!("Invalid UTF-8 in file stem for {:?}", path)));

    Ok(Document::new(name.to_owned(),
                     path_str.to_owned(),
                     attributes,
                     content,
                     false,
                     date,
                     markdown))
}

fn parse_file(path: &Path) -> Result<String> {
    let mut file = try!(File::open(path));
    let mut text = String::new();
    try!(file.read_to_string(&mut text));
    Ok(text)
}
<|endoftext|>