<commit_before>
use std::fmt;
use std::io::Read;  // Trait providing read_to_string()
use std::env;

use url;
use hyper;
use serde;
use serde_json;

// use Groups;

use ::errors::*;

pub const API_VERSION: u16 = 3;

#[derive(Default, Clone, Copy, Debug)]
pub struct Pagination {
    pub page: u16,
    pub per_page: u16,
}

pub struct GitLab {
    url: url::Url,
    private_token: String,
    pagination: Option<Pagination>,
    client: hyper::Client,
}

// Explicitly implement Debug trait for GitLab so we can hide the token.
impl fmt::Debug for GitLab {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f,
               "GitLab {{ scheme: {}, domain: {}, port: {}, private_token: XXXXXXXXXXXXXXXXXXXX, \
                pagination: {:?} }}",
               self.url.scheme(),
               self.url.domain().unwrap_or("bad hostname provided"),
               self.url
                   .port()
                   .map(|port_u16| port_u16.to_string())
                   .unwrap_or("no port provided".to_string()),
               self.pagination)
    }
}

fn validate_url(scheme: &str, domain: &str, port: u16) -> Result<url::Url> {

    match domain.find('.') {
        None => {
            // pass
        }
        Some(index) => {
            if index == 0 {
                bail!(format!("invalid domain: '{}' cannot start with a dot", domain));
            }
        }
    };

    if domain.ends_with('.') {
        bail!(format!("invalid domain: '{}' cannot end with a dot", domain));
    }

    let url_string = format!("{}://{}/api/v{}/", scheme, domain, API_VERSION);
    let mut url = url::Url::parse(&url_string)
        .chain_err(|| format!("failure to parse URL '{}'", url_string))?;
    url.set_port(Some(port)).expect("bad port provided");

    {
        let url_host = url.host_str();
        if url_host.is_none() {
            bail!("failure to get URL's hostname");
        }
        if url_host.unwrap() != domain {
            bail!(format!("invalid hostname '{}'", domain));
        }
    }

    Ok(url)
}

impl GitLab {
    pub fn _new(scheme: &str, domain: &str, port: u16, private_token: &str) -> Result<GitLab> {
        if private_token.len() != 20 {
            bail!(format!("private token should be a 20 characters string (not {})",
                          private_token.len()));
        }

        let url: url::Url = validate_url(scheme, domain, port).chain_err(|| "invalid URL")?;

        Ok(GitLab {
            url: url,
            private_token: private_token.to_string(),
            pagination: None,
            client: match env::var("HTTP_PROXY") {
                Ok(proxy) => {
                    let proxy: Vec<&str> = proxy.trim_left_matches("http://").split(':').collect();
                    let hostname = proxy[0].to_string();
                    let port = proxy[1].parse()
                        .chain_err(|| format!("failure to set port to {}", port))?;

                    hyper::Client::with_http_proxy(hostname, port)
                }
                Err(_) => hyper::Client::new(),
            },
        })
    }

    pub fn new_insecure(domain: &str, private_token: &str) -> Result<GitLab> {
        warn!("Using insecure http:// protocol: Token will be sent in clear!");
        GitLab::_new("http", domain, 80, private_token)
    }

    pub fn new(domain: &str, private_token: &str) -> Result<GitLab> {
        GitLab::_new("https", domain, 443, private_token)
    }

    pub fn port(mut self, port: u16) -> Self {
        self.url.set_port(Some(port)).unwrap();
        self
    }

    pub fn scheme(mut self, scheme: &str) -> Self {
        self.url.set_scheme(scheme).unwrap();
        self
    }

    /// Build a URL used to access GitLab instance, including some parameters.
    ///
    /// # Examples
    ///
    /// Example from GitLab: https://docs.gitlab.com/ce/api/#basic-usage
    ///
    /// ```
    /// use gitlab_api::GitLab;
    ///
    /// let expected_url = "https://gitlab.example.com\
    ///                     /api/v3/groups?order_by=path&private_token=XXXXXXXXXXXXXXXXXXXX";
    ///
    /// let gl = GitLab::new("gitlab.example.com", "XXXXXXXXXXXXXXXXXXXX").unwrap();
    ///
    /// assert_eq!(gl.build_url("groups?order_by=path").unwrap(), expected_url);
    /// ```
    pub fn build_url(&self, query: &str) -> Result<String> {
        let mut new_url = self.url
            .clone()
            .join(query)
            .chain_err(|| {
                format!("Failure to join query '{}' to url {}",
                        query,
                        self.url.as_str())
            })?;
        new_url.query_pairs_mut().append_pair("private_token", &self.private_token);
        self.pagination.as_ref().map(|pagination| {
            new_url.query_pairs_mut().append_pair("page", &pagination.page.to_string());
            new_url.query_pairs_mut().append_pair("per_page", &pagination.per_page.to_string());
        });

        Ok(new_url.into_string())
    }

    // pub fn attempt_connection(&self) -> Result<hyper::client::Response, hyper::Error> {
    //     let url = self.build_url("version");
    //     // Close connections after each GET.
    //     self.client.get(&url).header(hyper::header::Connection::close()).send()
    // }

    /// Set pagination information
    ///
    /// # Examples
    ///
    /// ```
    /// use gitlab_api::{GitLab, Pagination};
    ///
    /// let expected_url = "https://gitlab.example.com\
    ///                     /api/v3/groups?order_by=path&\
    ///                     private_token=XXXXXXXXXXXXXXXXXXXX&page=2&per_page=5";
    ///
    /// let mut gl = GitLab::new("gitlab.example.com", "XXXXXXXXXXXXXXXXXXXX").unwrap();
    /// gl.set_pagination(Pagination {page: 2, per_page: 5});
    /// assert_eq!(gl.build_url("groups?order_by=path").unwrap(), expected_url);
    /// ```
    pub fn set_pagination(&mut self, pagination: Pagination) {
        self.pagination = Some(pagination);
    }

    pub fn get<T>(&self, query: &str) -> Result<T>
        where T: serde::Deserialize
    {
        let url = self.build_url(query)
            .chain_err(|| format!("failure to build url for query '{}'", query))?;
        info!("url: {:?}", url);

        // Close connections after each GET.
        let mut res: hyper::client::Response = self.client
            .get(&url)
            .header(hyper::header::Connection::close())
            .send()
            .chain_err(|| format!("cannot send request '{}' to {:?}", query, self))?;
        info!("res.status: {:?}", res.status);
        debug!("res.headers: {:?}", res.headers);
        debug!("res.url: {:?}", res.url);

        let mut body = String::new();
        res.read_to_string(&mut body).chain_err(|| "cannot read response body")?;
        debug!("body:\n{:?}", body);

        if res.status != hyper::status::StatusCode::Ok {
            bail!(format!("status code '{}', not '200 OK'", res.status));
        }

        serde_json::from_str(body.as_str())
            .chain_err(|| format!("cannot build Rust struct from JSON data: {}", body))
    }

    pub fn version(&self) -> Result<::Version> {
        self.get("version").chain_err(|| "cannot query 'version'")
    }

    pub fn groups(&self) -> ::groups::GroupsLister {
        ::groups::GroupsLister::new(self)
    }

    pub fn projects(&self) -> ::projects::ProjectsLister {
        ::projects::ProjectsLister::new(self)
    }

    pub fn issues(&self) -> ::issues::IssuesLister {
        ::issues::IssuesLister::new(self)
    }

    pub fn merge_requests(&self, project_id: i64) -> ::merge_requests::MergeRequestsLister {
        ::merge_requests::MergeRequestsLister::new(self, project_id)
    }

    // pub fn groups(&mut self, listing: ::groups::Listing) -> Result<Groups, serde_json::Error> {
    //     let query = listing.build_query();
    //     // self.get(&query)
    //     unimplemented!();
    // }

    // pub fn owned_groups(&mut self) -> Result<Groups, serde_json::Error> {
    //     let query = ::groups::owned_groups::Listing::new().build_query();
    //     info!("query: {:?}", query);
    //     self.get(&query)
    // }
}

#[cfg(test)]
mod tests {
    use std::fmt;
    use gitlab::*;
    use errors::*;

    fn verify_ok<T>(result: &Result<T>) {
        if let &Err(ref e) = result {
            println!("error: {}", e);

            for e in e.iter().skip(1) {
                println!("caused by: {}", e);
            }

            // The backtrace is not always generated. Try to run this example
            // with `RUST_BACKTRACE=1`.
            if let Some(backtrace) = e.backtrace() {
                println!("backtrace: {:?}", backtrace);
            }
        }
        assert!(result.is_ok());
    }

    fn verify_err<T>(result: &Result<T>)
        where T: fmt::Debug
    {
        match result {
            &Err(_) => {
                // pass
            }
            &Ok(ref t) => {
                panic!(format!("Expected an Err(), got an Ok(t), with t: {:?}", t));
            }
        }
    }

    #[test]
    fn impl_debug_for_gitlab() {
        let gl = GitLab::new("gitlab.com", "XXXXXXXXXXXXXXXXXXXX").unwrap();

        let debug = format!("{:?}", gl);
        assert_eq!("GitLab { scheme: https, domain: gitlab.com, port: no port provided, \
                    private_token: XXXXXXXXXXXXXXXXXXXX, pagination: None }",
                   debug);

        let gl = gl.scheme("http").port(80);
        let debug = format!("{:?}", gl);
        assert_eq!("GitLab { scheme: http, domain: gitlab.com, port: no port provided, \
                    private_token: XXXXXXXXXXXXXXXXXXXX, pagination: None }",
                   debug);

        let mut gl = gl.port(81);
        let debug = format!("{:?}", gl);
        assert_eq!("GitLab { scheme: http, domain: gitlab.com, port: 81, private_token: \
                    XXXXXXXXXXXXXXXXXXXX, pagination: None }",
                   debug);

        gl.set_pagination(Pagination {
            page: 2,
            per_page: 5,
        });
        let debug = format!("{:?}", gl);
        assert_eq!("GitLab { scheme: http, domain: gitlab.com, port: 81, private_token: \
                    XXXXXXXXXXXXXXXXXXXX, pagination: Some(Pagination { page: 2, per_page: 5 }) }",
                   debug);
    }

    #[test]
    fn gitlab_listers_groups() {
        let gl = GitLab::new("gitlab.com", "XXXXXXXXXXXXXXXXXXXX").unwrap();
        let groups_lister = gl.groups();
        let debug = format!("{:?}", groups_lister);
        assert_eq!("GroupsLister { gl: GitLab { scheme: https, domain: gitlab.com, port: no \
                    port provided, private_token: XXXXXXXXXXXXXXXXXXXX, pagination: None }, \
                    internal: GroupsListerInternal { skip_groups: None, all_available: None, \
                    search: None, order_by: None, sort: None } }",
                   debug);
    }

    #[test]
    fn gitlab_listers_projects() {
        let gl = GitLab::new("gitlab.com", "XXXXXXXXXXXXXXXXXXXX").unwrap();
        let projects_lister = gl.projects();
        let debug = format!("{:?}", projects_lister);
        assert_eq!("ProjectsLister { gl: GitLab { scheme: https, domain: gitlab.com, port: no \
                    port provided, private_token: XXXXXXXXXXXXXXXXXXXX, pagination: None }, \
                    internal: ProjectListerInternal { archived: None, visibility: None, \
                    order_by: None, sort: None, search: None, simple: None } }",
                   debug);
    }

    #[test]
    fn new_valid() {
        let gl = GitLab::new("gitlab.com", "XXXXXXXXXXXXXXXXXXXX");
        verify_ok(&gl);

        let gl = GitLab::new_insecure("gitlab.com", "XXXXXXXXXXXXXXXXXXXX");
        verify_ok(&gl);

        let gl = GitLab::new("localhost", "XXXXXXXXXXXXXXXXXXXX");
        verify_ok(&gl);

        let gl = GitLab::new_insecure("localhost", "XXXXXXXXXXXXXXXXXXXX");
        verify_ok(&gl);
    }

    #[test]
    fn new_invalid_url_1() {
        let gl = GitLab::new("", "XXXXXXXXXXXXXXXXXXXX");
        verify_err(&gl);

        let gl = GitLab::new_insecure("", "XXXXXXXXXXXXXXXXXXXX");
        verify_err(&gl);
    }

    #[test]
    fn new_invalid_url_2() {
        let gl = GitLab::new("gitla/b.com", "XXXXXXXXXXXXXXXXXXXX");
        verify_err(&gl);

        let gl = GitLab::new_insecure("gitla/b.com", "XXXXXXXXXXXXXXXXXXXX");
        verify_err(&gl);
    }

    #[test]
    fn new_invalid_url_3() {
        let gl = GitLab::new("/gitlab.com", "XXXXXXXXXXXXXXXXXXXX");
        verify_err(&gl);

        let gl = GitLab::new_insecure("/gitlab.com", "XXXXXXXXXXXXXXXXXXXX");
        verify_err(&gl);
    }

    #[test]
    fn new_invalid_url_4() {
        let gl = GitLab::new("http:/gitlab.com", "XXXXXXXXXXXXXXXXXXXX");
        verify_err(&gl);

        let gl = GitLab::new_insecure("http:/gitlab.com", "XXXXXXXXXXXXXXXXXXXX");
        verify_err(&gl);
    }

    #[test]
    fn new_invalid_url_5() {
        let gl = GitLab::new("http:///gitlab.com", "XXXXXXXXXXXXXXXXXXXX");
        verify_err(&gl);

        let gl = GitLab::new_insecure("http:///gitlab.com", "XXXXXXXXXXXXXXXXXXXX");
        verify_err(&gl);
    }

    #[test]
    fn new_invalid_url_6() {
        let gl = GitLab::new(".gitlab", "XXXXXXXXXXXXXXXXXXXX");
        verify_err(&gl);

        let gl = GitLab::new_insecure(".gitlab", "XXXXXXXXXXXXXXXXXXXX");
        verify_err(&gl);
    }

    #[test]
    fn new_invalid_url_7() {
        let gl = GitLab::new(".gitlab.com", "XXXXXXXXXXXXXXXXXXXX");
        verify_err(&gl);

        let gl = GitLab::new_insecure(".gitlab.com", "XXXXXXXXXXXXXXXXXXXX");
        verify_err(&gl);
    }

    #[test]
    fn new_invalid_url_8() {
        let gl = GitLab::new("gitlab.", "XXXXXXXXXXXXXXXXXXXX");
        verify_err(&gl);

        let gl = GitLab::new_insecure("gitlab.", "XXXXXXXXXXXXXXXXXXXX");
        verify_err(&gl);
    }

    #[test]
    fn new_invalid_url_10() {
        let gl = GitLab::new("gitlab.com.", "XXXXXXXXXXXXXXXXXXXX");
        verify_err(&gl);

        let gl = GitLab::new_insecure("gitlab.com.", "XXXXXXXXXXXXXXXXXXXX");
        verify_err(&gl);
    }

    #[test]
    fn new_invalid_token() {
        let gl = GitLab::new("gitlab.com", "");
        assert!(gl.is_err());

        let gl = GitLab::new("gitlab.com", "X");
        assert!(gl.is_err());

        let gl = GitLab::new("gitlab.com", "XXXXXXXXXXXXXXXXXXX");
        assert!(gl.is_err());

        let gl = GitLab::new("gitlab.com", "XXXXXXXXXXXXXXXXXXXXX");
        assert!(gl.is_err());
    }
}
<commit_msg>New unit test: `gitlab_listers_issues()`<commit_after>
use std::fmt;
use std::io::Read;  // Trait providing read_to_string()
use std::env;

use url;
use hyper;
use serde;
use serde_json;

// use Groups;

use ::errors::*;

pub const API_VERSION: u16 = 3;

#[derive(Default, Clone, Copy, Debug)]
pub struct Pagination {
    pub page: u16,
    pub per_page: u16,
}

pub struct GitLab {
    url: url::Url,
    private_token: String,
    pagination: Option<Pagination>,
    client: hyper::Client,
}

// Explicitly implement Debug trait for GitLab so we can hide the token.
impl fmt::Debug for GitLab {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f,
               "GitLab {{ scheme: {}, domain: {}, port: {}, private_token: XXXXXXXXXXXXXXXXXXXX, \
                pagination: {:?} }}",
               self.url.scheme(),
               self.url.domain().unwrap_or("bad hostname provided"),
               self.url
                   .port()
                   .map(|port_u16| port_u16.to_string())
                   .unwrap_or("no port provided".to_string()),
               self.pagination)
    }
}

fn validate_url(scheme: &str, domain: &str, port: u16) -> Result<url::Url> {

    match domain.find('.') {
        None => {
            // pass
        }
        Some(index) => {
            if index == 0 {
                bail!(format!("invalid domain: '{}' cannot start with a dot", domain));
            }
        }
    };

    if domain.ends_with('.') {
        bail!(format!("invalid domain: '{}' cannot end with a dot", domain));
    }

    let url_string = format!("{}://{}/api/v{}/", scheme, domain, API_VERSION);
    let mut url = url::Url::parse(&url_string)
        .chain_err(|| format!("failure to parse URL '{}'", url_string))?;
    url.set_port(Some(port)).expect("bad port provided");

    {
        let url_host = url.host_str();
        if url_host.is_none() {
            bail!("failure to get URL's hostname");
        }
        if url_host.unwrap() != domain {
            bail!(format!("invalid hostname '{}'", domain));
        }
    }

    Ok(url)
}

impl GitLab {
    pub fn _new(scheme: &str, domain: &str, port: u16, private_token: &str) -> Result<GitLab> {
        if private_token.len() != 20 {
            bail!(format!("private token should be a 20 characters string (not {})",
                          private_token.len()));
        }

        let url: url::Url = validate_url(scheme, domain, port).chain_err(|| "invalid URL")?;

        Ok(GitLab {
            url: url,
            private_token: private_token.to_string(),
            pagination: None,
            client: match env::var("HTTP_PROXY") {
                Ok(proxy) => {
                    let proxy: Vec<&str> = proxy.trim_left_matches("http://").split(':').collect();
                    let hostname = proxy[0].to_string();
                    let port = proxy[1].parse()
                        .chain_err(|| format!("failure to set port to {}", port))?;

                    hyper::Client::with_http_proxy(hostname, port)
                }
                Err(_) => hyper::Client::new(),
            },
        })
    }

    pub fn new_insecure(domain: &str, private_token: &str) -> Result<GitLab> {
        warn!("Using insecure http:// protocol: Token will be sent in clear!");
        GitLab::_new("http", domain, 80, private_token)
    }

    pub fn new(domain: &str, private_token: &str) -> Result<GitLab> {
        GitLab::_new("https", domain, 443, private_token)
    }

    pub fn port(mut self, port: u16) -> Self {
        self.url.set_port(Some(port)).unwrap();
        self
    }

    pub fn scheme(mut self, scheme: &str) -> Self {
        self.url.set_scheme(scheme).unwrap();
        self
    }

    /// Build a URL used to access GitLab instance, including some parameters.
    ///
    /// # Examples
    ///
    /// Example from GitLab: https://docs.gitlab.com/ce/api/#basic-usage
    ///
    /// ```
    /// use gitlab_api::GitLab;
    ///
    /// let expected_url = "https://gitlab.example.com\
    ///                     /api/v3/groups?order_by=path&private_token=XXXXXXXXXXXXXXXXXXXX";
    ///
    /// let gl = GitLab::new("gitlab.example.com", "XXXXXXXXXXXXXXXXXXXX").unwrap();
    ///
    /// assert_eq!(gl.build_url("groups?order_by=path").unwrap(), expected_url);
    /// ```
    pub fn build_url(&self, query: &str) -> Result<String> {
        let mut new_url = self.url
            .clone()
            .join(query)
            .chain_err(|| {
                format!("Failure to join query '{}' to url {}",
                        query,
                        self.url.as_str())
            })?;
        new_url.query_pairs_mut().append_pair("private_token", &self.private_token);
        self.pagination.as_ref().map(|pagination| {
            new_url.query_pairs_mut().append_pair("page", &pagination.page.to_string());
            new_url.query_pairs_mut().append_pair("per_page", &pagination.per_page.to_string());
        });

        Ok(new_url.into_string())
    }

    // pub fn attempt_connection(&self) -> Result<hyper::client::Response, hyper::Error> {
    //     let url = self.build_url("version");
    //     // Close connections after each GET.
    //     self.client.get(&url).header(hyper::header::Connection::close()).send()
    // }

    /// Set pagination information
    ///
    /// # Examples
    ///
    /// ```
    /// use gitlab_api::{GitLab, Pagination};
    ///
    /// let expected_url = "https://gitlab.example.com\
    ///                     /api/v3/groups?order_by=path&\
    ///                     private_token=XXXXXXXXXXXXXXXXXXXX&page=2&per_page=5";
    ///
    /// let mut gl = GitLab::new("gitlab.example.com", "XXXXXXXXXXXXXXXXXXXX").unwrap();
    /// gl.set_pagination(Pagination {page: 2, per_page: 5});
    /// assert_eq!(gl.build_url("groups?order_by=path").unwrap(), expected_url);
    /// ```
    pub fn set_pagination(&mut self, pagination: Pagination) {
        self.pagination = Some(pagination);
    }

    pub fn get<T>(&self, query: &str) -> Result<T>
        where T: serde::Deserialize
    {
        let url = self.build_url(query)
            .chain_err(|| format!("failure to build url for query '{}'", query))?;
        info!("url: {:?}", url);

        // Close connections after each GET.
        let mut res: hyper::client::Response = self.client
            .get(&url)
            .header(hyper::header::Connection::close())
            .send()
            .chain_err(|| format!("cannot send request '{}' to {:?}", query, self))?;
        info!("res.status: {:?}", res.status);
        debug!("res.headers: {:?}", res.headers);
        debug!("res.url: {:?}", res.url);

        let mut body = String::new();
        res.read_to_string(&mut body).chain_err(|| "cannot read response body")?;
        debug!("body:\n{:?}", body);

        if res.status != hyper::status::StatusCode::Ok {
            bail!(format!("status code '{}', not '200 OK'", res.status));
        }

        serde_json::from_str(body.as_str())
            .chain_err(|| format!("cannot build Rust struct from JSON data: {}", body))
    }

    pub fn version(&self) -> Result<::Version> {
        self.get("version").chain_err(|| "cannot query 'version'")
    }

    pub fn groups(&self) -> ::groups::GroupsLister {
        ::groups::GroupsLister::new(self)
    }

    pub fn projects(&self) -> ::projects::ProjectsLister {
        ::projects::ProjectsLister::new(self)
    }

    pub fn issues(&self) -> ::issues::IssuesLister {
        ::issues::IssuesLister::new(self)
    }

    pub fn merge_requests(&self, project_id: i64) -> ::merge_requests::MergeRequestsLister {
        ::merge_requests::MergeRequestsLister::new(self, project_id)
    }

    // pub fn groups(&mut self, listing: ::groups::Listing) -> Result<Groups, serde_json::Error> {
    //     let query = listing.build_query();
    //     // self.get(&query)
    //     unimplemented!();
    // }

    // pub fn owned_groups(&mut self) -> Result<Groups, serde_json::Error> {
    //     let query = ::groups::owned_groups::Listing::new().build_query();
    //     info!("query: {:?}", query);
    //     self.get(&query)
    // }
}

#[cfg(test)]
mod tests {
    use std::fmt;
    use gitlab::*;
    use errors::*;

    fn verify_ok<T>(result: &Result<T>) {
        if let &Err(ref e) = result {
            println!("error: {}", e);

            for e in e.iter().skip(1) {
                println!("caused by: {}", e);
            }

            // The backtrace is not always generated. Try to run this example
            // with `RUST_BACKTRACE=1`.
            if let Some(backtrace) = e.backtrace() {
                println!("backtrace: {:?}", backtrace);
            }
        }
        assert!(result.is_ok());
    }

    fn verify_err<T>(result: &Result<T>)
        where T: fmt::Debug
    {
        match result {
            &Err(_) => {
                // pass
            }
            &Ok(ref t) => {
                panic!(format!("Expected an Err(), got an Ok(t), with t: {:?}", t));
            }
        }
    }

    #[test]
    fn impl_debug_for_gitlab() {
        let gl = GitLab::new("gitlab.com", "XXXXXXXXXXXXXXXXXXXX").unwrap();

        let debug = format!("{:?}", gl);
        assert_eq!("GitLab { scheme: https, domain: gitlab.com, port: no port provided, \
                    private_token: XXXXXXXXXXXXXXXXXXXX, pagination: None }",
                   debug);

        let gl = gl.scheme("http").port(80);
        let debug = format!("{:?}", gl);
        assert_eq!("GitLab { scheme: http, domain: gitlab.com, port: no port provided, \
                    private_token: XXXXXXXXXXXXXXXXXXXX, pagination: None }",
                   debug);

        let mut gl = gl.port(81);
        let debug = format!("{:?}", gl);
        assert_eq!("GitLab { scheme: http, domain: gitlab.com, port: 81, private_token: \
                    XXXXXXXXXXXXXXXXXXXX, pagination: None }",
                   debug);

        gl.set_pagination(Pagination {
            page: 2,
            per_page: 5,
        });
        let debug = format!("{:?}", gl);
        assert_eq!("GitLab { scheme: http, domain: gitlab.com, port: 81, private_token: \
                    XXXXXXXXXXXXXXXXXXXX, pagination: Some(Pagination { page: 2, per_page: 5 }) }",
                   debug);
    }

    #[test]
    fn gitlab_listers_groups() {
        let gl = GitLab::new("gitlab.com", "XXXXXXXXXXXXXXXXXXXX").unwrap();
        let groups_lister = gl.groups();
        let debug = format!("{:?}", groups_lister);
        assert_eq!("GroupsLister { gl: GitLab { scheme: https, domain: gitlab.com, port: no \
                    port provided, private_token: XXXXXXXXXXXXXXXXXXXX, pagination: None }, \
                    internal: GroupsListerInternal { skip_groups: None, all_available: None, \
                    search: None, order_by: None, sort: None } }",
                   debug);
    }

    #[test]
    fn gitlab_listers_projects() {
        let gl = GitLab::new("gitlab.com", "XXXXXXXXXXXXXXXXXXXX").unwrap();
        let projects_lister = gl.projects();
        let debug = format!("{:?}", projects_lister);
        assert_eq!("ProjectsLister { gl: GitLab { scheme: https, domain: gitlab.com, port: no \
                    port provided, private_token: XXXXXXXXXXXXXXXXXXXX, pagination: None }, \
                    internal: ProjectListerInternal { archived: None, visibility: None, \
                    order_by: None, sort: None, search: None, simple: None } }",
                   debug);
    }

    #[test]
    fn gitlab_listers_issues() {
        let gl = GitLab::new("gitlab.com", "XXXXXXXXXXXXXXXXXXXX").unwrap();
        let issues_lister = gl.issues();
        let debug = format!("{:?}", issues_lister);
        assert_eq!("IssuesLister { gl: GitLab { scheme: https, domain: gitlab.com, port: no port \
                    provided, private_token: XXXXXXXXXXXXXXXXXXXX, pagination: None }, internal: \
                    IssuesListerInternal { state: None, labels: None, order_by: None, sort: None \
                    } }",
                   debug);
    }

    #[test]
    fn new_valid() {
        let gl = GitLab::new("gitlab.com", "XXXXXXXXXXXXXXXXXXXX");
        verify_ok(&gl);

        let gl = GitLab::new_insecure("gitlab.com", "XXXXXXXXXXXXXXXXXXXX");
        verify_ok(&gl);

        let gl = GitLab::new("localhost", "XXXXXXXXXXXXXXXXXXXX");
        verify_ok(&gl);

        let gl = GitLab::new_insecure("localhost", "XXXXXXXXXXXXXXXXXXXX");
        verify_ok(&gl);
    }

    #[test]
    fn new_invalid_url_1() {
        let gl = GitLab::new("", "XXXXXXXXXXXXXXXXXXXX");
        verify_err(&gl);

        let gl = GitLab::new_insecure("", "XXXXXXXXXXXXXXXXXXXX");
        verify_err(&gl);
    }

    #[test]
    fn new_invalid_url_2() {
        let gl = GitLab::new("gitla/b.com", "XXXXXXXXXXXXXXXXXXXX");
        verify_err(&gl);

        let gl = GitLab::new_insecure("gitla/b.com", "XXXXXXXXXXXXXXXXXXXX");
        verify_err(&gl);
    }

    #[test]
    fn new_invalid_url_3() {
        let gl = GitLab::new("/gitlab.com", "XXXXXXXXXXXXXXXXXXXX");
        verify_err(&gl);

        let gl = GitLab::new_insecure("/gitlab.com", "XXXXXXXXXXXXXXXXXXXX");
        verify_err(&gl);
    }

    #[test]
    fn new_invalid_url_4() {
        let gl = GitLab::new("http:/gitlab.com", "XXXXXXXXXXXXXXXXXXXX");
        verify_err(&gl);

        let gl = GitLab::new_insecure("http:/gitlab.com", "XXXXXXXXXXXXXXXXXXXX");
        verify_err(&gl);
    }

    #[test]
    fn new_invalid_url_5() {
        let gl = GitLab::new("http:///gitlab.com", "XXXXXXXXXXXXXXXXXXXX");
        verify_err(&gl);

        let gl = GitLab::new_insecure("http:///gitlab.com", "XXXXXXXXXXXXXXXXXXXX");
        verify_err(&gl);
    }

    #[test]
    fn new_invalid_url_6() {
        let gl = GitLab::new(".gitlab", "XXXXXXXXXXXXXXXXXXXX");
        verify_err(&gl);

        let gl = GitLab::new_insecure(".gitlab", "XXXXXXXXXXXXXXXXXXXX");
        verify_err(&gl);
    }

    #[test]
    fn new_invalid_url_7() {
        let gl = GitLab::new(".gitlab.com", "XXXXXXXXXXXXXXXXXXXX");
        verify_err(&gl);

        let gl = GitLab::new_insecure(".gitlab.com", "XXXXXXXXXXXXXXXXXXXX");
        verify_err(&gl);
    }

    #[test]
    fn new_invalid_url_8() {
        let gl = GitLab::new("gitlab.", "XXXXXXXXXXXXXXXXXXXX");
        verify_err(&gl);

        let gl = GitLab::new_insecure("gitlab.", "XXXXXXXXXXXXXXXXXXXX");
        verify_err(&gl);
    }

    #[test]
    fn new_invalid_url_10() {
        let gl = GitLab::new("gitlab.com.", "XXXXXXXXXXXXXXXXXXXX");
        verify_err(&gl);

        let gl = GitLab::new_insecure("gitlab.com.", "XXXXXXXXXXXXXXXXXXXX");
        verify_err(&gl);
    }

    #[test]
    fn new_invalid_token() {
        let gl = GitLab::new("gitlab.com", "");
        assert!(gl.is_err());

        let gl = GitLab::new("gitlab.com", "X");
        assert!(gl.is_err());

        let gl = GitLab::new("gitlab.com", "XXXXXXXXXXXXXXXXXXX");
        assert!(gl.is_err());

        let gl = GitLab::new("gitlab.com", "XXXXXXXXXXXXXXXXXXXXX");
        assert!(gl.is_err());
    }
}
<|endoftext|>


<commit_before>//! The handle into NFQueue, necessary for library setup.
//!
//! Analagous to <http://netfilter.org/projects/libnetfilter_queue/doxygen/group__LibrarySetup.html>

use libc::*;
use std::mem;
use std::ptr::null;

use error::*;
use queue::{Queue, PacketHandler};
use message::Message;
use lock::NFQ_LOCK as LOCK;

use ffi::*;

/// Protocol Family
///
/// NFQueue will only deal with IP, so only those families are made available.
pub enum ProtocolFamily {
    /// IPv4 Address Family
    INET = AF_INET as isize,
    /// IPv4 Address Family
    INET6 = AF_INET6 as isize
}

/// A handle into NFQueue
///
/// This is needed for library setup.
pub struct Handle { pub ptr: *mut nfq_handle }

impl Drop for Handle {
    fn drop(&mut self) {
        let ret = unsafe { nfq_close(self.ptr) };
        if ret != 0 {
            panic!("Failed to close NFQHandle");
        }
    }
}

impl Handle {
    /// Open a new handle to NFQueue
    ///
    /// This tells the kernel that userspace queuing will be handled for the selected protocol.
    pub fn new() -> Result<Handle, NFQError> {
        let _lock = LOCK.lock().unwrap();

        let ptr = unsafe { nfq_open() };
        if ptr.is_null() {
            Err(error(ErrorReason::OpenHandle, "Failed to allocate NFQ Handle", None))
        } else {
            Ok(Handle{ ptr: ptr })
        }
    }

    /// Bind the handle to a `Protocol Family`
    pub fn bind(&mut self, proto: ProtocolFamily) -> Result<(), NFQError> {
        let _lock = LOCK.lock().unwrap();

        let res = unsafe { nfq_bind_pf(self.ptr, proto as uint16_t) };
        if res < 0 {
            Err(error(ErrorReason::Bind, "Failed to bind NFQ Handle", Some(res)))
        } else {
            Ok(())
        }
    }

    /// Unbind the handle from a `Protocol Family`
    ///
    /// This should usually be avoided, as it may attach other programs from the `Protocol Family`.
    pub fn unbind(&mut self, proto: ProtocolFamily) -> Result<(), NFQError> {
        let _lock = LOCK.lock().unwrap();

        let res = unsafe { nfq_unbind_pf(self.ptr, proto as uint16_t) };
        if res < 0 {
            Err(error(ErrorReason::Unbind, "Failed to unbind NFQ Handle", Some(res)))
        } else {
            Ok(())
        }
    }

    pub fn queue<F: PacketHandler>(&mut self,
                                   queue_number: u16,
                                   handler: F) -> Result<Box<Queue<F>>, NFQError> {
        Queue::new(self.ptr, queue_number, handler)
    }

    pub fn start(&mut self, length: u64) {
        unsafe {
            let buffer: *mut c_void = malloc(mem::size_of::<c_char>() as u64 * length as u64);
            if buffer.is_null() {
                panic!("Failed to allocate packet buffer");
            }
            let fd = nfq_fd(self.ptr);

            loop {
                match recv(fd, buffer, length, 0) {
                    rv if rv >=0 => { nfq_handle_packet(self.ptr, buffer as *mut c_char, rv as i32); },
                    _ => { break; }
                }
            }

            free(buffer as *mut c_void);
        }
    }
}
<commit_msg>Clean up warnings in handle<commit_after>//! The handle into NFQueue, necessary for library setup.
//!
//! Analagous to <http://netfilter.org/projects/libnetfilter_queue/doxygen/group__LibrarySetup.html>

use libc::*;
use std::mem;

use error::*;
use queue::{Queue, PacketHandler};
use lock::NFQ_LOCK as LOCK;

use ffi::*;

/// Protocol Family
///
/// NFQueue will only deal with IP, so only those families are made available.
pub enum ProtocolFamily {
    /// IPv4 Address Family
    INET = AF_INET as isize,
    /// IPv4 Address Family
    INET6 = AF_INET6 as isize
}

/// A handle into NFQueue
///
/// This is needed for library setup.
pub struct Handle { pub ptr: *mut nfq_handle }

impl Drop for Handle {
    fn drop(&mut self) {
        let ret = unsafe { nfq_close(self.ptr) };
        if ret != 0 {
            panic!("Failed to close NFQHandle");
        }
    }
}

impl Handle {
    /// Open a new handle to NFQueue
    ///
    /// This tells the kernel that userspace queuing will be handled for the selected protocol.
    pub fn new() -> Result<Handle, NFQError> {
        let _lock = LOCK.lock().unwrap();

        let ptr = unsafe { nfq_open() };
        if ptr.is_null() {
            Err(error(ErrorReason::OpenHandle, "Failed to allocate NFQ Handle", None))
        } else {
            Ok(Handle{ ptr: ptr })
        }
    }

    /// Bind the handle to a `Protocol Family`
    pub fn bind(&mut self, proto: ProtocolFamily) -> Result<(), NFQError> {
        let _lock = LOCK.lock().unwrap();

        let res = unsafe { nfq_bind_pf(self.ptr, proto as uint16_t) };
        if res < 0 {
            Err(error(ErrorReason::Bind, "Failed to bind NFQ Handle", Some(res)))
        } else {
            Ok(())
        }
    }

    /// Unbind the handle from a `Protocol Family`
    ///
    /// This should usually be avoided, as it may attach other programs from the `Protocol Family`.
    pub fn unbind(&mut self, proto: ProtocolFamily) -> Result<(), NFQError> {
        let _lock = LOCK.lock().unwrap();

        let res = unsafe { nfq_unbind_pf(self.ptr, proto as uint16_t) };
        if res < 0 {
            Err(error(ErrorReason::Unbind, "Failed to unbind NFQ Handle", Some(res)))
        } else {
            Ok(())
        }
    }

    pub fn queue<F: PacketHandler>(&mut self,
                                   queue_number: u16,
                                   handler: F) -> Result<Box<Queue<F>>, NFQError> {
        Queue::new(self.ptr, queue_number, handler)
    }

    pub fn start(&mut self, length: u64) {
        unsafe {
            let buffer: *mut c_void = malloc(mem::size_of::<c_char>() as u64 * length as u64);
            if buffer.is_null() {
                panic!("Failed to allocate packet buffer");
            }
            let fd = nfq_fd(self.ptr);

            loop {
                match recv(fd, buffer, length, 0) {
                    rv if rv >=0 => { nfq_handle_packet(self.ptr, buffer as *mut c_char, rv as i32); },
                    _ => { break; }
                }
            }

            free(buffer as *mut c_void);
        }
    }
}
<|endoftext|>


<commit_before>use std::fmt;
use std::from_str::FromStr;
use std::collections::HashMap;
use std::collections::hashmap::{Occupied, Vacant};
use std::slice::Items;

use super::utils::MultiIter;

/// Trait for converting from RFC822 Header values into
/// Rust types.
pub trait FromHeader {
    /// Parse the `value` of the header.
    ///
    /// Returns None if the value failed to be parsed
    fn from_header(value: String) -> Option<Self>;
}

impl FromHeader for String {
    fn from_header(value: String) -> Option<String> {
        Some(value)
    }
}

/// Represents an RFC 822 Header
#[deriving(PartialEq, Eq, Clone, Hash)]
pub struct Header {
    /// The name of this header
    pub name: String,
    value: String,
}

impl Header {
    /// Creates a new Header for the given `name` and `value`
    pub fn new(name: String, value: String) -> Header {
        Header {
            name: name,
            value: value,
        }
    }

    /// Get the value represented by this header, as parsed
    /// into whichever type `T`
    pub fn get_value<T: FromHeader>(&self) -> Option<T> {
        FromHeader::from_header(self.value.clone())
    }
}

impl fmt::Show for Header {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        let mut lines = self.value.as_slice().split('\n');
        // Take the first line as the start of the formatted value
        let mut formatted_value = lines.next().unwrap().to_string();
        for line in lines {
            // Add the separator between lines
            // RFC 822 uses linear whitespace (tabs or spaces) to indicate
            // a continuation of a header value over a newline
            formatted_value.push_str("\r\n\t");
            formatted_value.push_str(line);
        }

        write!(fmt, "{}: {}", self.name, formatted_value)
    }
}

impl FromStr for Header {
    fn from_str(s: &str) -> Option<Header> {
        // Split the header on the first semicolon
        let mut parts = s.splitn(2, ':');
        let before = parts.next();
        let after = parts.next();

        match (before, after) {
            // If either value is None, then this isn't a header
            (None, None) | (_, None) | (None, _) => None,
            (Some(ref name), Some(ref value)) => {
                Some(Header::new(
                    name.trim().to_string(),
                    value.trim().to_string()
                ))
            }
        }
    }
}

/// A collection of Headers
pub struct HeaderMap {
    headers: HashMap<String, Vec<Header>>,
}

impl HeaderMap {
    pub fn new() -> HeaderMap {
        HeaderMap {
            headers: HashMap::new()
        }
    }

    /// Adds a header to the collection
    pub fn insert(&mut self, header: Header) {
        // If the header hashmap already has this header, use that list.
        // Otherwise, make a new one.
        let header_list = match self.headers.entry(header.name.clone()) {
            Vacant(entry) => entry.set(Vec::new()),
            Occupied(entry) => entry.into_mut(),
        };
        // ... and add the new header to it
        header_list.push(header);
    }

    /// Get an Iterator over the collection of headers.
    pub fn iter(&self) -> MultiIter<&Header, Items<Header>> {
        let mut iters = Vec::new();
        for header_list in self.headers.values() {
            iters.push(header_list.iter());
        }
        MultiIter::new(iters)
    }
}

impl fmt::Show for HeaderMap {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        for header in self.iter() {
            match write!(fmt, "{}\r\n", header) {
                Err(e) => return Err(e),
                _ => {}
            }
        }
        Ok(())
    }
}

impl Collection for HeaderMap {
    fn len(&self) -> uint {
        self.iter().count()
    }
}

impl Map<String, Vec<Header>> for HeaderMap {
    fn find(&self, key: &String) -> Option<&Vec<Header>> {
        self.headers.find(key)
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use std::collections::HashSet;

    static SAMPLE_HEADERS: [&'static str, ..4] = [
        "Test: Value",
        "Test : Value 2",
        "Test-2: Value 3",
        "Test-Multiline: Foo\nBar",
    ];

    #[test]
    fn test_header_to_string() {
        let header = Header::new("Test".to_string(), "Value".to_string());
        assert_eq!(header.to_string(), "Test: Value".to_string());
    }

    #[test]
    fn test_multiline_header_to_string() {
        let header = Header::new("Test".to_string(), "Value\nOver lines".to_string());
        assert_eq!(header.to_string(), "Test: Value\r\n\tOver lines".to_string());
    }

    #[test]
    fn test_string_get_value() {
        let header = Header::new("Test".to_string(), "Value".to_string());
        let string_value: String = header.get_value().unwrap();
        assert_eq!(string_value, "Value".to_string());
    }

    #[test]
    fn test_parse_header() {
        let vals = ["Test: Value", "Test : Value"];
        for &val in vals.iter() {
            let header: Header = from_str(val).unwrap();
            assert_eq!(header.name, "Test".to_string());
            assert_eq!(header.value, "Value".to_string());
        }
    }

    #[test]
    fn test_parse_header_roundtrip() {
        let header: Header = from_str("Test: Value").unwrap();
        let header_string = header.to_string();
        assert_eq!(header_string, "Test: Value".to_string());
    }

    #[test]
    fn test_header_map_len() {
        let mut headers = HeaderMap::new();
        for (i, &val) in SAMPLE_HEADERS.iter().enumerate() {
            let header: Header = from_str(val).unwrap();
            headers.insert(header);
            assert_eq!(headers.len(), i + 1);
        }
    }

    #[test]
    fn test_header_map_iter() {
        let mut headers = HeaderMap::new();
        let mut expected_headers = HashSet::new();
        for &val in SAMPLE_HEADERS.iter() {
            let header: Header = from_str(val).unwrap();
            headers.insert(header.clone());
            expected_headers.insert(header);
        }

        let mut count = 0u;
        // Ensure all the headers returned are expected
        for header in headers.iter() {
            assert!(expected_headers.contains(header));
            count += 1;
        }
        // And that there is the right number of them
        assert_eq!(count, expected_headers.len());
    }

    #[test]
    fn test_header_map_string() {
        let mut headers = HeaderMap::new();
        for &val in SAMPLE_HEADERS.iter() {
            let header: Header = from_str(val).unwrap();
            headers.insert(header.clone());
        }
        let result = headers.to_string();
        let slice = result.as_slice();
        println!("{}", result);
        assert!(slice.contains("Test: Value\r\n"));
        assert!(slice.contains("Test: Value 2\r\n"));
        assert!(slice.contains("Test-2: Value 3\r\n"));
        assert!(slice.contains("Test-Multiline: Foo\r\n\tBar"));
    }
}
<commit_msg>Add HeaderMap parsing.<commit_after>use std::fmt;
use std::from_str::FromStr;
use std::collections::HashMap;
use std::collections::hashmap::{Occupied, Vacant};
use std::slice::Items;

use super::utils::MultiIter;

/// Trait for converting from RFC822 Header values into
/// Rust types.
pub trait FromHeader {
    /// Parse the `value` of the header.
    ///
    /// Returns None if the value failed to be parsed
    fn from_header(value: String) -> Option<Self>;
}

impl FromHeader for String {
    fn from_header(value: String) -> Option<String> {
        Some(value)
    }
}

/// Represents an RFC 822 Header
#[deriving(PartialEq, Eq, Clone, Hash)]
pub struct Header {
    /// The name of this header
    pub name: String,
    value: String,
}

impl Header {
    /// Creates a new Header for the given `name` and `value`
    pub fn new(name: String, value: String) -> Header {
        Header {
            name: name,
            value: value,
        }
    }

    /// Get the value represented by this header, as parsed
    /// into whichever type `T`
    pub fn get_value<T: FromHeader>(&self) -> Option<T> {
        FromHeader::from_header(self.value.clone())
    }
}

impl fmt::Show for Header {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        let mut lines = self.value.as_slice().split('\n');
        // Take the first line as the start of the formatted value
        let mut formatted_value = lines.next().unwrap().to_string();
        for line in lines {
            // Add the separator between lines
            // RFC 822 uses linear whitespace (tabs or spaces) to indicate
            // a continuation of a header value over a newline
            formatted_value.push_str("\r\n\t");
            formatted_value.push_str(line);
        }

        write!(fmt, "{}: {}", self.name, formatted_value)
    }
}

impl FromStr for Header {
    fn from_str(s: &str) -> Option<Header> {
        // Split the header on the first semicolon
        let mut parts = s.splitn(2, ':');
        let before = parts.next();
        let after = parts.next();

        match (before, after) {
            // If either value is None, then this isn't a header
            (None, None) | (_, None) | (None, _) => None,
            (Some(ref name), Some(ref value)) => {
                Some(Header::new(
                    name.trim().to_string(),
                    value.trim().to_string()
                ))
            }
        }
    }
}

/// A collection of Headers
pub struct HeaderMap {
    headers: HashMap<String, Vec<Header>>,
}

impl HeaderMap {
    pub fn new() -> HeaderMap {
        HeaderMap {
            headers: HashMap::new()
        }
    }

    /// Adds a header to the collection
    pub fn insert(&mut self, header: Header) {
        // If the header hashmap already has this header, use that list.
        // Otherwise, make a new one.
        let header_list = match self.headers.entry(header.name.clone()) {
            Vacant(entry) => entry.set(Vec::new()),
            Occupied(entry) => entry.into_mut(),
        };
        // ... and add the new header to it
        header_list.push(header);
    }

    /// Get an Iterator over the collection of headers.
    pub fn iter(&self) -> MultiIter<&Header, Items<Header>> {
        let mut iters = Vec::new();
        for header_list in self.headers.values() {
            iters.push(header_list.iter());
        }
        MultiIter::new(iters)
    }
}

impl fmt::Show for HeaderMap {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        for header in self.iter() {
            match write!(fmt, "{}\r\n", header) {
                Err(e) => return Err(e),
                _ => {}
            }
        }
        Ok(())
    }
}

impl FromStr for HeaderMap {
    fn from_str(s: &str) -> Option<HeaderMap> {
        #[deriving(PartialEq, Eq)]
        enum State {
            SeenCr,
            SeenLf,
            Awaiting,
        }

        let mut start = 0u;
        let mut pos = 0u;
        let mut state = Awaiting;
        let mut header_map = HeaderMap::new();

        while pos < s.len() {
            let c = s.char_range_at(pos);
            state = match c.ch {
                '\r' => SeenCr,
                '\n' if state == SeenCr => SeenLf,
                '\t' | ' ' if state == SeenLf => Awaiting,
                _ if state == SeenLf => {
                    let header = from_str(s.slice_chars(start, pos));
                    if header.is_none() {
                        return None
                    }
                    header_map.insert(header.unwrap());
                    // Move the next slice on
                    start = pos;
                    Awaiting
                },
                _ => Awaiting
            };
            pos = c.next;
        }

        // Handle the final header
        let final_header = from_str(s.slice_from(start));
        if final_header.is_none() {
            None
        } else {
            header_map.insert(final_header.unwrap());
            Some(header_map)
        }
    }
}

impl Collection for HeaderMap {
    fn len(&self) -> uint {
        self.iter().count()
    }
}

impl Map<String, Vec<Header>> for HeaderMap {
    fn find(&self, key: &String) -> Option<&Vec<Header>> {
        self.headers.find(key)
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use std::collections::HashSet;

    static SAMPLE_HEADERS: [&'static str, ..4] = [
        "Test: Value",
        "Test : Value 2",
        "Test-2: Value 3",
        "Test-Multiline: Foo\nBar",
    ];

    #[test]
    fn test_header_to_string() {
        let header = Header::new("Test".to_string(), "Value".to_string());
        assert_eq!(header.to_string(), "Test: Value".to_string());
    }

    #[test]
    fn test_multiline_header_to_string() {
        let header = Header::new("Test".to_string(), "Value\nOver lines".to_string());
        assert_eq!(header.to_string(), "Test: Value\r\n\tOver lines".to_string());
    }

    #[test]
    fn test_string_get_value() {
        let header = Header::new("Test".to_string(), "Value".to_string());
        let string_value: String = header.get_value().unwrap();
        assert_eq!(string_value, "Value".to_string());
    }

    #[test]
    fn test_parse_header() {
        let vals = ["Test: Value", "Test : Value"];
        for &val in vals.iter() {
            let header: Header = from_str(val).unwrap();
            assert_eq!(header.name, "Test".to_string());
            assert_eq!(header.value, "Value".to_string());
        }
    }

    #[test]
    fn test_parse_header_roundtrip() {
        let header: Header = from_str("Test: Value").unwrap();
        let header_string = header.to_string();
        assert_eq!(header_string, "Test: Value".to_string());
    }

    #[test]
    fn test_header_map_len() {
        let mut headers = HeaderMap::new();
        for (i, &val) in SAMPLE_HEADERS.iter().enumerate() {
            let header: Header = from_str(val).unwrap();
            headers.insert(header);
            assert_eq!(headers.len(), i + 1);
        }
    }

    #[test]
    fn test_header_map_iter() {
        let mut headers = HeaderMap::new();
        let mut expected_headers = HashSet::new();
        for &val in SAMPLE_HEADERS.iter() {
            let header: Header = from_str(val).unwrap();
            headers.insert(header.clone());
            expected_headers.insert(header);
        }

        let mut count = 0u;
        // Ensure all the headers returned are expected
        for header in headers.iter() {
            assert!(expected_headers.contains(header));
            count += 1;
        }
        // And that there is the right number of them
        assert_eq!(count, expected_headers.len());
    }

    #[test]
    fn test_header_map_string() {
        let mut headers = HeaderMap::new();
        for &val in SAMPLE_HEADERS.iter() {
            let header: Header = from_str(val).unwrap();
            headers.insert(header.clone());
        }
        let result = headers.to_string();
        let slice = result.as_slice();
        assert!(slice.contains("Test: Value\r\n"));
        assert!(slice.contains("Test: Value 2\r\n"));
        assert!(slice.contains("Test-2: Value 3\r\n"));
        assert!(slice.contains("Test-Multiline: Foo\r\n\tBar"));
    }

    #[test]
    fn test_header_map_parse() {
        let headers = from_str::<HeaderMap>("Test: Value\r\nTest: Value 2\r\nTest-2: Value 3\r\nTest-Multiline: Foo\r\n\tBar\r\n").unwrap();
        println!("{}", headers);
        assert_eq!(headers.len(), 4);
        assert_eq!(
            headers.find(&"Test".to_string()).unwrap(), &vec![
                Header::new("Test".to_string(), "Value".to_string()),
                Header::new("Test".to_string(), "Value 2".to_string()),
        ]);
        assert_eq!(
            headers.find(&"Test-2".to_string()).unwrap(), &vec![
                Header::new("Test-2".to_string(), "Value 3".to_string()),
        ]);
        assert_eq!(
            headers.find(&"Test-Multiline".to_string()).unwrap(), &vec![
                Header::new("Test-Multiline".to_string(), "Foo\r\n\tBar".to_string()),
        ]);
    }
}
<|endoftext|>


<commit_before>
//! Implements generic error type

use curl;
use git2;
use serde_json;
use std;
use url;

#[derive(Debug)]
pub enum GerritError {
    ChangeInputProblem,
    GetReviewerListProblem(String),
    GerritApi(u32, String),
}

quick_error! {
    #[derive(Debug)]
    pub enum GGRError {
        Curl(err: curl::Error) {
            description(err.description())
            from()
        }
        FromUtf8(err: std::string::FromUtf8Error) {
            description(err.description())
            from()
        }
        General(err: String) {
            description(err)
            from()
        }
        Git2(err: git2::Error) {
            description(err.message())
            from()
        }
        JsonError(err: serde_json::error::Error) {
            description(err.description())
            from()
        }
        Num(err: std::num::ParseIntError) {
            description(err.description())
            from()
        }
        StdIo(err: std::io::Error) {
            description(err.description())
            from()
        }
        Url(err: url::ParseError) {
            description(err.description())
            from()
        }
        GerritApiError(err: GerritError) {
        }
    }
}

/// General result type
pub type GGRResult<T> = Result<T, GGRError>;

<commit_msg>use quick_error for GerritError in libgerrit<commit_after>
//! Implements generic error type

use curl;
use git2;
use serde_json;
use std;
use url;

quick_error! {
    #[derive(Debug)]
    pub enum GerritError {
        ChangeInputProblem {
            display("Problem with ChangeInput")
        }

        GetReviewerListProblem(r: String) {
            display("ProblemWithReviewer: {}", r)
        }

        /// The reviewer isn't found
        ReviewerNotFound {
            description("Reviewer not found")
            display("Reviewer not found")
        }

        GerritApi(status: u32, text: String) {
            display("HTTP status: {}, text: {}", status, text)
        }
    }
}

quick_error! {
    #[derive(Debug)]
    pub enum GGRError {
        Curl(err: curl::Error) {
            description(err.description())
            from()
        }
        FromUtf8(err: std::string::FromUtf8Error) {
            description(err.description())
            from()
        }
        General(err: String) {
            description(err)
            from()
        }
        HTTPError(status: u32) {
            description("HTTP Error")
            display("HTTP not success ({})", status)
        }
        Git2(err: git2::Error) {
            description(err.message())
            from()
        }
        JsonError(err: serde_json::error::Error) {
            description(err.description())
            from()
        }
        Num(err: std::num::ParseIntError) {
            description(err.description())
            from()
        }
        StdIo(err: std::io::Error) {
            description(err.description())
            from()
        }
        Url(err: url::ParseError) {
            description(err.description())
            from()
        }
        GerritApiError(err: GerritError) {
        }
    }
}

/// General result type
pub type GGRResult<T> = Result<T, GGRError>;

<|endoftext|>


<commit_before>#![feature(phase)]
#![crate_name = "asteroids"]

#[phase(plugin)]
extern crate gfx_macros;

fn main() {
	//empty
}
<commit_msg>Created an empty gl-init-rs window for asteroids<commit_after>#![feature(phase)]
#![crate_name = "asteroids"]

#[phase(plugin)]
extern crate gfx_macros;
extern crate gfx;
extern crate glinit = "gl-init-rs";
extern crate cgmath;
extern crate native;

//#[start]
//fn start(argc: int, argv: *const *const u8) -> int {
//     native::start(argc, argv, main)
//}

fn main() {
    let window = gfx::gl_init::Window::new().unwrap();
    window.set_title("Asteroids example for #scenegraph-rs");
    unsafe { window.make_current() };

    let (w, h) = window.get_inner_size().unwrap();

    let mut device = gfx::build()
        .with_context(&window)
        .with_provider(&window)
        .with_queue_size(1)
        .spawn(proc(r) render(r, w as u16, h as u16))
        .unwrap();

    'main: loop {
        // quit when Esc is pressed.
        for event in window.poll_events() {
            match event {
                glinit::Pressed(glinit::Escape) => break 'main,
                glinit::Closed => break 'main,
                _ => {},
            }
        }

        device.update();
    }

    device.update();
    device.close();
}

fn render(mut renderer: gfx::Renderer, width: u16, height: u16) {
    let frame = gfx::Frame::new(width as u16, height as u16);
    let clear = gfx::ClearData {
        color: Some(gfx::Color([0.3, 0.3, 0.3, 1.0])),
        depth: None,
        stencil: None,
    };

    while !renderer.should_finish() {
        renderer.clear(clear, frame);
        renderer.end_frame();
        for err in renderer.errors() {
            println!("Renderer error: {}", err);
        }
    }
}
<|endoftext|>


<commit_before>use std::cell::RefCell;
use std::cmp;
use std::io::{self, BufRead, Read, Write};
use std::net::SocketAddr;
use std::rc::Rc;
use std::time::Duration;

use futures::{Future, Stream};
use kcp::{Error as KcpError, get_conv};
use mio::{self, Evented, PollOpt, Ready, Registration, SetReadiness, Token};
use tokio_core::reactor::Handle;

use session::{KcpClientSession, KcpServerSession, KcpSession, KcpSessionMode, KcpSessionUpdater};
use skcp::SharedKcp;

/// Base Io object for KCP
struct KcpIo {
    kcp: SharedKcp,
    read_buf: Vec<u8>,
    read_pos: usize,
    read_cap: usize,
}

impl KcpIo {
    pub fn new(kcp: SharedKcp) -> KcpIo {
        let mtu = kcp.mtu();
        trace!("[INIT] KcpIo mtu {}", mtu);
        let mut buf = Vec::with_capacity(mtu);
        unsafe {
            buf.set_len(mtu);
        }

        KcpIo {
            kcp: kcp,
            read_buf: buf,
            read_pos: 0,
            read_cap: 0,
        }
    }

    /// Call everytime you got data from transmission
    pub fn input(&mut self, buf: &[u8]) -> io::Result<()> {
        self.kcp.input(buf).map_err(From::from)
    }

    /// MTU
    pub fn mtu(&self) -> usize {
        self.kcp.mtu()
    }

    /// Shutdown
    pub fn shutdown(&mut self) -> io::Result<()> {
        self.kcp.close();
        Ok(())
    }

    /// Check if can read
    pub fn can_read(&self) -> bool {
        self.kcp.can_read()
    }
}

impl BufRead for KcpIo {
    fn fill_buf(&mut self) -> io::Result<&[u8]> {
        if self.read_pos >= self.read_cap {
            loop {
                let n = match self.kcp.recv(&mut self.read_buf) {
                    Ok(n) => n,
                    Err(KcpError::UserBufTooSmall) => {
                        let orig = self.read_buf.len();
                        let incr = self.kcp.peeksize().next_power_of_two();
                        trace!("[RECV] kcp.recv buf too small, {} -> {}", orig, incr);
                        self.read_buf.resize(incr, 0);
                        continue;
                    }
                    Err(err) => {
                        return Err(From::from(err));
                    }
                };

                self.read_pos = 0;
                self.read_cap = n;

                let buf = &self.read_buf[..n];
                trace!("[RECV] kcp.recv size={} conv={} {:?}", n, get_conv(buf), ::debug::BsDebug(&buf));

                break;
            }
        }

        Ok(&self.read_buf[self.read_pos..self.read_cap])
    }

    fn consume(&mut self, amt: usize) {
        self.read_pos = cmp::min(self.read_cap, self.read_pos + amt);
    }
}

impl Read for KcpIo {
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
        let nread = {
            let mut available = self.fill_buf()?;
            available.read(buf)?
        };
        self.consume(nread);
        trace!("[RECV] KcpIo.read size={}", nread);
        Ok(nread)
    }
}

impl Write for KcpIo {
    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
        trace!("[SEND] KcpIo.write size={} {:?}", buf.len(), ::debug::BsDebug(buf));
        let n = self.kcp.send(buf)?;
        trace!("[SEND] kcp.send size={}", n);
        Ok(n)
    }

    fn flush(&mut self) -> io::Result<()> {
        self.kcp.flush()?;
        trace!("[SEND] Flushed KCP buffer");
        Ok(())
    }
}

impl Drop for KcpIo {
    fn drop(&mut self) {
        self.kcp.close()
    }
}

/// Io object for client
///
/// It doesn't need to have evented to be implemented
pub struct ClientKcpIo {
    io: KcpIo,
}

impl ClientKcpIo {
    pub fn new(kcp: SharedKcp, addr: SocketAddr, expire_dur: Duration, handle: &Handle) -> io::Result<ClientKcpIo> {
        let mut sess = KcpSession::new(kcp.clone(), addr, expire_dur, handle, KcpSessionMode::Client)?;
        sess.update()?; // Call update once it is created
        let sess = Rc::new(RefCell::new(sess));
        let sess = KcpClientSession::new(sess);
        handle.spawn(sess.for_each(|_| Ok(())).map_err(|err| {
                                                           error!("Failed to update KCP session: err: {:?}", err);
                                                       }));
        trace!("[CLIENT] Created stream bind addr={}", addr);
        Ok(ClientKcpIo { io: KcpIo::new(kcp) })
    }

    pub fn input(&mut self, buf: &[u8]) -> io::Result<()> {
        self.io.input(buf)
    }

    pub fn mtu(&self) -> usize {
        self.io.mtu()
    }
}

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

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

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

/// Io object for server
///
/// It implements `Evented` apis
pub struct ServerKcpIo {
    io: KcpIo,
    reg: Registration,
    readiness: SetReadiness,
}

impl ServerKcpIo {
    pub fn new(kcp: SharedKcp,
               addr: SocketAddr,
               expire_dur: Duration,
               handle: &Handle,
               u: &mut KcpSessionUpdater)
               -> io::Result<ServerKcpIo> {
        let sess = KcpSession::new_shared(kcp.clone(), addr, expire_dur, handle, KcpSessionMode::Server)?;
        let (reg, r) = Registration::new2();
        let sess = KcpServerSession::new(sess, r.clone());
        u.insert_by_conv(kcp.conv(), sess);

        Ok(ServerKcpIo {
               io: KcpIo::new(kcp),
               reg: reg,
               readiness: r,
           })
    }

    /// Call everytime you got data from transmission
    pub fn input(&mut self, data: &[u8]) -> io::Result<()> {
        self.io.input(data)?;
        if self.io.can_read() {
            self.readiness.set_readiness(Ready::readable())?;
        }
        Ok(())
    }

    /// Close it
    pub fn shutdown(&mut self) -> io::Result<()> {
        self.io.shutdown()
    }
}

impl Evented for ServerKcpIo {
    fn register(&self, poll: &mio::Poll, token: Token, interest: Ready, opts: PollOpt) -> io::Result<()> {
        self.reg.register(poll, token, interest, opts)
    }

    fn reregister(&self, poll: &mio::Poll, token: Token, interest: Ready, opts: PollOpt) -> io::Result<()> {
        self.reg.reregister(poll, token, interest, opts)
    }

    fn deregister(&self, poll: &mio::Poll) -> io::Result<()> {
        <Registration as Evented>::deregister(&self.reg, poll)
    }
}

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

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

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

// impl KcpIo {
//     pub fn new(shared_kcp: SharedKcp,
//                addr: SocketAddr,
//                handle: &Handle,
//                owner: Option<KcpSessionUpdater>,
//                expire_dur: Duration)
//                -> io::Result<KcpIo> {
//         let (registration, readiness) = Registration::new2();
//         let timer = Timeout::new_at(Instant::now(), handle)?;

//         let elapsed = Rc::new(RefCell::new(Instant::now()));
//         let close_flag = Rc::new(RefCell::new(false));
//         let session = SharedKcpSession::new(shared_kcp.clone(),
//                                             timer,
//                                             elapsed.clone(),
//                                             readiness.clone(),
//                                             addr,
//                                             owner,
//                                             close_flag.clone(),
//                                             expire_dur)?;
//         handle.spawn(session.for_each(|_| Ok(())).map_err(|err| {
//                                                               error!("Failed to update KCP session: err: {:?}", err);
//                                                           }));

//         Ok(KcpIo {
//                kcp: shared_kcp,
//                registration: registration,
//                readiness: readiness,
//                last_update: elapsed,
//                close_flag: close_flag,
//                read_buf: vec![0u8; 65535],
//                read_pos: 0,
//                read_cap: 0,
//            })
//     }

//     pub fn input_buf(&mut self, buf: &[u8]) -> io::Result<()> {
//         {
//             let mut last_update = self.last_update.borrow_mut();
//             *last_update = Instant::now();
//             let mut kcp = self.kcp.borrow_mut();
//             kcp.input(buf)?;
//             trace!("[INPUT] KcpIo.input size={}", buf.len());
//         }
//         self.set_readable()
//     }

//     fn set_readable(&mut self) -> io::Result<()> {
//         self.readiness.set_readiness(Ready::readable())
//     }

//     pub fn can_read(&self) -> io::Result<bool> {
//         let kcp = self.kcp.borrow();
//         kcp.peeksize().map(|n| n != 0)
//     }
// }

// impl Evented for KcpIo {
//     fn register(&self, poll: &mio::Poll, token: Token, interest: Ready, opts: PollOpt) -> io::Result<()> {
//         self.registration.register(poll, token, interest, opts)
//     }

//     fn reregister(&self, poll: &mio::Poll, token: Token, interest: Ready, opts: PollOpt) -> io::Result<()> {
//         self.registration.reregister(poll, token, interest, opts)
//     }

//     fn deregister(&self, poll: &mio::Poll) -> io::Result<()> {
//         <Registration as Evented>::deregister(&self.registration, poll)
//     }
// }
<commit_msg>Ignore empty response (EOF)<commit_after>use std::cell::RefCell;
use std::cmp;
use std::io::{self, BufRead, Read, Write};
use std::net::SocketAddr;
use std::rc::Rc;
use std::time::Duration;

use futures::{Future, Stream};
use kcp::{Error as KcpError, get_conv};
use mio::{self, Evented, PollOpt, Ready, Registration, SetReadiness, Token};
use tokio_core::reactor::Handle;

use session::{KcpClientSession, KcpServerSession, KcpSession, KcpSessionMode, KcpSessionUpdater};
use skcp::SharedKcp;

/// Base Io object for KCP
struct KcpIo {
    kcp: SharedKcp,
    read_buf: Vec<u8>,
    read_pos: usize,
    read_cap: usize,
}

impl KcpIo {
    pub fn new(kcp: SharedKcp) -> KcpIo {
        let mtu = kcp.mtu();
        trace!("[INIT] KcpIo mtu {}", mtu);
        let mut buf = Vec::with_capacity(mtu);
        unsafe {
            buf.set_len(mtu);
        }

        KcpIo {
            kcp: kcp,
            read_buf: buf,
            read_pos: 0,
            read_cap: 0,
        }
    }

    /// Call everytime you got data from transmission
    pub fn input(&mut self, buf: &[u8]) -> io::Result<()> {
        self.kcp.input(buf).map_err(From::from)
    }

    /// MTU
    pub fn mtu(&self) -> usize {
        self.kcp.mtu()
    }

    /// Shutdown
    pub fn shutdown(&mut self) -> io::Result<()> {
        self.kcp.close();
        Ok(())
    }

    /// Check if can read
    pub fn can_read(&self) -> bool {
        self.kcp.can_read()
    }
}

impl BufRead for KcpIo {
    fn fill_buf(&mut self) -> io::Result<&[u8]> {
        if self.read_pos >= self.read_cap {
            loop {
                let n = match self.kcp.recv(&mut self.read_buf) {
                    Ok(n) => n,
                    Err(KcpError::UserBufTooSmall) => {
                        let orig = self.read_buf.len();
                        let incr = self.kcp.peeksize().next_power_of_two();
                        trace!("[RECV] kcp.recv buf too small, {} -> {}", orig, incr);
                        self.read_buf.resize(incr, 0);
                        continue;
                    }
                    Err(err) => {
                        return Err(From::from(err));
                    }
                };

                self.read_pos = 0;
                self.read_cap = n;

                if n != 0 {
                    let buf = &self.read_buf[..n];
                    trace!("[RECV] kcp.recv size={} conv={} {:?}", n, get_conv(buf), ::debug::BsDebug(&buf));
                }

                break;
            }
        }

        Ok(&self.read_buf[self.read_pos..self.read_cap])
    }

    fn consume(&mut self, amt: usize) {
        self.read_pos = cmp::min(self.read_cap, self.read_pos + amt);
    }
}

impl Read for KcpIo {
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
        let nread = {
            let mut available = self.fill_buf()?;
            available.read(buf)?
        };
        self.consume(nread);
        trace!("[RECV] KcpIo.read size={}", nread);
        Ok(nread)
    }
}

impl Write for KcpIo {
    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
        trace!("[SEND] KcpIo.write size={} {:?}", buf.len(), ::debug::BsDebug(buf));
        let n = self.kcp.send(buf)?;
        trace!("[SEND] kcp.send size={}", n);
        Ok(n)
    }

    fn flush(&mut self) -> io::Result<()> {
        self.kcp.flush()?;
        trace!("[SEND] Flushed KCP buffer");
        Ok(())
    }
}

impl Drop for KcpIo {
    fn drop(&mut self) {
        self.kcp.close()
    }
}

/// Io object for client
///
/// It doesn't need to have evented to be implemented
pub struct ClientKcpIo {
    io: KcpIo,
}

impl ClientKcpIo {
    pub fn new(kcp: SharedKcp, addr: SocketAddr, expire_dur: Duration, handle: &Handle) -> io::Result<ClientKcpIo> {
        let mut sess = KcpSession::new(kcp.clone(), addr, expire_dur, handle, KcpSessionMode::Client)?;
        sess.update()?; // Call update once it is created
        let sess = Rc::new(RefCell::new(sess));
        let sess = KcpClientSession::new(sess);
        handle.spawn(sess.for_each(|_| Ok(())).map_err(|err| {
                                                           error!("Failed to update KCP session: err: {:?}", err);
                                                       }));
        trace!("[CLIENT] Created stream bind addr={}", addr);
        Ok(ClientKcpIo { io: KcpIo::new(kcp) })
    }

    pub fn input(&mut self, buf: &[u8]) -> io::Result<()> {
        self.io.input(buf)
    }

    pub fn mtu(&self) -> usize {
        self.io.mtu()
    }
}

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

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

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

/// Io object for server
///
/// It implements `Evented` apis
pub struct ServerKcpIo {
    io: KcpIo,
    reg: Registration,
    readiness: SetReadiness,
}

impl ServerKcpIo {
    pub fn new(kcp: SharedKcp,
               addr: SocketAddr,
               expire_dur: Duration,
               handle: &Handle,
               u: &mut KcpSessionUpdater)
               -> io::Result<ServerKcpIo> {
        let sess = KcpSession::new_shared(kcp.clone(), addr, expire_dur, handle, KcpSessionMode::Server)?;
        let (reg, r) = Registration::new2();
        let sess = KcpServerSession::new(sess, r.clone());
        u.insert_by_conv(kcp.conv(), sess);

        Ok(ServerKcpIo {
               io: KcpIo::new(kcp),
               reg: reg,
               readiness: r,
           })
    }

    /// Call everytime you got data from transmission
    pub fn input(&mut self, data: &[u8]) -> io::Result<()> {
        self.io.input(data)?;
        if self.io.can_read() {
            self.readiness.set_readiness(Ready::readable())?;
        }
        Ok(())
    }

    /// Close it
    pub fn shutdown(&mut self) -> io::Result<()> {
        self.io.shutdown()
    }
}

impl Evented for ServerKcpIo {
    fn register(&self, poll: &mio::Poll, token: Token, interest: Ready, opts: PollOpt) -> io::Result<()> {
        self.reg.register(poll, token, interest, opts)
    }

    fn reregister(&self, poll: &mio::Poll, token: Token, interest: Ready, opts: PollOpt) -> io::Result<()> {
        self.reg.reregister(poll, token, interest, opts)
    }

    fn deregister(&self, poll: &mio::Poll) -> io::Result<()> {
        <Registration as Evented>::deregister(&self.reg, poll)
    }
}

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

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

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

// impl KcpIo {
//     pub fn new(shared_kcp: SharedKcp,
//                addr: SocketAddr,
//                handle: &Handle,
//                owner: Option<KcpSessionUpdater>,
//                expire_dur: Duration)
//                -> io::Result<KcpIo> {
//         let (registration, readiness) = Registration::new2();
//         let timer = Timeout::new_at(Instant::now(), handle)?;

//         let elapsed = Rc::new(RefCell::new(Instant::now()));
//         let close_flag = Rc::new(RefCell::new(false));
//         let session = SharedKcpSession::new(shared_kcp.clone(),
//                                             timer,
//                                             elapsed.clone(),
//                                             readiness.clone(),
//                                             addr,
//                                             owner,
//                                             close_flag.clone(),
//                                             expire_dur)?;
//         handle.spawn(session.for_each(|_| Ok(())).map_err(|err| {
//                                                               error!("Failed to update KCP session: err: {:?}", err);
//                                                           }));

//         Ok(KcpIo {
//                kcp: shared_kcp,
//                registration: registration,
//                readiness: readiness,
//                last_update: elapsed,
//                close_flag: close_flag,
//                read_buf: vec![0u8; 65535],
//                read_pos: 0,
//                read_cap: 0,
//            })
//     }

//     pub fn input_buf(&mut self, buf: &[u8]) -> io::Result<()> {
//         {
//             let mut last_update = self.last_update.borrow_mut();
//             *last_update = Instant::now();
//             let mut kcp = self.kcp.borrow_mut();
//             kcp.input(buf)?;
//             trace!("[INPUT] KcpIo.input size={}", buf.len());
//         }
//         self.set_readable()
//     }

//     fn set_readable(&mut self) -> io::Result<()> {
//         self.readiness.set_readiness(Ready::readable())
//     }

//     pub fn can_read(&self) -> io::Result<bool> {
//         let kcp = self.kcp.borrow();
//         kcp.peeksize().map(|n| n != 0)
//     }
// }

// impl Evented for KcpIo {
//     fn register(&self, poll: &mio::Poll, token: Token, interest: Ready, opts: PollOpt) -> io::Result<()> {
//         self.registration.register(poll, token, interest, opts)
//     }

//     fn reregister(&self, poll: &mio::Poll, token: Token, interest: Ready, opts: PollOpt) -> io::Result<()> {
//         self.registration.reregister(poll, token, interest, opts)
//     }

//     fn deregister(&self, poll: &mio::Poll) -> io::Result<()> {
//         <Registration as Evented>::deregister(&self.registration, poll)
//     }
// }
<|endoftext|>


<commit_before>///! Basic CSS block layout.

use style::{StyledNode, Inline, Block, DisplayNone};
use css::{Keyword, Length, Px};
use std::default::Default;
use std::iter::AdditiveIterator; // for `sum`

// CSS box model. All sizes are in px.

#[deriving(Default, Show)]
pub struct Dimensions {
    // Position of the content area relative to the document origin:
    pub x: f32,
    pub y: f32,

    // Content area size:
    pub width: f32,
    pub height: f32,

    // Surrounding edges:
    pub padding: EdgeSizes,
    pub border: EdgeSizes,
    pub margin: EdgeSizes,
}

/// A node in the layout tree.
pub struct LayoutBox<'a> {
    pub box_type: BoxType<'a>,
    pub dimensions: Dimensions,
    pub children: Vec<LayoutBox<'a>>,
}

pub enum BoxType<'a> {
    BlockNode(&'a StyledNode<'a>),
    InlineNode(&'a StyledNode<'a>),
    InlineContainer,
}

impl<'a> LayoutBox<'a> {
    fn new(box_type: BoxType) -> LayoutBox {
        LayoutBox {
            box_type: box_type,
            dimensions: Default::default(),
            children: Vec::new(),
        }
    }

    fn get_style_node(&self) -> &'a StyledNode<'a> {
        match self.box_type {
            BlockNode(node) => node,
            InlineNode(node) => node,
            InlineContainer => fail!("Inline container has no style node")
        }
    }
}

#[deriving(Default, Show)]
struct EdgeSizes { left: f32, right: f32, top: f32, bottom: f32 }

/// Transform a style tree into a layout tree.
pub fn layout_tree<'a>(node: &'a StyledNode<'a>, containing_block: Dimensions) -> LayoutBox<'a> {
    let mut root_box = build_layout_tree(node);
    root_box.layout(containing_block);
    return root_box;
}

/// Build the tree of LayoutBoxes, but don't perform any layout calculations yet.
fn build_layout_tree<'a>(style_node: &'a StyledNode<'a>) -> LayoutBox<'a> {
    // Create the root box.
    let mut root = LayoutBox::new(match style_node.display() {
        Block => BlockNode(style_node),
        Inline => InlineNode(style_node),
        DisplayNone => fail!("Root node has display: none.")
    });

    // Create the descendant boxes.
    for child in style_node.children.iter() {
        match child.display() {
            Block => root.children.push(build_layout_tree(child)),
            Inline => root.push_inline(build_layout_tree(child)),
            DisplayNone => {} // Don't lay out nodes with `display: none;`
        }
    }
    return root;
}

impl<'a> LayoutBox<'a> {
    /// Lay out a box and its descendants.
    fn layout(&mut self, containing_block: Dimensions) {
        match self.box_type {
            BlockNode(_) => self.layout_block(containing_block),
            InlineNode(_) => {} // TODO
            InlineContainer => {} // TODO
        }
    }

    /// Lay out a block-level element and its descendants.
    fn layout_block(&mut self, containing_block: Dimensions) {
        // Child width can depend on parent width, so we need to calculate this node's width before
        // laying out its children.
        self.calculate_block_width(containing_block);

        // Recursively lay out the children of this node within its content area.
        let content_height = self.layout_block_content(containing_block);

        // Parent height can depend on child height, so `calculate_height` must be called after the
        // content layout is finished.
        self.calculate_block_height(content_height);
    }

    /// Calculate the width of a block-level non-replaced element in normal flow.
    ///
    /// http://www.w3.org/TR/CSS2/visudet.html#blockwidth
    fn calculate_block_width(&mut self, containing_block: Dimensions) {
        let style = self.get_style_node();

        // `width` has initial value `auto`.
        let auto = Keyword("auto".to_string());
        let mut width = style.value("width").unwrap_or(auto.clone());

        // margin, border, and padding have initial value 0.
        let zero = Length(0.0, Px);

        let mut margin_left = style.lookup("margin-left", "margin", &zero);
        let mut margin_right = style.lookup("margin-right", "margin", &zero);

        let border_left = style.lookup("border-left-width", "border-width", &zero);
        let border_right = style.lookup("border-right-width", "border-width", &zero);

        let padding_left = style.lookup("padding-left", "padding", &zero);
        let padding_right = style.lookup("padding-right", "padding", &zero);

        let total = [&margin_left, &margin_right, &border_left, &border_right,
                     &padding_left, &padding_right, &width].iter().map(|v| v.to_px()).sum();

        // If width is not auto and the total is wider than the container, treat auto margins as 0.
        if width != auto && total > containing_block.width {
            if margin_left == auto {
                margin_left = Length(0.0, Px);
            }
            if margin_right == auto {
                margin_right = Length(0.0, Px);
            }
        }

        // Adjust used values so that the above sum equals `containing_block.width`.
        // Each arm of the `match` should increase the total width by exactly `underflow`,
        // and afterward all values should be absolute lengths in px.
        let underflow = containing_block.width - total;
        match (width == auto, margin_left == auto, margin_right == auto) {
            // If the values are overconstrained, calculate margin_right.
            (false, false, false) => {
                margin_right = Length(margin_right.to_px() + underflow, Px);
            }
            // If exactly one value is auto, its used value follows from the equality.
            (false, false, true) => {
                margin_right = Length(underflow, Px);
            }
            (false, true, false) => {
                margin_left = Length(underflow, Px);
            }
            // If width is set to auto, any other auto values become 0.
            (true, _, _) => {
                if margin_left == auto {
                    margin_left = Length(0.0, Px);
                }
                if margin_right == auto {
                    margin_right = Length(0.0, Px);
                }
                width = Length(underflow, Px);
            }
            (false, true, true) => {
                // If margin-left and margin-right are both auto, their used values are equal.
                margin_left = Length(underflow / 2.0, Px);
                margin_right = Length(underflow / 2.0, Px);
            }
        }

        let d = &mut self.dimensions;
        d.width = width.to_px();

        d.padding.left = padding_left.to_px();
        d.padding.right = padding_right.to_px();

        d.border.left = border_left.to_px();
        d.border.right = border_right.to_px();

        d.margin.left = margin_left.to_px();
        d.margin.right = margin_right.to_px();

        d.x = containing_block.x + d.margin.left + d.border.left + d.padding.left;
    }

    /// Lay out the node's children within its content area and return the content height.
    ///
    /// http://www.w3.org/TR/CSS2/visudet.html#normal-block
    fn layout_block_content(&mut self, containing_block: Dimensions) -> f32 {
        // First we need to find the position of the content area...
        let style = self.get_style_node();
        let d = &mut self.dimensions;

        // margin, border, and padding have initial value 0.
        let zero = Length(0.0, Px);

        // If margin-top or margin-bottom is `auto`, the used value is zero.
        d.margin.top = style.lookup("margin-top", "margin", &zero).to_px();
        d.margin.bottom = style.lookup("margin-bottom", "margin", &zero).to_px();

        d.border.top = style.lookup("border-top-width", "border-width", &zero).to_px();
        d.border.bottom = style.lookup("border-bottom-width", "border-width", &zero).to_px();

        d.padding.top = style.lookup("padding-top", "padding", &zero).to_px();
        d.padding.bottom = style.lookup("padding-bottom", "padding", &zero).to_px();

        d.y = containing_block.y + d.margin.top + d.border.top + d.padding.top;

        // Now we can lay out the children within the content area.
        let mut content_height = 0.0;
        for child in self.children.mut_iter() {
            child.layout(*d);

            // Position each child below the previous one. TODO: margin collapsing
            child.dimensions.y = d.y + content_height;
            content_height = content_height + child.dimensions.margin_box_height();
        }
        return content_height;
    }

    /// Height of a block-level non-replaced element in normal flow with overflow visible.
    fn calculate_block_height(&mut self, content_height: f32) {
        let height = self.get_style_node().value("height");
        self.dimensions.height = match height {
            Some(Length(h, Px)) => h,
            _ => content_height // In the default (`auto`) case, use the content height.
        };
    }

    /// Add an inline-level child.
    fn push_inline(&mut self, child: LayoutBox<'a>) {
        self.container_for_inline().children.push(child);
    }

    /// Where a new inline child should go.
    fn container_for_inline(&mut self) -> &mut LayoutBox<'a> {
        match self.box_type {
            InlineNode(_) | InlineContainer => self,
            BlockNode(_) => {
                // If we're in the middle of a series of inline nodes, keep using the existing
                // container.  Otherwise, create a new inline container.
                match self.children.last() {
                    Some(&LayoutBox { box_type: InlineContainer,..}) => {}
                    _ => self.children.push(LayoutBox::new(InlineContainer))
                }
                self.children.mut_last().unwrap()
            }
        }
    }
}

impl Dimensions {
    /// Total height of a box including its margins, border, and padding.
    fn margin_box_height(&self) -> f32 {
        self.height + self.padding.top + self.padding.bottom
                    + self.border.top + self.border.bottom
                    + self.margin.top + self.margin.bottom
    }
}
<commit_msg>Minor refactoring in build_layout_tree<commit_after>///! Basic CSS block layout.

use style::{StyledNode, Inline, Block, DisplayNone};
use css::{Keyword, Length, Px};
use std::default::Default;
use std::iter::AdditiveIterator; // for `sum`

// CSS box model. All sizes are in px.

#[deriving(Default, Show)]
pub struct Dimensions {
    // Position of the content area relative to the document origin:
    pub x: f32,
    pub y: f32,

    // Content area size:
    pub width: f32,
    pub height: f32,

    // Surrounding edges:
    pub padding: EdgeSizes,
    pub border: EdgeSizes,
    pub margin: EdgeSizes,
}

/// A node in the layout tree.
pub struct LayoutBox<'a> {
    pub box_type: BoxType<'a>,
    pub dimensions: Dimensions,
    pub children: Vec<LayoutBox<'a>>,
}

pub enum BoxType<'a> {
    BlockNode(&'a StyledNode<'a>),
    InlineNode(&'a StyledNode<'a>),
    InlineContainer,
}

impl<'a> LayoutBox<'a> {
    fn new(box_type: BoxType) -> LayoutBox {
        LayoutBox {
            box_type: box_type,
            dimensions: Default::default(),
            children: Vec::new(),
        }
    }

    fn get_style_node(&self) -> &'a StyledNode<'a> {
        match self.box_type {
            BlockNode(node) => node,
            InlineNode(node) => node,
            InlineContainer => fail!("Inline container has no style node")
        }
    }
}

#[deriving(Default, Show)]
struct EdgeSizes { left: f32, right: f32, top: f32, bottom: f32 }

/// Transform a style tree into a layout tree.
pub fn layout_tree<'a>(node: &'a StyledNode<'a>, containing_block: Dimensions) -> LayoutBox<'a> {
    let mut root_box = build_layout_tree(node);
    root_box.layout(containing_block);
    return root_box;
}

/// Build the tree of LayoutBoxes, but don't perform any layout calculations yet.
fn build_layout_tree<'a>(style_node: &'a StyledNode<'a>) -> LayoutBox<'a> {
    // Create the root box.
    let mut root = LayoutBox::new(match style_node.display() {
        Block => BlockNode(style_node),
        Inline => InlineNode(style_node),
        DisplayNone => fail!("Root node has display: none.")
    });

    // Create the descendant boxes.
    for child in style_node.children.iter() {
        match child.display() {
            Block => root.children.push(build_layout_tree(child)),
            Inline => root.get_inline_container().children.push(build_layout_tree(child)),
            DisplayNone => {} // Don't lay out nodes with `display: none;`
        }
    }
    return root;
}

impl<'a> LayoutBox<'a> {
    /// Lay out a box and its descendants.
    fn layout(&mut self, containing_block: Dimensions) {
        match self.box_type {
            BlockNode(_) => self.layout_block(containing_block),
            InlineNode(_) => {} // TODO
            InlineContainer => {} // TODO
        }
    }

    /// Lay out a block-level element and its descendants.
    fn layout_block(&mut self, containing_block: Dimensions) {
        // Child width can depend on parent width, so we need to calculate this node's width before
        // laying out its children.
        self.calculate_block_width(containing_block);

        // Recursively lay out the children of this node within its content area.
        let content_height = self.layout_block_content(containing_block);

        // Parent height can depend on child height, so `calculate_height` must be called after the
        // content layout is finished.
        self.calculate_block_height(content_height);
    }

    /// Calculate the width of a block-level non-replaced element in normal flow.
    ///
    /// http://www.w3.org/TR/CSS2/visudet.html#blockwidth
    fn calculate_block_width(&mut self, containing_block: Dimensions) {
        let style = self.get_style_node();

        // `width` has initial value `auto`.
        let auto = Keyword("auto".to_string());
        let mut width = style.value("width").unwrap_or(auto.clone());

        // margin, border, and padding have initial value 0.
        let zero = Length(0.0, Px);

        let mut margin_left = style.lookup("margin-left", "margin", &zero);
        let mut margin_right = style.lookup("margin-right", "margin", &zero);

        let border_left = style.lookup("border-left-width", "border-width", &zero);
        let border_right = style.lookup("border-right-width", "border-width", &zero);

        let padding_left = style.lookup("padding-left", "padding", &zero);
        let padding_right = style.lookup("padding-right", "padding", &zero);

        let total = [&margin_left, &margin_right, &border_left, &border_right,
                     &padding_left, &padding_right, &width].iter().map(|v| v.to_px()).sum();

        // If width is not auto and the total is wider than the container, treat auto margins as 0.
        if width != auto && total > containing_block.width {
            if margin_left == auto {
                margin_left = Length(0.0, Px);
            }
            if margin_right == auto {
                margin_right = Length(0.0, Px);
            }
        }

        // Adjust used values so that the above sum equals `containing_block.width`.
        // Each arm of the `match` should increase the total width by exactly `underflow`,
        // and afterward all values should be absolute lengths in px.
        let underflow = containing_block.width - total;
        match (width == auto, margin_left == auto, margin_right == auto) {
            // If the values are overconstrained, calculate margin_right.
            (false, false, false) => {
                margin_right = Length(margin_right.to_px() + underflow, Px);
            }
            // If exactly one value is auto, its used value follows from the equality.
            (false, false, true) => {
                margin_right = Length(underflow, Px);
            }
            (false, true, false) => {
                margin_left = Length(underflow, Px);
            }
            // If width is set to auto, any other auto values become 0.
            (true, _, _) => {
                if margin_left == auto {
                    margin_left = Length(0.0, Px);
                }
                if margin_right == auto {
                    margin_right = Length(0.0, Px);
                }
                width = Length(underflow, Px);
            }
            (false, true, true) => {
                // If margin-left and margin-right are both auto, their used values are equal.
                margin_left = Length(underflow / 2.0, Px);
                margin_right = Length(underflow / 2.0, Px);
            }
        }

        let d = &mut self.dimensions;
        d.width = width.to_px();

        d.padding.left = padding_left.to_px();
        d.padding.right = padding_right.to_px();

        d.border.left = border_left.to_px();
        d.border.right = border_right.to_px();

        d.margin.left = margin_left.to_px();
        d.margin.right = margin_right.to_px();

        d.x = containing_block.x + d.margin.left + d.border.left + d.padding.left;
    }

    /// Lay out the node's children within its content area and return the content height.
    ///
    /// http://www.w3.org/TR/CSS2/visudet.html#normal-block
    fn layout_block_content(&mut self, containing_block: Dimensions) -> f32 {
        // First we need to find the position of the content area...
        let style = self.get_style_node();
        let d = &mut self.dimensions;

        // margin, border, and padding have initial value 0.
        let zero = Length(0.0, Px);

        // If margin-top or margin-bottom is `auto`, the used value is zero.
        d.margin.top = style.lookup("margin-top", "margin", &zero).to_px();
        d.margin.bottom = style.lookup("margin-bottom", "margin", &zero).to_px();

        d.border.top = style.lookup("border-top-width", "border-width", &zero).to_px();
        d.border.bottom = style.lookup("border-bottom-width", "border-width", &zero).to_px();

        d.padding.top = style.lookup("padding-top", "padding", &zero).to_px();
        d.padding.bottom = style.lookup("padding-bottom", "padding", &zero).to_px();

        d.y = containing_block.y + d.margin.top + d.border.top + d.padding.top;

        // Now we can lay out the children within the content area.
        let mut content_height = 0.0;
        for child in self.children.mut_iter() {
            child.layout(*d);

            // Position each child below the previous one. TODO: margin collapsing
            child.dimensions.y = d.y + content_height;
            content_height = content_height + child.dimensions.margin_box_height();
        }
        return content_height;
    }

    /// Height of a block-level non-replaced element in normal flow with overflow visible.
    fn calculate_block_height(&mut self, content_height: f32) {
        let height = self.get_style_node().value("height");
        self.dimensions.height = match height {
            Some(Length(h, Px)) => h,
            _ => content_height // In the default (`auto`) case, use the content height.
        };
    }

    /// Where a new inline child should go.
    fn get_inline_container(&mut self) -> &mut LayoutBox<'a> {
        match self.box_type {
            InlineNode(_) | InlineContainer => self,
            BlockNode(_) => {
                // If we're in the middle of a series of inline nodes, keep using the existing
                // container.  Otherwise, create a new inline container.
                match self.children.last() {
                    Some(&LayoutBox { box_type: InlineContainer,..}) => {}
                    _ => self.children.push(LayoutBox::new(InlineContainer))
                }
                self.children.mut_last().unwrap()
            }
        }
    }
}

impl Dimensions {
    /// Total height of a box including its margins, border, and padding.
    fn margin_box_height(&self) -> f32 {
        self.height + self.padding.top + self.padding.bottom
                    + self.border.top + self.border.bottom
                    + self.margin.top + self.margin.bottom
    }
}
<|endoftext|>