<commit_before>use std::collections::HashMap;

use hyper::Client as HyperClient;
use hyper::client::{IntoUrl, Response};
use hyper::header::ContentType;
use hyper::status::StatusCode;
use rustc_serialize::json::{self, Json, ToJson};
use url::{ParseError, Url};

use error::FleetError;
use schema::{MachinePage, Unit, UnitOption, UnitPage, UnitStatePage, UnitStates};
use serialize::{self, CreateUnit, ModifyUnit};

/// An API client for fleet.
///
/// # Pagination
///
/// Methods that return a collection of resources are paginated. On a successful API call, a
/// special "page" type will be returned. This type contains a vector of values of that resource
/// type and an optional "next page token." If this token is present, it indicates that at least
/// one additional page of resources is available. To make another call for the next page, pass
/// this token in to the API method. When the token value is `None`, the last page has been
/// reached.
///
/// # Failures
///
/// Each method involves making an HTTP request to the fleet API and can possibly result in an
/// error. Errors may occur either due to problems with the HTTP request itself (such as network
/// errors) or because the fleet API returned an explicit error code and message. In the *failures*
/// sections for the methods below, the error conditions that the fleet API itself can return are
/// detailed, but this is not an exhaustive list of reasons a request might fail.
///
/// # Examples
///
/// ```
/// extern crate fleet;
///
/// use fleet::Client;
///
/// let client = Client::new("http://localhost:2999");
///
/// match client.list_units(None) {
///     Ok(unit_page) => {
///         for unit in unit_page.units {
///             println!("{}", unit.name);
///         }
///     },
///     None => println!("No units in fleet!"),
/// };
pub struct Client {
    root_url: String,
}

impl Client {
    /// Constructs a new `Client`.
    ///
    /// `root_url` is a network scheme, hostname or IP address, and optional port where fleetd is
    /// running. This value should not include a path.
    ///
    /// Due to limitations in underlying libraries, Unix domain sockets are not yet supported. On
    /// CoreOS, fleet runs only on a Unix domain socket by default. It can be exposed on a TCP port
    /// by including a systemd drop-in for the `fleet.socket` unit. This can be achieved via
    /// cloud-config by overriding the default `fleet.socket` unit.
    ///
    /// ```yaml
    /// #cloud-config
    ///
    /// ---
    /// coreos:
    ///   units:
    ///     -  name: fleet.socket
    ///        command: start
    ///        drop_ins:
    ///        -  name: 30-ListenStream.conf
    ///           content: |
    ///             [Socket]
    ///             ListenStream=2999
    /// ```
    ///
    /// # Failures
    ///
    /// If the value provided for `root_url` cannot be parsed, a `url::ParseError` will be
    /// returned.
    pub fn new(root_url: &str) -> Result<Client, ParseError> {
        let url = try!(Url::parse(root_url));
        let client = Client {
            root_url: format!("{}{}", url.serialize(), "fleet/v1"),
        };

        Ok(client)
    }

    /// Creates a fleet unit.
    ///
    /// A unit consists of a name, the desired runtime state, and a set of unit options which
    /// comprise the unit's unit file.
    ///
    /// # Failures
    ///
    /// TODO: Document possible API error responses.
    ///
    /// # Examples
    ///
    /// ```
    /// # extern create fleet;
    /// # use fleet::{Client, UnitOption};
    /// # let client = Client::new("http://localhost:2999").unwrap();
    /// let options = vec![
    ///     UnitOption {
    ///         name: "ExecStart".to_string(),
    ///         section: "Service".to_string(),
    ///         value: "/usr/bin/sleep 3000".to_string(),
    ///     },
    /// ];
    ///
    /// client.create_unit("test.service", UnitStates::Launched, options).unwrap();
    /// ```
    pub fn create_unit(
        &self,
        name: &str,
        desired_state: UnitStates,
        options: Vec<UnitOption>
    ) -> Result<(), FleetError> {
        let serializer = CreateUnit {
            desiredState: desired_state.to_json(),
            options: options,
        };

        let url = self.build_url(&format!("/units/{}", name));
        let body = json::encode(&serializer).unwrap();
        let mut response = try!(self.put(url, body));

        match response.status {
            StatusCode::Created | StatusCode::NoContent => Ok(()),
            _ => Err(FleetError::from_hyper_response(&mut response)),
        }
    }

    /// Destroys the unit with the given name.
    ///
    /// # Failures
    ///
    /// TODO: Document possible API error responses.
    ///
    /// # Examples
    ///
    /// ```
    /// # extern crate fleet;
    /// # use fleet::Client;
    /// # let client = Client::new("http://localhost:2999");
    /// client.destroy_unit("test.service").ok().unwrap();
    pub fn destroy_unit(&self, name: &str) -> Result<(), FleetError> {
        let url = self.build_url(&format!("/units/{}", name));
        let mut response = try!(self.delete(&url[..]));

        match response.status {
            StatusCode::NoContent => Ok(()),
            _ => Err(FleetError::from_hyper_response(&mut response)),
        }
    }

    /// Gets a single unit by name.
    ///
    /// # Failures
    ///
    /// TODO: Document possible API error responses.
    ///
    /// # Examples
    ///
    /// ```
    /// # extern crate fleet;
    /// # use fleet::Client;
    /// # let client = Client::new("http://localhost:2999");
    /// client.get_unit("test.service").ok().unwrap();
    /// ```
    pub fn get_unit(&self, name: &str) -> Result<Unit, FleetError> {
        let url = self.build_url(&format!("/units/{}", name));
        let mut response = try!(self.get(&url[..]));

        match response.status {
            StatusCode::Ok => {
                let json = Json::from_reader(&mut response).unwrap();

                Ok(serialize::unit_from_json(&json))
            },
            _ => Err(FleetError::from_hyper_response(&mut response)),
        }
    }

    /// Lists machines in the fleet cluster. This is a paginated resource.
    ///
    /// # Failures
    ///
    /// TODO: Document possible API error responses.
    ///
    /// # Examples
    ///
    /// ```
    /// # extern crate fleet;
    /// # use fleet::Client;
    /// # let client = Client::new("http://localhost:2999");
    /// match client.list_machines(None) {
    ///     Ok(machine_page) {
    ///         for machine in machine_page.machines {
    ///             println!("Machine {}", machine.id);
    ///         }
    ///     },
    ///     None => println!("No machines found"),
    /// };
    /// ```
    pub fn list_machines(
        &self,
        next_page_token: Option<String>,
    ) -> Result<MachinePage, FleetError> {
        let url = self.build_url(&format!("/machines"));
        let mut response = try!(self.get(&url[..]));

        match response.status {
            StatusCode::Ok => {
                let json = Json::from_reader(&mut response).unwrap();

                let machines = match json.find("machines") {
                    Some(machines_json) => {
                        let machines = machines_json.as_array().unwrap();

                        machines.iter().map(|json| {
                            serialize::machine_from_json(json)
                        }).collect()
                    },
                    None => vec![],
                };

                Ok(MachinePage {
                    machines: machines,
                    next_page_token: self.get_next_page_token(&json),
                })
            },
            _ => Err(FleetError::from_hyper_response(&mut response)),
        }
    }

    /// Lists the states of units in the fleet cluster. This is a paginated resource.
    ///
    /// If `Some` values are provided for `machine_id` or `unit_name`, they will be used to filter
    /// the unit states to only those matching the supplied values.
    ///
    /// # Failures
    ///
    /// TODO: Document possible API error responses.
    ///
    /// # Examples
    ///
    /// ```
    /// # extern crate fleet;
    /// # use fleet::Client;
    /// # let client = Client::new("http://localhost:2999");
    /// match client.list_unit_states(None, None, None) {
    ///     Ok(unit_state_page) {
    ///         for state in unit_state_page.states {
    ///             println!("{}: {}", state.name, state.systemd_load_state);
    ///         }
    ///     },
    ///     None => println!("No units found"),
    /// };
    /// ```
    pub fn list_unit_states(
        &self,
        machine_id: Option<&str>,
        unit_name: Option<&str>,
        next_page_token: Option<String>,
    ) -> Result<UnitStatePage, FleetError> {
        let mut query_pairs = HashMap::new();

        if machine_id.is_some() {
            query_pairs.insert("machineID", machine_id.unwrap());
        }

        if unit_name.is_some() {
            query_pairs.insert("unitName", unit_name.unwrap());
        }

        let base_url = self.build_url("/state");
        let mut url = Url::parse(&base_url[..]).unwrap();
        url.set_query_from_pairs(query_pairs.iter().map(|(k, v)| (*k, *v)));
        let mut response = try!(self.get(url));

        match response.status {
            StatusCode::Ok => {
                let json = Json::from_reader(&mut response).unwrap();

                let unit_states = match json.find("states") {
                    Some(unit_states_json) => {
                        let unit_states = unit_states_json.as_array().unwrap();

                        unit_states.iter().map(|json| {
                            serialize::unit_state_from_json(json)
                        }).collect()
                    },
                    None => vec![],
                };

                Ok(UnitStatePage {
                    states: unit_states,
                    next_page_token: self.get_next_page_token(&json),
                })
            },
            _ => Err(FleetError::from_hyper_response(&mut response)),
        }
    }

    /// Lists the units in the fleet cluster. This is a paginated resource.
    ///
    /// # Failures
    ///
    /// TODO: Document possible API error responses.
    ///
    /// # Examples
    ///
    /// ```
    /// # extern crate fleet;
    /// # use fleet::Client;
    /// # let client = Client::new("http://localhost:2999");
    /// match client.list_units(None) {
    ///     Ok(unit_page) {
    ///         for unit in unit_page.units {
    ///             println!("{}: {}", state.name, state.current_state);
    ///         }
    ///     },
    ///     None => println!("No units found"),
    /// };
    /// ```
    pub fn list_units(&self, next_page_token: Option<String>) -> Result<UnitPage, FleetError> {
        let url = self.build_url("/units");
        let mut response = try!(self.get(&url[..]));

        match response.status {
            StatusCode::Ok => {
                let json = Json::from_reader(&mut response).unwrap();
                let units_json = json.find("units").unwrap().as_array().unwrap();
                let units = units_json.iter().map(|json| serialize::unit_from_json(json)).collect();

                Ok(UnitPage {
                    units: units,
                    next_page_token: self.get_next_page_token(&json),
                })
            },
            _ => Err(FleetError::from_hyper_response(&mut response)),
        }
    }

    /// Modifies a unit, instructing fleetd to move the unit to a new state.
    ///
    /// # Failures
    ///
    /// TODO: Document possible API error responses.
    ///
    /// # Examples
    ///
    /// ```
    /// # extern crate fleet;
    /// # use fleet::{Client, UnitStates};
    /// # let client = Client::new("http://localhost:2999");
    /// client.modify_unit("test.service", UnitStates::Loaded).ok().unwrap();
    /// ```
    pub fn modify_unit(
        &self,
        name: &str,
        desired_state: UnitStates
    ) -> Result<(), FleetError> {
        let serializer = ModifyUnit {
            desiredState: desired_state.to_json(),
        };

        let url = self.build_url(&format!("/units/{}", name));
        let body = json::encode(&serializer).unwrap();
        let mut response = try!(self.put(url, body));

        match response.status {
            StatusCode::Created | StatusCode::NoContent => Ok(()),
            _ => Err(FleetError::from_hyper_response(&mut response)),
        }
    }

    // Private

    fn build_url(&self, path: &str) -> String {
        format!("{}{}", self.root_url, path)
    }

    fn delete(&self, url: &str) -> Result<Response, FleetError> {
        let mut client = HyperClient::new();
        let content_type: ContentType = ContentType("application/json".parse().unwrap());

        match client.delete(url).header(content_type).send() {
            Ok(response) => Ok(response),
            Err(error) => Err(FleetError::from_hyper_error(&error)),
        }
    }

    fn get<U: IntoUrl>(&self, url: U) -> Result<Response, FleetError> {
        let mut client = HyperClient::new();
        let content_type: ContentType = ContentType("application/json".parse().unwrap());

        match client.get(url).header(content_type).send() {
            Ok(response) => Ok(response),
            Err(error) => Err(FleetError::from_hyper_error(&error)),
        }
    }

    fn get_next_page_token(&self, json: &Json) -> Option<String> {
        match json.find("nextPageToken") {
            Some(next_page_token_json) => match next_page_token_json.as_string() {
                Some(next_page_token) => Some(next_page_token.to_string()),
                None => None,
            },
            None => None,
        }
    }

    fn put(&self, url: String, body: String) -> Result<Response, FleetError> {
        let mut client = HyperClient::new();
        let content_type: ContentType = ContentType("application/json".parse().unwrap());

        match client.put(&url[..]).header(content_type).body(&body[..]).send() {
            Ok(response) => Ok(response),
            Err(error) => Err(FleetError::from_hyper_error(&error)),
        }
    }
}

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

    #[test]
    fn it_can_be_constructed() {
       Client::new("http://localhost").unwrap();
    }

    #[test]
    fn it_returns_an_error_for_invalid_root_urls() {
        assert!(Client::new("asdf").is_err());
    }
}
<commit_msg>Expand general description of the Client type.<commit_after>use std::collections::HashMap;

use hyper::Client as HyperClient;
use hyper::client::{IntoUrl, Response};
use hyper::header::ContentType;
use hyper::status::StatusCode;
use rustc_serialize::json::{self, Json, ToJson};
use url::{ParseError, Url};

use error::FleetError;
use schema::{MachinePage, Unit, UnitOption, UnitPage, UnitStatePage, UnitStates};
use serialize::{self, CreateUnit, ModifyUnit};

/// An API client for fleet.
///
/// Provides methods for basic CRUD operations on fleet units as well as for listing machines in the
/// fleet cluster.
///
/// # Pagination
///
/// Methods that return a collection of resources are paginated. On a successful API call, a
/// special "page" type will be returned. This type contains a vector of values of that resource
/// type and an optional "next page token." If this token is present, it indicates that at least
/// one additional page of resources is available. To make another call for the next page, pass
/// this token in to the API method. When the token value is `None`, the last page has been
/// reached.
///
/// # Failures
///
/// Each method involves making an HTTP request to the fleet API and can possibly result in an
/// error. Errors may occur either due to problems with the HTTP request itself (such as network
/// errors) or because the fleet API returned an explicit error code and message. In the *failures*
/// sections for the methods below, the error conditions that the fleet API itself can return are
/// detailed, but this is not an exhaustive list of reasons a request might fail.
///
/// # Examples
///
/// ```
/// extern crate fleet;
///
/// use fleet::Client;
///
/// let client = Client::new("http://localhost:2999");
///
/// match client.list_units(None) {
///     Ok(unit_page) => {
///         for unit in unit_page.units {
///             println!("{}", unit.name);
///         }
///     },
///     None => println!("No units in fleet!"),
/// };
pub struct Client {
    root_url: String,
}

impl Client {
    /// Constructs a new `Client`.
    ///
    /// `root_url` is a network scheme, hostname or IP address, and optional port where fleetd is
    /// running. This value should not include a path.
    ///
    /// Due to limitations in underlying libraries, Unix domain sockets are not yet supported. On
    /// CoreOS, fleet runs only on a Unix domain socket by default. It can be exposed on a TCP port
    /// by including a systemd drop-in for the `fleet.socket` unit. This can be achieved via
    /// cloud-config by overriding the default `fleet.socket` unit.
    ///
    /// ```yaml
    /// #cloud-config
    ///
    /// ---
    /// coreos:
    ///   units:
    ///     -  name: fleet.socket
    ///        command: start
    ///        drop_ins:
    ///        -  name: 30-ListenStream.conf
    ///           content: |
    ///             [Socket]
    ///             ListenStream=2999
    /// ```
    ///
    /// # Failures
    ///
    /// If the value provided for `root_url` cannot be parsed, a `url::ParseError` will be
    /// returned.
    pub fn new(root_url: &str) -> Result<Client, ParseError> {
        let url = try!(Url::parse(root_url));
        let client = Client {
            root_url: format!("{}{}", url.serialize(), "fleet/v1"),
        };

        Ok(client)
    }

    /// Creates a fleet unit.
    ///
    /// A unit consists of a name, the desired runtime state, and a set of unit options which
    /// comprise the unit's unit file.
    ///
    /// # Failures
    ///
    /// TODO: Document possible API error responses.
    ///
    /// # Examples
    ///
    /// ```
    /// # extern create fleet;
    /// # use fleet::{Client, UnitOption};
    /// # let client = Client::new("http://localhost:2999").unwrap();
    /// let options = vec![
    ///     UnitOption {
    ///         name: "ExecStart".to_string(),
    ///         section: "Service".to_string(),
    ///         value: "/usr/bin/sleep 3000".to_string(),
    ///     },
    /// ];
    ///
    /// client.create_unit("test.service", UnitStates::Launched, options).unwrap();
    /// ```
    pub fn create_unit(
        &self,
        name: &str,
        desired_state: UnitStates,
        options: Vec<UnitOption>
    ) -> Result<(), FleetError> {
        let serializer = CreateUnit {
            desiredState: desired_state.to_json(),
            options: options,
        };

        let url = self.build_url(&format!("/units/{}", name));
        let body = json::encode(&serializer).unwrap();
        let mut response = try!(self.put(url, body));

        match response.status {
            StatusCode::Created | StatusCode::NoContent => Ok(()),
            _ => Err(FleetError::from_hyper_response(&mut response)),
        }
    }

    /// Destroys the unit with the given name.
    ///
    /// # Failures
    ///
    /// TODO: Document possible API error responses.
    ///
    /// # Examples
    ///
    /// ```
    /// # extern crate fleet;
    /// # use fleet::Client;
    /// # let client = Client::new("http://localhost:2999");
    /// client.destroy_unit("test.service").ok().unwrap();
    pub fn destroy_unit(&self, name: &str) -> Result<(), FleetError> {
        let url = self.build_url(&format!("/units/{}", name));
        let mut response = try!(self.delete(&url[..]));

        match response.status {
            StatusCode::NoContent => Ok(()),
            _ => Err(FleetError::from_hyper_response(&mut response)),
        }
    }

    /// Gets a single unit by name.
    ///
    /// # Failures
    ///
    /// TODO: Document possible API error responses.
    ///
    /// # Examples
    ///
    /// ```
    /// # extern crate fleet;
    /// # use fleet::Client;
    /// # let client = Client::new("http://localhost:2999");
    /// client.get_unit("test.service").ok().unwrap();
    /// ```
    pub fn get_unit(&self, name: &str) -> Result<Unit, FleetError> {
        let url = self.build_url(&format!("/units/{}", name));
        let mut response = try!(self.get(&url[..]));

        match response.status {
            StatusCode::Ok => {
                let json = Json::from_reader(&mut response).unwrap();

                Ok(serialize::unit_from_json(&json))
            },
            _ => Err(FleetError::from_hyper_response(&mut response)),
        }
    }

    /// Lists machines in the fleet cluster. This is a paginated resource.
    ///
    /// # Failures
    ///
    /// TODO: Document possible API error responses.
    ///
    /// # Examples
    ///
    /// ```
    /// # extern crate fleet;
    /// # use fleet::Client;
    /// # let client = Client::new("http://localhost:2999");
    /// match client.list_machines(None) {
    ///     Ok(machine_page) {
    ///         for machine in machine_page.machines {
    ///             println!("Machine {}", machine.id);
    ///         }
    ///     },
    ///     None => println!("No machines found"),
    /// };
    /// ```
    pub fn list_machines(
        &self,
        next_page_token: Option<String>,
    ) -> Result<MachinePage, FleetError> {
        let url = self.build_url(&format!("/machines"));
        let mut response = try!(self.get(&url[..]));

        match response.status {
            StatusCode::Ok => {
                let json = Json::from_reader(&mut response).unwrap();

                let machines = match json.find("machines") {
                    Some(machines_json) => {
                        let machines = machines_json.as_array().unwrap();

                        machines.iter().map(|json| {
                            serialize::machine_from_json(json)
                        }).collect()
                    },
                    None => vec![],
                };

                Ok(MachinePage {
                    machines: machines,
                    next_page_token: self.get_next_page_token(&json),
                })
            },
            _ => Err(FleetError::from_hyper_response(&mut response)),
        }
    }

    /// Lists the states of units in the fleet cluster. This is a paginated resource.
    ///
    /// If `Some` values are provided for `machine_id` or `unit_name`, they will be used to filter
    /// the unit states to only those matching the supplied values.
    ///
    /// # Failures
    ///
    /// TODO: Document possible API error responses.
    ///
    /// # Examples
    ///
    /// ```
    /// # extern crate fleet;
    /// # use fleet::Client;
    /// # let client = Client::new("http://localhost:2999");
    /// match client.list_unit_states(None, None, None) {
    ///     Ok(unit_state_page) {
    ///         for state in unit_state_page.states {
    ///             println!("{}: {}", state.name, state.systemd_load_state);
    ///         }
    ///     },
    ///     None => println!("No units found"),
    /// };
    /// ```
    pub fn list_unit_states(
        &self,
        machine_id: Option<&str>,
        unit_name: Option<&str>,
        next_page_token: Option<String>,
    ) -> Result<UnitStatePage, FleetError> {
        let mut query_pairs = HashMap::new();

        if machine_id.is_some() {
            query_pairs.insert("machineID", machine_id.unwrap());
        }

        if unit_name.is_some() {
            query_pairs.insert("unitName", unit_name.unwrap());
        }

        let base_url = self.build_url("/state");
        let mut url = Url::parse(&base_url[..]).unwrap();
        url.set_query_from_pairs(query_pairs.iter().map(|(k, v)| (*k, *v)));
        let mut response = try!(self.get(url));

        match response.status {
            StatusCode::Ok => {
                let json = Json::from_reader(&mut response).unwrap();

                let unit_states = match json.find("states") {
                    Some(unit_states_json) => {
                        let unit_states = unit_states_json.as_array().unwrap();

                        unit_states.iter().map(|json| {
                            serialize::unit_state_from_json(json)
                        }).collect()
                    },
                    None => vec![],
                };

                Ok(UnitStatePage {
                    states: unit_states,
                    next_page_token: self.get_next_page_token(&json),
                })
            },
            _ => Err(FleetError::from_hyper_response(&mut response)),
        }
    }

    /// Lists the units in the fleet cluster. This is a paginated resource.
    ///
    /// # Failures
    ///
    /// TODO: Document possible API error responses.
    ///
    /// # Examples
    ///
    /// ```
    /// # extern crate fleet;
    /// # use fleet::Client;
    /// # let client = Client::new("http://localhost:2999");
    /// match client.list_units(None) {
    ///     Ok(unit_page) {
    ///         for unit in unit_page.units {
    ///             println!("{}: {}", state.name, state.current_state);
    ///         }
    ///     },
    ///     None => println!("No units found"),
    /// };
    /// ```
    pub fn list_units(&self, next_page_token: Option<String>) -> Result<UnitPage, FleetError> {
        let url = self.build_url("/units");
        let mut response = try!(self.get(&url[..]));

        match response.status {
            StatusCode::Ok => {
                let json = Json::from_reader(&mut response).unwrap();
                let units_json = json.find("units").unwrap().as_array().unwrap();
                let units = units_json.iter().map(|json| serialize::unit_from_json(json)).collect();

                Ok(UnitPage {
                    units: units,
                    next_page_token: self.get_next_page_token(&json),
                })
            },
            _ => Err(FleetError::from_hyper_response(&mut response)),
        }
    }

    /// Modifies a unit, instructing fleetd to move the unit to a new state.
    ///
    /// # Failures
    ///
    /// TODO: Document possible API error responses.
    ///
    /// # Examples
    ///
    /// ```
    /// # extern crate fleet;
    /// # use fleet::{Client, UnitStates};
    /// # let client = Client::new("http://localhost:2999");
    /// client.modify_unit("test.service", UnitStates::Loaded).ok().unwrap();
    /// ```
    pub fn modify_unit(
        &self,
        name: &str,
        desired_state: UnitStates
    ) -> Result<(), FleetError> {
        let serializer = ModifyUnit {
            desiredState: desired_state.to_json(),
        };

        let url = self.build_url(&format!("/units/{}", name));
        let body = json::encode(&serializer).unwrap();
        let mut response = try!(self.put(url, body));

        match response.status {
            StatusCode::Created | StatusCode::NoContent => Ok(()),
            _ => Err(FleetError::from_hyper_response(&mut response)),
        }
    }

    // Private

    fn build_url(&self, path: &str) -> String {
        format!("{}{}", self.root_url, path)
    }

    fn delete(&self, url: &str) -> Result<Response, FleetError> {
        let mut client = HyperClient::new();
        let content_type: ContentType = ContentType("application/json".parse().unwrap());

        match client.delete(url).header(content_type).send() {
            Ok(response) => Ok(response),
            Err(error) => Err(FleetError::from_hyper_error(&error)),
        }
    }

    fn get<U: IntoUrl>(&self, url: U) -> Result<Response, FleetError> {
        let mut client = HyperClient::new();
        let content_type: ContentType = ContentType("application/json".parse().unwrap());

        match client.get(url).header(content_type).send() {
            Ok(response) => Ok(response),
            Err(error) => Err(FleetError::from_hyper_error(&error)),
        }
    }

    fn get_next_page_token(&self, json: &Json) -> Option<String> {
        match json.find("nextPageToken") {
            Some(next_page_token_json) => match next_page_token_json.as_string() {
                Some(next_page_token) => Some(next_page_token.to_string()),
                None => None,
            },
            None => None,
        }
    }

    fn put(&self, url: String, body: String) -> Result<Response, FleetError> {
        let mut client = HyperClient::new();
        let content_type: ContentType = ContentType("application/json".parse().unwrap());

        match client.put(&url[..]).header(content_type).body(&body[..]).send() {
            Ok(response) => Ok(response),
            Err(error) => Err(FleetError::from_hyper_error(&error)),
        }
    }
}

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

    #[test]
    fn it_can_be_constructed() {
       Client::new("http://localhost").unwrap();
    }

    #[test]
    fn it_returns_an_error_for_invalid_root_urls() {
        assert!(Client::new("asdf").is_err());
    }
}
<|endoftext|>


<commit_before>use object_id::ObjectId;
use object_header::ObjectHeader;
use object_header;
use object_type;
use meta::Meta;
use serialization::Serializable;
use serialization;
use reader::Reader;
use error::GitError;
use error::CorruptCommit;
use error::NotFound;
use repository::Repository;
use commit_filter::CommitFilter;
use object_database;
use eobject::ECommit;
use commit_sort_strategy::MostRecent;
use extensions;
use std::collections::HashSet;

#[deriving(PartialEq, Show, Clone)]
pub struct Commit {
    pub meta: Meta,
    pub author_name: String,
    pub author_email: String,
    pub author_date: uint,
    pub committer_name: String,
    pub committer_email: String,
    pub commit_date: uint,
    pub message: String,
    pub tree_id: ObjectId,
    pub parent_ids: Vec<ObjectId>,
}

impl Serializable for Commit {
    fn encode(&self) -> Vec<u8> {
        let mut buff = Vec::new();
        let mut header = self.meta.header;

        let body_buff = self.encode_body();

        // Update header length if necessary.
        if header.length == 0 {
            header = ObjectHeader { length: body_buff.len(), ..header };
        }

        let header_buff = header.encode();

        buff.push_all(header_buff.as_slice());
        buff.push_all(body_buff.as_slice());

        buff
    }

    fn encode_body(&self) -> Vec<u8> {
        let mut buff = Vec::new();

        buff.push_all(format!("tree {}\n", self.tree_id.hash).into_bytes().as_slice());

        for id in self.parent_ids.iter() {
            buff.push_all(format!("parent {}\n", id.hash).into_bytes().as_slice());
        }

        buff.push_all(serialization::encode_author_info(self).as_slice());
        buff.push_all(serialization::encode_commit_info(self).as_slice());

        buff.push_all(format!("\n{}", self.message).into_bytes().as_slice());

        buff
    }
}

pub fn decode(bytes: &[u8]) -> Result<Commit, GitError> {
    let header = object_header::decode(bytes);

    decode_body(bytes.slice_from(bytes.len() - header.length), &header)
}

pub fn decode_body(bytes: &[u8], header: &ObjectHeader) -> Result<Commit, GitError> {
    let mut reader = Reader::from_data(bytes);

    if reader.take_string(5) != "tree " {
        return Err(CorruptCommit("Expected 'tree '"))
    }

    let tree_id = reader.take_string_based_object_id();

    reader.skip(1);

    let mut parent_ids = Vec::new();

    while reader.take_string_while(|&c| c != 32) == "parent" {
        reader.skip(1);

        parent_ids.push(reader.take_string_based_object_id());

        reader.skip(1);
    }

    reader.back(6);

    if reader.take_string(7) != "author " {
        return Err(CorruptCommit("Expected 'author '"))
    }

    let (author_name, author_email, author_date) = serialization::decode_user_info(&mut reader);

    if reader.take_string(10) != "committer " {
        return Err(CorruptCommit("Expected 'committer '"))
    }

    let (committer_name, committer_email, commit_date) = serialization::decode_user_info(&mut reader);

    reader.skip(1);

    let message = reader.get_rest_as_string();

    Ok(Commit {
        meta: Meta {
            id: ObjectId::from_string("b744d5cddb5095249299d95ee531cbd990741140"),
            header: *header,
        },
        author_name: author_name,
        author_email: author_email,
        author_date: author_date,
        committer_name: committer_name,
        committer_email: committer_email,
        commit_date: commit_date,
        message: message.into_string(),
        tree_id: tree_id,
        parent_ids: parent_ids,
    })
}

pub fn find(repository: &Repository, filter: CommitFilter) -> Vec<Commit> {
    let mut buffer = Vec::new();

    let since_ids = match filter {
        CommitFilter { since: Some(since), .. } => since,
        _ => Vec::new(),
    };

    fn find_commit(id: &ObjectId, repository: &Repository) -> Result<Commit, GitError> {
        match object_database::find_object_by_id(repository, id) {
            Ok(box ECommit(c)) => Ok(c),
            Err(e) => Err(e),
            _ => Err(NotFound),
        }
    };

    match filter.sort {
        MostRecent => {
            let mut commits: Vec<Commit> = since_ids.iter().map(|id| find_commit(id, repository).unwrap()).collect();

            loop {
                if commits.len() == 0 || buffer.len() == filter.limit {
                    break;
                }

                let most_recent: Commit = extensions::max_by(commits.as_slice(), |c| c.commit_date).unwrap();

                buffer.push(most_recent.clone());

                let index = commits.iter().position(|c| *c == most_recent).unwrap();
                commits.remove(index);

                for id in most_recent.parent_ids.move_iter() {
                    let commit = find_commit(&id, repository).unwrap();
                    if !commits.contains(&commit) {
                        commits.push(commit);
                    }
                }
            }

            buffer
        },
        _ => buffer,
    }
}<commit_msg>Refactor commit finding to use map and filter<commit_after>use object_id::ObjectId;
use object_header::ObjectHeader;
use object_header;
use object_type;
use meta::Meta;
use serialization::Serializable;
use serialization;
use reader::Reader;
use error::GitError;
use error::CorruptCommit;
use error::NotFound;
use repository::Repository;
use commit_filter::CommitFilter;
use object_database;
use eobject::ECommit;
use commit_sort_strategy::MostRecent;
use extensions;
use std::collections::HashSet;

#[deriving(PartialEq, Show, Clone)]
pub struct Commit {
    pub meta: Meta,
    pub author_name: String,
    pub author_email: String,
    pub author_date: uint,
    pub committer_name: String,
    pub committer_email: String,
    pub commit_date: uint,
    pub message: String,
    pub tree_id: ObjectId,
    pub parent_ids: Vec<ObjectId>,
}

impl Serializable for Commit {
    fn encode(&self) -> Vec<u8> {
        let mut buff = Vec::new();
        let mut header = self.meta.header;

        let body_buff = self.encode_body();

        // Update header length if necessary.
        if header.length == 0 {
            header = ObjectHeader { length: body_buff.len(), ..header };
        }

        let header_buff = header.encode();

        buff.push_all(header_buff.as_slice());
        buff.push_all(body_buff.as_slice());

        buff
    }

    fn encode_body(&self) -> Vec<u8> {
        let mut buff = Vec::new();

        buff.push_all(format!("tree {}\n", self.tree_id.hash).into_bytes().as_slice());

        for id in self.parent_ids.iter() {
            buff.push_all(format!("parent {}\n", id.hash).into_bytes().as_slice());
        }

        buff.push_all(serialization::encode_author_info(self).as_slice());
        buff.push_all(serialization::encode_commit_info(self).as_slice());

        buff.push_all(format!("\n{}", self.message).into_bytes().as_slice());

        buff
    }
}

pub fn decode(bytes: &[u8]) -> Result<Commit, GitError> {
    let header = object_header::decode(bytes);

    decode_body(bytes.slice_from(bytes.len() - header.length), &header)
}

pub fn decode_body(bytes: &[u8], header: &ObjectHeader) -> Result<Commit, GitError> {
    let mut reader = Reader::from_data(bytes);

    if reader.take_string(5) != "tree " {
        return Err(CorruptCommit("Expected 'tree '"))
    }

    let tree_id = reader.take_string_based_object_id();

    reader.skip(1);

    let mut parent_ids = Vec::new();

    while reader.take_string_while(|&c| c != 32) == "parent" {
        reader.skip(1);

        parent_ids.push(reader.take_string_based_object_id());

        reader.skip(1);
    }

    reader.back(6);

    if reader.take_string(7) != "author " {
        return Err(CorruptCommit("Expected 'author '"))
    }

    let (author_name, author_email, author_date) = serialization::decode_user_info(&mut reader);

    if reader.take_string(10) != "committer " {
        return Err(CorruptCommit("Expected 'committer '"))
    }

    let (committer_name, committer_email, commit_date) = serialization::decode_user_info(&mut reader);

    reader.skip(1);

    let message = reader.get_rest_as_string();

    Ok(Commit {
        meta: Meta {
            id: ObjectId::from_string("b744d5cddb5095249299d95ee531cbd990741140"),
            header: *header,
        },
        author_name: author_name,
        author_email: author_email,
        author_date: author_date,
        committer_name: committer_name,
        committer_email: committer_email,
        commit_date: commit_date,
        message: message.into_string(),
        tree_id: tree_id,
        parent_ids: parent_ids,
    })
}

pub fn find(repository: &Repository, filter: CommitFilter) -> Vec<Commit> {
    let mut buffer = Vec::new();

    let since_ids = match filter {
        CommitFilter { since: Some(since), .. } => since,
        _ => Vec::new(),
    };

    fn find_commit(id: &ObjectId, repository: &Repository) -> Result<Commit, GitError> {
        match object_database::find_object_by_id(repository, id) {
            Ok(box ECommit(c)) => Ok(c),
            Err(e) => Err(e),
            _ => Err(NotFound),
        }
    };

    match filter.sort {
        MostRecent => {
            let mut commits: Vec<Commit> = since_ids.iter().map(|id| find_commit(id, repository).unwrap()).collect();

            loop {
                if commits.len() == 0 || buffer.len() == filter.limit {
                    break;
                }

                let most_recent: Commit = extensions::max_by(commits.as_slice(), |c| c.commit_date).unwrap();

                buffer.push(most_recent.clone());

                let index = commits.iter().position(|c| *c == most_recent).unwrap();
                commits.remove(index);

                let add_parent_commits = || {
                    let parent_commits: Vec<Commit> = most_recent.parent_ids.iter()
                        .filter(|id| !commits.iter().any(|c| c.meta.id == **id))
                        .map(|id| find_commit(id, repository).unwrap())
                        .collect();

                    commits.push_all(parent_commits.as_slice());
                };

                add_parent_commits();
            }

            buffer
        },
        _ => buffer,
    }
}<|endoftext|>


<commit_before>// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

extern crate toml;

use file_lines::FileLines;
use lists::{SeparatorTactic, ListTactic};

macro_rules! configuration_option_enum{
    ($e:ident: $( $x:ident ),+ $(,)*) => {
        #[derive(Copy, Clone, Eq, PartialEq, Debug)]
        pub enum $e {
            $( $x ),+
        }

        impl_enum_decodable!($e, $( $x ),+);
    }
}

configuration_option_enum! { Style:
    Rfc, // Follow the style RFCs style.
    Default, // Follow the traditional Rustfmt style.
}

configuration_option_enum! { NewlineStyle:
    Windows, // \r\n
    Unix, // \n
    Native, // \r\n in Windows, \n on other platforms
}

configuration_option_enum! { BraceStyle:
    AlwaysNextLine,
    PreferSameLine,
    // Prefer same line except where there is a where clause, in which case force
    // the brace to the next line.
    SameLineWhere,
}

configuration_option_enum! { ControlBraceStyle:
    // K&R style, Rust community default
    AlwaysSameLine,
    // Stroustrup style
    ClosingNextLine,
    // Allman style
    AlwaysNextLine,
}

// How to indent a function's return type.
configuration_option_enum! { ReturnIndent:
    // Aligned with the arguments
    WithArgs,
    // Aligned with the where clause
    WithWhereClause,
}

configuration_option_enum! { IndentStyle:
    // First line on the same line as the opening brace, all lines aligned with
    // the first line.
    Visual,
    // First line is on a new line and all lines align with block indent.
    Block,
}

configuration_option_enum! { Density:
    // Fit as much on one line as possible.
    Compressed,
    // Use more lines.
    Tall,
    // Try to compress if the body is empty.
    CompressedIfEmpty,
    // Place every item on a separate line.
    Vertical,
}

configuration_option_enum! { TypeDensity:
    // No spaces around "=" and "+"
    Compressed,
    // Spaces around " = " and " + "
    Wide,
}

impl Density {
    pub fn to_list_tactic(self) -> ListTactic {
        match self {
            Density::Compressed => ListTactic::Mixed,
            Density::Tall |
            Density::CompressedIfEmpty => ListTactic::HorizontalVertical,
            Density::Vertical => ListTactic::Vertical,
        }
    }
}

configuration_option_enum! { LicensePolicy:
    // Do not place license text at top of files
    NoLicense,
    // Use the text in "license" field as the license
    TextLicense,
    // Use a text file as the license text
    FileLicense,
}

configuration_option_enum! { MultilineStyle:
    // Use horizontal layout if it fits in one line, fall back to vertical
    PreferSingle,
    // Use vertical layout
    ForceMulti,
}

impl MultilineStyle {
    pub fn to_list_tactic(self) -> ListTactic {
        match self {
            MultilineStyle::PreferSingle => ListTactic::HorizontalVertical,
            MultilineStyle::ForceMulti => ListTactic::Vertical,
        }
    }
}

configuration_option_enum! { ReportTactic:
    Always,
    Unnumbered,
    Never,
}

configuration_option_enum! { WriteMode:
    // Backs the original file up and overwrites the original.
    Replace,
    // Overwrites original file without backup.
    Overwrite,
    // Writes the output to stdout.
    Display,
    // Writes the diff to stdout.
    Diff,
    // Displays how much of the input file was processed
    Coverage,
    // Unfancy stdout
    Plain,
    // Outputs a checkstyle XML file.
    Checkstyle,
}

/// Trait for types that can be used in `Config`.
pub trait ConfigType: Sized {
    /// Returns hint text for use in `Config::print_docs()`. For enum types, this is a
    /// pipe-separated list of variants; for other types it returns "<type>".
    fn doc_hint() -> String;
}

impl ConfigType for bool {
    fn doc_hint() -> String {
        String::from("<boolean>")
    }
}

impl ConfigType for usize {
    fn doc_hint() -> String {
        String::from("<unsigned integer>")
    }
}

impl ConfigType for isize {
    fn doc_hint() -> String {
        String::from("<signed integer>")
    }
}

impl ConfigType for String {
    fn doc_hint() -> String {
        String::from("<string>")
    }
}

impl ConfigType for FileLines {
    fn doc_hint() -> String {
        String::from("<json>")
    }
}

pub struct ConfigHelpItem {
    option_name: &'static str,
    doc_string: &'static str,
    variant_names: String,
    default: &'static str,
}

impl ConfigHelpItem {
    pub fn option_name(&self) -> &'static str {
        self.option_name
    }

    pub fn doc_string(&self) -> &'static str {
        self.doc_string
    }

    pub fn variant_names(&self) -> &String {
        &self.variant_names
    }

    pub fn default(&self) -> &'static str {
        self.default
    }
}

macro_rules! create_config {
    ($($i:ident: $ty:ty, $def:expr, $( $dstring:expr ),+ );+ $(;)*) => (
        #[derive(RustcDecodable, Clone)]
        pub struct Config {
            $(pub $i: $ty),+
        }

        // Just like the Config struct but with each property wrapped
        // as Option<T>. This is used to parse a rustfmt.toml that doesn't
        // specity all properties of `Config`.
        // We first parse into `ParsedConfig`, then create a default `Config`
        // and overwrite the properties with corresponding values from `ParsedConfig`
        #[derive(RustcDecodable, Clone)]
        pub struct ParsedConfig {
            $(pub $i: Option<$ty>),+
        }

        impl Config {

            fn fill_from_parsed_config(mut self, parsed: ParsedConfig) -> Config {
            $(
                if let Some(val) = parsed.$i {
                    self.$i = val;
                }
            )+
                self
            }

            pub fn from_toml(toml: &str) -> Result<Config, String> {
                let parsed: toml::Value = toml.parse().expect("Could not parse TOML");
                let mut err: String = String::new();
                for (key, _) in parsed.as_table().expect("Parsed config was not table") {
                    match &**key {
                        $(
                            stringify!($i) => (),
                        )+
                        _ => {
                            let msg = &format!("Warning: Unknown configuration option `{}`\n", key);
                            err.push_str(msg)
                        }
                    }
                }
                match toml::decode(parsed) {
                    Some(parsed_config) =>
                        Ok(Config::default().fill_from_parsed_config(parsed_config)),
                    None => {
                        err.push_str("Error: Decoding config file failed. ");
                        err.push_str("Please check your config file.\n");
                        Err(err)
                    }
                }
            }

            pub fn override_value(&mut self, key: &str, val: &str) {
                match key {
                    $(
                        stringify!($i) => {
                            self.$i = val.parse::<$ty>()
                                .expect(&format!("Failed to parse override for {} (\"{}\") as a {}",
                                                 stringify!($i),
                                                 val,
                                                 stringify!($ty)));
                        }
                    )+
                    _ => panic!("Unknown config key in override: {}", key)
                }
            }

            pub fn print_docs() {
                use std::cmp;
                let max = 0;
                $( let max = cmp::max(max, stringify!($i).len()+1); )+
                let mut space_str = String::with_capacity(max);
                for _ in 0..max {
                    space_str.push(' ');
                }
                println!("Configuration Options:");
                $(
                    let name_raw = stringify!($i);
                    let mut name_out = String::with_capacity(max);
                    for _ in name_raw.len()..max-1 {
                        name_out.push(' ')
                    }
                    name_out.push_str(name_raw);
                    name_out.push(' ');
                    println!("{}{} Default: {:?}",
                             name_out,
                             <$ty>::doc_hint(),
                             $def);
                    $(
                        println!("{}{}", space_str, $dstring);
                    )+
                    println!("");
                )+
            }
        }

        // Template for the default configuration
        impl Default for Config {
            fn default() -> Config {
                Config {
                    $(
                        $i: $def,
                    )+
                }
            }
        }
    )
}

create_config! {
    verbose: bool, false, "Use verbose output";
    disable_all_formatting: bool, false, "Don't reformat anything";
    skip_children: bool, false, "Don't reformat out of line modules";
    file_lines: FileLines, FileLines::all(),
        "Lines to format; this is not supported in rustfmt.toml, and can only be specified \
         via the --file-lines option";
    max_width: usize, 100, "Maximum width of each line";
    error_on_line_overflow: bool, true, "Error if unable to get all lines within max_width";
    tab_spaces: usize, 4, "Number of spaces per tab";
    fn_call_width: usize, 60,
        "Maximum width of the args of a function call before falling back to vertical formatting";
    struct_lit_width: usize, 18,
        "Maximum width in the body of a struct lit before falling back to vertical formatting";
    struct_variant_width: usize, 35,
        "Maximum width in the body of a struct variant before falling back to vertical formatting";
    force_explicit_abi: bool, true, "Always print the abi for extern items";
    newline_style: NewlineStyle, NewlineStyle::Unix, "Unix or Windows line endings";
    fn_brace_style: BraceStyle, BraceStyle::SameLineWhere, "Brace style for functions";
    item_brace_style: BraceStyle, BraceStyle::SameLineWhere, "Brace style for structs and enums";
    control_brace_style: ControlBraceStyle, ControlBraceStyle::AlwaysSameLine,
        "Brace style for control flow constructs";
    impl_empty_single_line: bool, true, "Put empty-body implementations on a single line";
    trailing_comma: SeparatorTactic, SeparatorTactic::Vertical,
        "How to handle trailing commas for lists";
    fn_empty_single_line: bool, true, "Put empty-body functions on a single line";
    fn_single_line: bool, false, "Put single-expression functions on a single line";
    fn_return_indent: ReturnIndent, ReturnIndent::WithArgs,
        "Location of return type in function declaration";
    fn_args_paren_newline: bool, true, "If function argument parenthesis goes on a newline";
    fn_args_density: Density, Density::Tall, "Argument density in functions";
    fn_args_layout: IndentStyle, IndentStyle::Visual,
        "Layout of function arguments and tuple structs";
    array_layout: IndentStyle, IndentStyle::Visual, "Indent on arrays";
    array_width: usize, 60,
        "Maximum width of an array literal before falling back to vertical formatting";
    type_punctuation_density: TypeDensity, TypeDensity::Wide,
        "Determines if '+' or '=' are wrapped in spaces in the punctuation of types";
    where_style: Style, Style::Default, "Overall strategy for where clauses";
    // Should we at least try to put the where clause on the same line as the rest of the
    // function decl?
    where_density: Density, Density::CompressedIfEmpty, "Density of a where clause";
    where_layout: ListTactic, ListTactic::Vertical, "Element layout inside a where clause";
    where_pred_indent: IndentStyle, IndentStyle::Visual,
        "Indentation style of a where predicate";
    generics_style: Style, Style::Default, "Overall strategy for generics";
    generics_indent: IndentStyle, IndentStyle::Visual, "Indentation of generics";
    struct_lit_style: IndentStyle, IndentStyle::Block, "Style of struct definition";
    struct_lit_multiline_style: MultilineStyle, MultilineStyle::PreferSingle,
        "Multiline style on literal structs";
    fn_call_style: IndentStyle, IndentStyle::Visual, "Indentation for function calls, etc.";
    report_todo: ReportTactic, ReportTactic::Never,
        "Report all, none or unnumbered occurrences of TODO in source file comments";
    report_fixme: ReportTactic, ReportTactic::Never,
        "Report all, none or unnumbered occurrences of FIXME in source file comments";
    chain_indent: IndentStyle, IndentStyle::Block, "Indentation of chain";
    chain_one_line_max: usize, 60, "Maximum length of a chain to fit on a single line";
    reorder_imports: bool, false, "Reorder import statements alphabetically";
    reorder_imported_names: bool, false,
        "Reorder lists of names in import statements alphabetically";
    single_line_if_else_max_width: usize, 50, "Maximum line length for single line if-else \
                                                expressions. A value of zero means always break \
                                                if-else expressions.";
    format_strings: bool, false, "Format string literals where necessary";
    force_format_strings: bool, false, "Always format string literals";
    take_source_hints: bool, false, "Retain some formatting characteristics from the source code";
    hard_tabs: bool, false, "Use tab characters for indentation, spaces for alignment";
    wrap_comments: bool, false, "Break comments to fit on the line";
    comment_width: usize, 80, "Maximum length of comments. No effect unless wrap_comments = true";
    normalize_comments: bool, false, "Convert /* */ comments to // comments where possible";
    wrap_match_arms: bool, true, "Wrap multiline match arms in blocks";
    match_block_trailing_comma: bool, false,
        "Put a trailing comma after a block based match arm (non-block arms are not affected)";
    indent_match_arms: bool, true, "Indent match arms instead of keeping them at the same \
                                    indentation level as the match keyword";
    closure_block_indent_threshold: isize, 7, "How many lines a closure must have before it is \
                                               block indented. -1 means never use block indent.";
    space_before_type_annotation: bool, false,
        "Leave a space before the colon in a type annotation";
    space_after_type_annotation_colon: bool, true,
        "Leave a space after the colon in a type annotation";
    space_before_bound: bool, false, "Leave a space before the colon in a trait or lifetime bound";
    space_after_bound_colon: bool, true,
        "Leave a space after the colon in a trait or lifetime bound";
    spaces_around_ranges: bool, false, "Put spaces around the  .. and ... range operators";
    spaces_within_angle_brackets: bool, false, "Put spaces within non-empty generic arguments";
    spaces_within_square_brackets: bool, false, "Put spaces within non-empty square brackets";
    spaces_within_parens: bool, false, "Put spaces within non-empty parentheses";
    use_try_shorthand: bool, false, "Replace uses of the try! macro by the ? shorthand";
    write_mode: WriteMode, WriteMode::Replace,
        "What Write Mode to use when none is supplied: Replace, Overwrite, Display, Diff, Coverage";
    condense_wildcard_suffices: bool, false, "Replace strings of _ wildcards by a single .. in \
                                              tuple patterns"
}
<commit_msg>Add todo on equal output for `where_density = Tall` or `Vertical`<commit_after>// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

extern crate toml;

use file_lines::FileLines;
use lists::{SeparatorTactic, ListTactic};

macro_rules! configuration_option_enum{
    ($e:ident: $( $x:ident ),+ $(,)*) => {
        #[derive(Copy, Clone, Eq, PartialEq, Debug)]
        pub enum $e {
            $( $x ),+
        }

        impl_enum_decodable!($e, $( $x ),+);
    }
}

configuration_option_enum! { Style:
    Rfc, // Follow the style RFCs style.
    Default, // Follow the traditional Rustfmt style.
}

configuration_option_enum! { NewlineStyle:
    Windows, // \r\n
    Unix, // \n
    Native, // \r\n in Windows, \n on other platforms
}

configuration_option_enum! { BraceStyle:
    AlwaysNextLine,
    PreferSameLine,
    // Prefer same line except where there is a where clause, in which case force
    // the brace to the next line.
    SameLineWhere,
}

configuration_option_enum! { ControlBraceStyle:
    // K&R style, Rust community default
    AlwaysSameLine,
    // Stroustrup style
    ClosingNextLine,
    // Allman style
    AlwaysNextLine,
}

// How to indent a function's return type.
configuration_option_enum! { ReturnIndent:
    // Aligned with the arguments
    WithArgs,
    // Aligned with the where clause
    WithWhereClause,
}

configuration_option_enum! { IndentStyle:
    // First line on the same line as the opening brace, all lines aligned with
    // the first line.
    Visual,
    // First line is on a new line and all lines align with block indent.
    Block,
}

configuration_option_enum! { Density:
    // Fit as much on one line as possible.
    Compressed,
    // Use more lines.
    Tall,
    // Try to compress if the body is empty.
    CompressedIfEmpty,
    // Place every item on a separate line.
    Vertical,
}

configuration_option_enum! { TypeDensity:
    // No spaces around "=" and "+"
    Compressed,
    // Spaces around " = " and " + "
    Wide,
}

impl Density {
    pub fn to_list_tactic(self) -> ListTactic {
        match self {
            Density::Compressed => ListTactic::Mixed,
            Density::Tall |
            Density::CompressedIfEmpty => ListTactic::HorizontalVertical,
            Density::Vertical => ListTactic::Vertical,
        }
    }
}

configuration_option_enum! { LicensePolicy:
    // Do not place license text at top of files
    NoLicense,
    // Use the text in "license" field as the license
    TextLicense,
    // Use a text file as the license text
    FileLicense,
}

configuration_option_enum! { MultilineStyle:
    // Use horizontal layout if it fits in one line, fall back to vertical
    PreferSingle,
    // Use vertical layout
    ForceMulti,
}

impl MultilineStyle {
    pub fn to_list_tactic(self) -> ListTactic {
        match self {
            MultilineStyle::PreferSingle => ListTactic::HorizontalVertical,
            MultilineStyle::ForceMulti => ListTactic::Vertical,
        }
    }
}

configuration_option_enum! { ReportTactic:
    Always,
    Unnumbered,
    Never,
}

configuration_option_enum! { WriteMode:
    // Backs the original file up and overwrites the original.
    Replace,
    // Overwrites original file without backup.
    Overwrite,
    // Writes the output to stdout.
    Display,
    // Writes the diff to stdout.
    Diff,
    // Displays how much of the input file was processed
    Coverage,
    // Unfancy stdout
    Plain,
    // Outputs a checkstyle XML file.
    Checkstyle,
}

/// Trait for types that can be used in `Config`.
pub trait ConfigType: Sized {
    /// Returns hint text for use in `Config::print_docs()`. For enum types, this is a
    /// pipe-separated list of variants; for other types it returns "<type>".
    fn doc_hint() -> String;
}

impl ConfigType for bool {
    fn doc_hint() -> String {
        String::from("<boolean>")
    }
}

impl ConfigType for usize {
    fn doc_hint() -> String {
        String::from("<unsigned integer>")
    }
}

impl ConfigType for isize {
    fn doc_hint() -> String {
        String::from("<signed integer>")
    }
}

impl ConfigType for String {
    fn doc_hint() -> String {
        String::from("<string>")
    }
}

impl ConfigType for FileLines {
    fn doc_hint() -> String {
        String::from("<json>")
    }
}

pub struct ConfigHelpItem {
    option_name: &'static str,
    doc_string: &'static str,
    variant_names: String,
    default: &'static str,
}

impl ConfigHelpItem {
    pub fn option_name(&self) -> &'static str {
        self.option_name
    }

    pub fn doc_string(&self) -> &'static str {
        self.doc_string
    }

    pub fn variant_names(&self) -> &String {
        &self.variant_names
    }

    pub fn default(&self) -> &'static str {
        self.default
    }
}

macro_rules! create_config {
    ($($i:ident: $ty:ty, $def:expr, $( $dstring:expr ),+ );+ $(;)*) => (
        #[derive(RustcDecodable, Clone)]
        pub struct Config {
            $(pub $i: $ty),+
        }

        // Just like the Config struct but with each property wrapped
        // as Option<T>. This is used to parse a rustfmt.toml that doesn't
        // specity all properties of `Config`.
        // We first parse into `ParsedConfig`, then create a default `Config`
        // and overwrite the properties with corresponding values from `ParsedConfig`
        #[derive(RustcDecodable, Clone)]
        pub struct ParsedConfig {
            $(pub $i: Option<$ty>),+
        }

        impl Config {

            fn fill_from_parsed_config(mut self, parsed: ParsedConfig) -> Config {
            $(
                if let Some(val) = parsed.$i {
                    self.$i = val;
                }
            )+
                self
            }

            pub fn from_toml(toml: &str) -> Result<Config, String> {
                let parsed: toml::Value = toml.parse().expect("Could not parse TOML");
                let mut err: String = String::new();
                for (key, _) in parsed.as_table().expect("Parsed config was not table") {
                    match &**key {
                        $(
                            stringify!($i) => (),
                        )+
                        _ => {
                            let msg = &format!("Warning: Unknown configuration option `{}`\n", key);
                            err.push_str(msg)
                        }
                    }
                }
                match toml::decode(parsed) {
                    Some(parsed_config) =>
                        Ok(Config::default().fill_from_parsed_config(parsed_config)),
                    None => {
                        err.push_str("Error: Decoding config file failed. ");
                        err.push_str("Please check your config file.\n");
                        Err(err)
                    }
                }
            }

            pub fn override_value(&mut self, key: &str, val: &str) {
                match key {
                    $(
                        stringify!($i) => {
                            self.$i = val.parse::<$ty>()
                                .expect(&format!("Failed to parse override for {} (\"{}\") as a {}",
                                                 stringify!($i),
                                                 val,
                                                 stringify!($ty)));
                        }
                    )+
                    _ => panic!("Unknown config key in override: {}", key)
                }
            }

            pub fn print_docs() {
                use std::cmp;
                let max = 0;
                $( let max = cmp::max(max, stringify!($i).len()+1); )+
                let mut space_str = String::with_capacity(max);
                for _ in 0..max {
                    space_str.push(' ');
                }
                println!("Configuration Options:");
                $(
                    let name_raw = stringify!($i);
                    let mut name_out = String::with_capacity(max);
                    for _ in name_raw.len()..max-1 {
                        name_out.push(' ')
                    }
                    name_out.push_str(name_raw);
                    name_out.push(' ');
                    println!("{}{} Default: {:?}",
                             name_out,
                             <$ty>::doc_hint(),
                             $def);
                    $(
                        println!("{}{}", space_str, $dstring);
                    )+
                    println!("");
                )+
            }
        }

        // Template for the default configuration
        impl Default for Config {
            fn default() -> Config {
                Config {
                    $(
                        $i: $def,
                    )+
                }
            }
        }
    )
}

create_config! {
    verbose: bool, false, "Use verbose output";
    disable_all_formatting: bool, false, "Don't reformat anything";
    skip_children: bool, false, "Don't reformat out of line modules";
    file_lines: FileLines, FileLines::all(),
        "Lines to format; this is not supported in rustfmt.toml, and can only be specified \
         via the --file-lines option";
    max_width: usize, 100, "Maximum width of each line";
    error_on_line_overflow: bool, true, "Error if unable to get all lines within max_width";
    tab_spaces: usize, 4, "Number of spaces per tab";
    fn_call_width: usize, 60,
        "Maximum width of the args of a function call before falling back to vertical formatting";
    struct_lit_width: usize, 18,
        "Maximum width in the body of a struct lit before falling back to vertical formatting";
    struct_variant_width: usize, 35,
        "Maximum width in the body of a struct variant before falling back to vertical formatting";
    force_explicit_abi: bool, true, "Always print the abi for extern items";
    newline_style: NewlineStyle, NewlineStyle::Unix, "Unix or Windows line endings";
    fn_brace_style: BraceStyle, BraceStyle::SameLineWhere, "Brace style for functions";
    item_brace_style: BraceStyle, BraceStyle::SameLineWhere, "Brace style for structs and enums";
    control_brace_style: ControlBraceStyle, ControlBraceStyle::AlwaysSameLine,
        "Brace style for control flow constructs";
    impl_empty_single_line: bool, true, "Put empty-body implementations on a single line";
    trailing_comma: SeparatorTactic, SeparatorTactic::Vertical,
        "How to handle trailing commas for lists";
    fn_empty_single_line: bool, true, "Put empty-body functions on a single line";
    fn_single_line: bool, false, "Put single-expression functions on a single line";
    fn_return_indent: ReturnIndent, ReturnIndent::WithArgs,
        "Location of return type in function declaration";
    fn_args_paren_newline: bool, true, "If function argument parenthesis goes on a newline";
    fn_args_density: Density, Density::Tall, "Argument density in functions";
    fn_args_layout: IndentStyle, IndentStyle::Visual,
        "Layout of function arguments and tuple structs";
    array_layout: IndentStyle, IndentStyle::Visual, "Indent on arrays";
    array_width: usize, 60,
        "Maximum width of an array literal before falling back to vertical formatting";
    type_punctuation_density: TypeDensity, TypeDensity::Wide,
        "Determines if '+' or '=' are wrapped in spaces in the punctuation of types";
    where_style: Style, Style::Default, "Overall strategy for where clauses";
    // TODO:
    // 1. Should we at least try to put the where clause on the same line as the rest of the
    // function decl?
    // 2. Currently options `Tall` and `Vertical` produce the same output.
    where_density: Density, Density::CompressedIfEmpty, "Density of a where clause";
    where_layout: ListTactic, ListTactic::Vertical, "Element layout inside a where clause";
    where_pred_indent: IndentStyle, IndentStyle::Visual,
        "Indentation style of a where predicate";
    generics_style: Style, Style::Default, "Overall strategy for generics";
    generics_indent: IndentStyle, IndentStyle::Visual, "Indentation of generics";
    struct_lit_style: IndentStyle, IndentStyle::Block, "Style of struct definition";
    struct_lit_multiline_style: MultilineStyle, MultilineStyle::PreferSingle,
        "Multiline style on literal structs";
    fn_call_style: IndentStyle, IndentStyle::Visual, "Indentation for function calls, etc.";
    report_todo: ReportTactic, ReportTactic::Never,
        "Report all, none or unnumbered occurrences of TODO in source file comments";
    report_fixme: ReportTactic, ReportTactic::Never,
        "Report all, none or unnumbered occurrences of FIXME in source file comments";
    chain_indent: IndentStyle, IndentStyle::Block, "Indentation of chain";
    chain_one_line_max: usize, 60, "Maximum length of a chain to fit on a single line";
    reorder_imports: bool, false, "Reorder import statements alphabetically";
    reorder_imported_names: bool, false,
        "Reorder lists of names in import statements alphabetically";
    single_line_if_else_max_width: usize, 50, "Maximum line length for single line if-else \
                                                expressions. A value of zero means always break \
                                                if-else expressions.";
    format_strings: bool, false, "Format string literals where necessary";
    force_format_strings: bool, false, "Always format string literals";
    take_source_hints: bool, false, "Retain some formatting characteristics from the source code";
    hard_tabs: bool, false, "Use tab characters for indentation, spaces for alignment";
    wrap_comments: bool, false, "Break comments to fit on the line";
    comment_width: usize, 80, "Maximum length of comments. No effect unless wrap_comments = true";
    normalize_comments: bool, false, "Convert /* */ comments to // comments where possible";
    wrap_match_arms: bool, true, "Wrap multiline match arms in blocks";
    match_block_trailing_comma: bool, false,
        "Put a trailing comma after a block based match arm (non-block arms are not affected)";
    indent_match_arms: bool, true, "Indent match arms instead of keeping them at the same \
                                    indentation level as the match keyword";
    closure_block_indent_threshold: isize, 7, "How many lines a closure must have before it is \
                                               block indented. -1 means never use block indent.";
    space_before_type_annotation: bool, false,
        "Leave a space before the colon in a type annotation";
    space_after_type_annotation_colon: bool, true,
        "Leave a space after the colon in a type annotation";
    space_before_bound: bool, false, "Leave a space before the colon in a trait or lifetime bound";
    space_after_bound_colon: bool, true,
        "Leave a space after the colon in a trait or lifetime bound";
    spaces_around_ranges: bool, false, "Put spaces around the  .. and ... range operators";
    spaces_within_angle_brackets: bool, false, "Put spaces within non-empty generic arguments";
    spaces_within_square_brackets: bool, false, "Put spaces within non-empty square brackets";
    spaces_within_parens: bool, false, "Put spaces within non-empty parentheses";
    use_try_shorthand: bool, false, "Replace uses of the try! macro by the ? shorthand";
    write_mode: WriteMode, WriteMode::Replace,
        "What Write Mode to use when none is supplied: Replace, Overwrite, Display, Diff, Coverage";
    condense_wildcard_suffices: bool, false, "Replace strings of _ wildcards by a single .. in \
                                              tuple patterns"
}
<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use jsapi::{JSCLASS_GLOBAL_APPLICATION_SLOTS, JSCLASS_RESERVED_SLOTS_SHIFT};
use jsapi::{JSCLASS_RESERVED_SLOTS_WIDTH, JSProtoKey};
use libc::c_uint;

pub const default_heapsize: u32 = 32_u32 * 1024_u32 * 1024_u32;

pub const JSCLASS_IS_DOMJSCLASS: c_uint = 1 << 4;
pub const JSCLASS_USERBIT1: c_uint = 1 << 7;

pub const JSCLASS_RESERVED_SLOTS_MASK: c_uint =
    (1 << JSCLASS_RESERVED_SLOTS_WIDTH) - 1;

pub const JSCLASS_HIGH_FLAGS_SHIFT: c_uint =
    (JSCLASS_RESERVED_SLOTS_SHIFT as c_uint) + JSCLASS_RESERVED_SLOTS_WIDTH;

pub const JSCLASS_IS_GLOBAL: c_uint =
    1 << (JSCLASS_HIGH_FLAGS_SHIFT + 1);

pub const JSCLASS_IS_PROXY: c_uint =
    1 << (JSCLASS_HIGH_FLAGS_SHIFT + 4);

pub const JSCLASS_GLOBAL_SLOT_COUNT: c_uint =
    JSCLASS_GLOBAL_APPLICATION_SLOTS + JSProtoKey::JSProto_LIMIT as c_uint * 3 + 36;
<commit_msg>change the slot count to match the spidermonkey defintion<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use jsapi::{JSCLASS_GLOBAL_APPLICATION_SLOTS, JSCLASS_RESERVED_SLOTS_SHIFT};
use jsapi::{JSCLASS_RESERVED_SLOTS_WIDTH, JSProtoKey};
use libc::c_uint;

pub const default_heapsize: u32 = 32_u32 * 1024_u32 * 1024_u32;

pub const JSCLASS_IS_DOMJSCLASS: c_uint = 1 << 4;
pub const JSCLASS_USERBIT1: c_uint = 1 << 7;

pub const JSCLASS_RESERVED_SLOTS_MASK: c_uint =
    (1 << JSCLASS_RESERVED_SLOTS_WIDTH) - 1;

pub const JSCLASS_HIGH_FLAGS_SHIFT: c_uint =
    (JSCLASS_RESERVED_SLOTS_SHIFT as c_uint) + JSCLASS_RESERVED_SLOTS_WIDTH;

pub const JSCLASS_IS_GLOBAL: c_uint =
    1 << (JSCLASS_HIGH_FLAGS_SHIFT + 1);

pub const JSCLASS_IS_PROXY: c_uint =
    1 << (JSCLASS_HIGH_FLAGS_SHIFT + 4);

pub const JSCLASS_GLOBAL_SLOT_COUNT: c_uint =
    JSCLASS_GLOBAL_APPLICATION_SLOTS + JSProtoKey::JSProto_LIMIT as c_uint * 2 + 38;
<|endoftext|>


<commit_before>use std::fmt;

use chrono::{DateTime, Local};
use itertools::Itertools;
use json::JsonValue;

// #[derive(Debug, Default)]
// pub struct Error {
//     pub detail: String,
// }
//
// #[derive(Debug)]
// pub struct CrateLinks {
//     pub owners: Option<String>,
//     pub reverse_dependencies: String,
//     pub version_downloads: String,
//     pub versions: Option<String>,
// }
//
// pub struct Crate {
//     pub created_at: String,
//     pub description: Option<String>,
//     pub documentation: Option<String>,
//     pub downloads: i32,
//     pub homepage: Option<String>,
//     pub id: String,
//     pub keywords: Option<Vec<String>>,
//     pub license: Option<String>,
//     pub links: CrateLinks,
//     pub max_version: String,
//     pub name: String,
//     pub repository: Option<String>,
//     pub updated_at: String,
//     pub versions: Option<Vec<u64>>,
// }
//
// #[derive(Debug)]
// pub struct Keyword {
//     pub crates_cnt: u64,
//     pub created_at: String,
//     pub id: String,
//     pub keyword: String,
// }
//
// #[derive(Debug)]
// pub struct VersionLinks {
//     pub authors: String,
//     pub dependencies: String,
//     pub version_downloads: String,
// }
//
// #[derive(Debug)]
// pub struct Version {
//     pub krate: String,
//     pub created_at: String,
//     pub dl_path: String,
//     pub downloads: i32,
//     pub features: HashMap<String, Vec<String>>,
//     pub id: i32,
//     pub links: VersionLinks,
//     pub num: String,
//     pub updated_at: String,
//     pub yanked: bool,
// }
//
// pub struct Reply {
//     pub errors: Error,
//     pub krate: Crate,
//     pub keywords: Vec<Keyword>,
//     pub versions: Vec<Version>,
// }

struct TimeStamp(Option<DateTime<Local>>);

impl<'a> From<&'a JsonValue> for TimeStamp {
    fn from(jv: &JsonValue) -> Self {
        let parse = |s: &str| s.parse::<DateTime<Local>>().ok();
        TimeStamp(jv.as_str().and_then(parse))
    }
}

impl fmt::Display for TimeStamp {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        if let Some(ts) = self.0 {
            f.pad(&format!("{}", ts))
        } else {
            f.pad("")
        }
    }
}

pub struct Crate {
    krate: JsonValue,
    versions: JsonValue,
    keywords: JsonValue,
}

impl Crate {
    pub fn new(json: &JsonValue) -> Self {
        Crate {
            krate: json["crate"].clone(),
            versions: json["versions"].clone(),
            keywords: json["keywords"].clone(),
        }
    }

    pub fn print_repository(&self, verbose: bool) {
        if let JsonValue::String(ref repository) = self.krate["repository"] {
            let fmt = if verbose {
                format!("{:<16}{}", "Repository:", repository)
            } else {
                repository.clone()
            };
            println!("{}", fmt);
        }
    }

    pub fn print_documentation(&self, verbose: bool) {
        if let JsonValue::String(ref documentation) = self.krate["documentation"] {
            let fmt = if verbose {
                format!("{:<16}{}", "Documentation:", documentation)
            } else {
                documentation.clone()
            };
            println!("{}", fmt);
        }
    }

    pub fn print_downloads(&self, verbose: bool) {
        if let JsonValue::Number(downloads) = self.krate["downloads"] {
            let fmt = if verbose {
                format!("{:<16}{}", "Downloads:", downloads)
            } else {
                format!("{}", downloads)
            };
            println!("{}", fmt);
        }
    }

    pub fn print_homepage(&self, verbose: bool) {
        if let JsonValue::String(ref homepage) = self.krate["homepage"] {
            let fmt = if verbose {
                format!("{:<16}{}", "Homepage:", homepage)
            } else {
                homepage.clone()
            };
            println!("{}", fmt);
        }
    }

    fn print_version(v: &JsonValue, verbose: bool) {
        if verbose {
            let created_at = TimeStamp::from(&v["created_at"]);
            let yanked = if v["yanked"] == true {
                "YANKED"
            } else {
                ""
            };
            println!("{:<10}{:<28}{:<11}{}",
                     v["num"],
                     created_at,
                     v["downloads"],
                     yanked)
        } else {
            println!("{:<10}", v["num"])
        };
    }

    fn print_version_header(verbose: bool) {
        if verbose {
            println!("{:<10}{:<28}{:<11}{}",
                    "VERSION",
                    "RELEASE DATE",
                    "DOWNLOADS",
                    "YANKED")
        } else {
            println!("{:<10}", "VERSION")
        };
    }

    pub fn print_last_versions(&self, limit: usize, verbose: bool) {
        Crate::print_version_header(verbose);
        self.versions
            .members()
            .take(limit)
            // This collect()::<Vec<_>>.iter() is here only because take()
            // does not support DoubleEndedIterator for rev(), bummer
            .collect::<Vec<_>>()
            .iter()
            .rev()
            .foreach(|v| Crate::print_version(v, verbose));
    }

    pub fn print_keywords(&self, verbose: bool) {
        let fmt = if verbose {
            format!("{:#}", self.keywords)
        } else {
            format!("{}", self.keywords)
        };
        println!("{}", fmt);
    }
}

impl fmt::Display for Crate {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let empty = "";

        let name = self.krate["name"].as_str().unwrap_or(empty);
        let max_version = self.krate["max_version"].as_str().unwrap_or(empty);
        let downloads = self.krate["downloads"].as_i32().unwrap_or(0);

        let created_at = TimeStamp::from(&self.krate["created_at"]);
        let updated_at = TimeStamp::from(&self.krate["updated_at"]);

        let description = self.krate["description"].as_str().unwrap_or(empty);
        let documentation = self.krate["documentation"].as_str().unwrap_or(empty);
        let homepage = self.krate["homepage"].as_str().unwrap_or(empty);
        let repository = self.krate["repository"].as_str().unwrap_or(empty);
        let license = self.krate["license"].as_str().unwrap_or(empty);

        let keywords = self.krate["keywords"]
            .members()
            .filter_map(|jv| jv.as_str())
            .collect::<Vec<_>>();

        if f.alternate() {
            write!(f,
                   "{}\n{}\n{}\n{}\n{}\n{}\n{}\n{}\n{}\n{}\n{}",
                   format_args!("{:<16}{}", "Crate:", name),
                   format_args!("{:<16}{}", "Version:", max_version),
                   format_args!("{:<16}{}", "Description:", description),
                   format_args!("{:<16}{}", "Downloads:", downloads),
                   format_args!("{:<16}{}", "Homepage:", homepage),
                   format_args!("{:<16}{}", "Documentation:", documentation),
                   format_args!("{:<16}{}", "Repository:", repository),
                   format_args!("{:<16}{}", "License:", license),
                   format_args!("{:<16}{:?}", "Keywords:", keywords),
                   format_args!("{:<16}{}", "Created at:", created_at),
                   format_args!("{:<16}{}", "Updated at:", updated_at))
        } else {
            write!(f,
                   "{}\n{}\n{}\n{}\n{}\n{}\n{}",
                   format_args!("{:<16}{}", "Crate:", name),
                   format_args!("{:<16}{}", "Version:", max_version),
                   format_args!("{:<16}{}", "Description:", description),
                   format_args!("{:<16}{}", "Downloads:", downloads),
                   format_args!("{:<16}{}", "Homepage:", homepage),
                   format_args!("{:<16}{}", "Documentation:", documentation),
                   format_args!("{:<16}{}", "Repository:", repository))
        }
    }
}
<commit_msg>Cleanup output style<commit_after>use std::fmt;

use chrono::{DateTime, Local};
use itertools::Itertools;
use json::JsonValue;

// #[derive(Debug, Default)]
// pub struct Error {
//     pub detail: String,
// }
//
// #[derive(Debug)]
// pub struct CrateLinks {
//     pub owners: Option<String>,
//     pub reverse_dependencies: String,
//     pub version_downloads: String,
//     pub versions: Option<String>,
// }
//
// pub struct Crate {
//     pub created_at: String,
//     pub description: Option<String>,
//     pub documentation: Option<String>,
//     pub downloads: i32,
//     pub homepage: Option<String>,
//     pub id: String,
//     pub keywords: Option<Vec<String>>,
//     pub license: Option<String>,
//     pub links: CrateLinks,
//     pub max_version: String,
//     pub name: String,
//     pub repository: Option<String>,
//     pub updated_at: String,
//     pub versions: Option<Vec<u64>>,
// }
//
// #[derive(Debug)]
// pub struct Keyword {
//     pub crates_cnt: u64,
//     pub created_at: String,
//     pub id: String,
//     pub keyword: String,
// }
//
// #[derive(Debug)]
// pub struct VersionLinks {
//     pub authors: String,
//     pub dependencies: String,
//     pub version_downloads: String,
// }
//
// #[derive(Debug)]
// pub struct Version {
//     pub krate: String,
//     pub created_at: String,
//     pub dl_path: String,
//     pub downloads: i32,
//     pub features: HashMap<String, Vec<String>>,
//     pub id: i32,
//     pub links: VersionLinks,
//     pub num: String,
//     pub updated_at: String,
//     pub yanked: bool,
// }
//
// pub struct Reply {
//     pub errors: Error,
//     pub krate: Crate,
//     pub keywords: Vec<Keyword>,
//     pub versions: Vec<Version>,
// }

struct TimeStamp(Option<DateTime<Local>>);

impl<'a> From<&'a JsonValue> for TimeStamp {
    fn from(jv: &JsonValue) -> Self {
        let parse = |s: &str| s.parse::<DateTime<Local>>().ok();
        TimeStamp(jv.as_str().and_then(parse))
    }
}

impl fmt::Display for TimeStamp {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        if let Some(ts) = self.0 {
            f.pad(&format!("{}", ts))
        } else {
            f.pad("")
        }
    }
}

pub struct Crate {
    krate: JsonValue,
    versions: JsonValue,
    keywords: JsonValue,
}

impl Crate {
    pub fn new(json: &JsonValue) -> Self {
        Crate {
            krate: json["crate"].clone(),
            versions: json["versions"].clone(),
            keywords: json["keywords"].clone(),
        }
    }

    pub fn print_repository(&self, verbose: bool) {
        if let JsonValue::String(ref repository) = self.krate["repository"] {
            let fmt = if verbose {
                format!("{:<16}{}", "Repository:", repository)
            } else {
                repository.clone()
            };
            println!("{}", fmt);
        }
    }

    pub fn print_documentation(&self, verbose: bool) {
        if let JsonValue::String(ref documentation) = self.krate["documentation"] {
            let fmt = if verbose {
                format!("{:<16}{}", "Documentation:", documentation)
            } else {
                documentation.clone()
            };
            println!("{}", fmt);
        }
    }

    pub fn print_downloads(&self, verbose: bool) {
        if let JsonValue::Number(downloads) = self.krate["downloads"] {
            let fmt = if verbose {
                format!("{:<16}{}", "Downloads:", downloads)
            } else {
                format!("{}", downloads)
            };
            println!("{}", fmt);
        }
    }

    pub fn print_homepage(&self, verbose: bool) {
        if let JsonValue::String(ref homepage) = self.krate["homepage"] {
            let fmt = if verbose {
                format!("{:<16}{}", "Homepage:", homepage)
            } else {
                homepage.clone()
            };
            println!("{}", fmt);
        }
    }

    #[allow(bool_comparison)]
    fn print_version(v: &JsonValue, verbose: bool) {
        let created_at = TimeStamp::from(&v["created_at"]);
        print!("{:<10}{:<28}{:<11}", v["num"], created_at, v["downloads"]);

        if v["yanked"] == true {
            print!("(yanked)");
        }

        if verbose {
            // Consider adding some more useful information in verbose mode
            println!("");
        } else {
            println!("");
        }
    }

    fn print_version_header(verbose: bool) {
        print!("{:<10}{:<28}{:<11}\n",
                 "VERSION",
                 "RELEASE DATE",
                 "DOWNLOADS");
        if verbose {
            // Consider adding some more useful information in verbose mode
            println!("");
        } else {
            println!("");
        }
    }

    pub fn print_last_versions(&self, limit: usize, verbose: bool) {
        Crate::print_version_header(verbose);
        self.versions
            .members()
            .take(limit)
            .foreach(|v| Crate::print_version(v, verbose));

        let length = self.versions.len();
        if limit < length {
            println!("\n... use -VV to show all {} versions", length);
        }
    }

    pub fn print_keywords(&self, verbose: bool) {
        let fmt = if verbose {
            format!("{:#}", self.keywords)
        } else {
            format!("{}", self.keywords)
        };
        println!("{}", fmt);
    }
}

impl fmt::Display for Crate {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let empty = "";

        let name = self.krate["name"].as_str().unwrap_or(empty);
        let max_version = self.krate["max_version"].as_str().unwrap_or(empty);
        let downloads = self.krate["downloads"].as_i32().unwrap_or(0);

        let created_at = TimeStamp::from(&self.krate["created_at"]);
        let updated_at = TimeStamp::from(&self.krate["updated_at"]);

        let description = self.krate["description"].as_str().unwrap_or(empty);
        let documentation = self.krate["documentation"].as_str().unwrap_or(empty);
        let homepage = self.krate["homepage"].as_str().unwrap_or(empty);
        let repository = self.krate["repository"].as_str().unwrap_or(empty);
        let license = self.krate["license"].as_str().unwrap_or(empty);

        let keywords = self.krate["keywords"]
            .members()
            .filter_map(|jv| jv.as_str())
            .collect::<Vec<_>>();

        if f.alternate() {
            write!(f,
                   "{}\n{}\n{}\n{}\n{}\n{}\n{}\n{}\n{}\n{}\n{}",
                   format_args!("{:<16}{}", "Crate:", name),
                   format_args!("{:<16}{}", "Version:", max_version),
                   format_args!("{:<16}{}", "Description:", description),
                   format_args!("{:<16}{}", "Downloads:", downloads),
                   format_args!("{:<16}{}", "Homepage:", homepage),
                   format_args!("{:<16}{}", "Documentation:", documentation),
                   format_args!("{:<16}{}", "Repository:", repository),
                   format_args!("{:<16}{}", "License:", license),
                   format_args!("{:<16}{:?}", "Keywords:", keywords),
                   format_args!("{:<16}{}", "Created at:", created_at),
                   format_args!("{:<16}{}", "Updated at:", updated_at))
        } else {
            write!(f,
                   "{}\n{}\n{}\n{}\n{}\n{}\n{}",
                   format_args!("{:<16}{}", "Crate:", name),
                   format_args!("{:<16}{}", "Version:", max_version),
                   format_args!("{:<16}{}", "Description:", description),
                   format_args!("{:<16}{}", "Downloads:", downloads),
                   format_args!("{:<16}{}", "Homepage:", homepage),
                   format_args!("{:<16}{}", "Documentation:", documentation),
                   format_args!("{:<16}{}", "Repository:", repository))
        }
    }
}
<|endoftext|>


<commit_before>
use std::ffi::CString;
use std::os::raw::c_char;

#[link( name = "ncurses" )]
extern {
    pub fn initscr();
    pub fn refresh();
    pub fn endwin();
    pub fn getch() -> i32;
    pub fn printw( format : *const c_char, ... );
    pub fn cbreak();
    pub fn noecho();
    pub fn keypad();
}

extern {
    pub static error : i32;
    fn move_wrapper( y : i32, x : i32 );
}

pub unsafe fn cursor_move( y : i32, x : i32 ) {
    move_wrapper( y, x );
}

<commit_msg>added clear from ncurses<commit_after>
use std::ffi::CString;
use std::os::raw::c_char;

#[link( name = "ncurses" )]
extern {
    pub fn initscr();
    pub fn refresh();
    pub fn endwin();
    pub fn getch() -> i32;
    pub fn printw( format : *const c_char, ... );
    pub fn cbreak();
    pub fn noecho();
    pub fn keypad();
    pub fn clear();
}

extern {
    pub static error : i32;
    fn move_wrapper( y : i32, x : i32 );
}

pub unsafe fn cursor_move( y : i32, x : i32 ) {
    move_wrapper( y, x );
}

<|endoftext|>