<commit_before>extern crate reqwest;
extern crate serde_json;

use self::reqwest::header::LINK;
use self::reqwest::{Error, Response, Url};
use reqwest::hyper_011::{header::Link, header::RelationType, Headers};

use std::collections::HashMap;
use std::io::Read;
use std::{thread, time};

#[derive(Deserialize, Debug)]
pub struct GithubRepo {
    id: i32,
    pub name: String,
    pub url: String,
}

#[derive(Deserialize, Debug)]
pub struct CompareCommitsResponse {
    pub status: String,
    pub behind_by: i32,
}

#[derive(Deserialize, Debug)]
pub struct GithubPullRequest {
    id: i32,
    pub url: String,
    pub html_url: String,
    pub head: Commit,
    pub base: Commit,
}

#[derive(Deserialize, Debug)]
pub struct Commit {
    pub sha: String,
}

#[derive(Deserialize, Debug)]
pub struct ActualCommitInPR {
    pub message: String,
}

#[derive(Deserialize, Debug)]
pub struct CommitInPR {
    pub sha: String,
    #[serde(rename = "commit")]
    pub actual_commit: ActualCommitInPR,
}

pub fn is_release_up_to_date_with_master(repo_url: &str, client: &reqwest::Client) -> bool {
    let repo_pr_url = format!("{}/{}/{}...{}", repo_url, "compare", "master", "release");
    let url = match Url::parse(&repo_pr_url) {
        Ok(new_url) => new_url,
        Err(e) => {
            println!("Couldn't create url for compare page: {}", e);
            return true;
        }
    };
    let mut res = match client.get(url.clone()).send() {
        Ok(response) => response,
        Err(e) => {
            println!("Error in request to github for compare page: {}", e);
            return true;
        }
    };
    delay_if_running_out_of_requests(res.headers());

    let mut buffer = String::new();
    match res.read_to_string(&mut buffer) {
        Ok(_) => (),
        Err(e) => println!("error checking commit diff for {}: {}", repo_url, e),
    }

    let commits_diff: CompareCommitsResponse = match serde_json::from_str(&buffer) {
        Ok(compare_response) => compare_response,
        Err(_) => return true,
    };

    if commits_diff.behind_by > 0 {
        return false;
    }

    true
}

fn delay_if_running_out_of_requests(response_headers: &reqwest::header::HeaderMap) {
    if close_to_running_out_of_requests(response_headers) {
        println!("Running low on requests, throttling back...");
        thread::sleep(time::Duration::from_millis(2000));
    }
}

// This is a bit simplistic at the moment by trying to prevent bottoming out completely.
// An improvement could be to use the fraction of requests left over the overall limit.
// EG: 55 requests left out of a limit of 60 is fine.
// 10 requests left out of 60 is time to throttle back.
// TODO: dump the `expects` and handle things more gracefully.
fn close_to_running_out_of_requests(response_headers: &reqwest::header::HeaderMap) -> bool {
    let requests_to_treat_as_running_out = 10;
    let remaining_requests = match response_headers.get("X-RateLimit-Remaining") {
        Some(remaining_req_from_github) => {
            let req_left = remaining_req_from_github
                .to_str()
                .expect("Rate limit should be able to be a string")
                .replace('"', ""); // the formatter puts quotes around the number.  EG: "55"
            let int_req_left = req_left
                .parse::<i32>()
                .expect("Expected number in X-RateLimit-Remaining field");
            if int_req_left == 0 {
                panic!("We're out of requests, sad! Exiting poorly.");
            }
            int_req_left
        }
        // If it's not specified, we'll say we have enough to keep going:
        None => requests_to_treat_as_running_out + 1,
    };
    remaining_requests < requests_to_treat_as_running_out
}

fn response_has_a_next_link(response_headers: &reqwest::header::HeaderMap) -> bool {
    if response_headers.get(LINK).is_none() {
        return false;
    }

    let headers = Headers::from(response_headers.clone());
    if let Some(link) = headers.get::<Link>() {
        return link.values().iter().any(|ref l| {
            if let Some(r) = l.rel() {
                if r.iter().any(|foo| foo == &RelationType::Next) {
                    return true;
                }
            }
            false
        });
    }
    false
}

// Expects caller to check to ensure the `next` link is present
fn response_next_link(response_headers: &reqwest::header::HeaderMap) -> Result<Url, String> {
    let headers = Headers::from(response_headers.clone());
    if let Some(link) = headers.get::<Link>() {
        for l in link.values() {
            if let Some(r) = l.rel() {
                // r will be a collection of relations
                for rel in r {
                    if rel == &RelationType::Next {
                        let uri = Url::parse(l.link()).expect("Should have a nextlink");
                        return Ok(uri);
                    }
                }
            }
        }
    }
    Err("Couldn't find a next link: does it exist?".to_string())
}

pub fn get_repos_at(repos_url: &str, client: &reqwest::Client) -> Result<Vec<GithubRepo>, String> {
    // We need to pass in the URL from the link headers to github API docs.
    // We'll construct it this first time.
    let url = match Url::parse_with_params(repos_url, &[("per_page", "50")]) {
        Ok(new_url) => new_url,
        Err(e) => return Err(format!("Couldn't parse uri {:?} : {:?}", repos_url, e)),
    };
    let mut response = get_repos_at_url(url, client).expect("request failed");

    if !response.status().is_success() {
        let mut buffer = String::new();
        match response.read_to_string(&mut buffer) {
            Ok(_) => (),
            Err(e) => println!(
                "error reading response from github when getting repo list: {}",
                e
            ),
        }
        panic!("Error: Github responded with {:?}", buffer);
    }
    delay_if_running_out_of_requests(response.headers());
    let mut buffer = String::new();
    match response.read_to_string(&mut buffer) {
        Ok(_) => (),
        Err(e) => println!(
            "error reading response from github when getting repo list: {}",
            e
        ),
    }
    let mut repos = repo_list_from_string(&buffer).expect("expected repos");

    if response_has_a_next_link(response.headers()) {
        loop {
            let paging_url = response_next_link(response.headers()).expect("a thing");
            response = get_repos_at_url(paging_url, client).expect("request failed");
            delay_if_running_out_of_requests(response.headers());
            buffer = String::new();
            match response.read_to_string(&mut buffer) {
                Ok(_) => (),
                Err(e) => println!(
                    "error reading response from github when getting repo list: {}",
                    e
                ),
            }
            repos.append(&mut repo_list_from_string(&buffer).expect("expected repos"));
            if !response_has_a_next_link(response.headers()) {
                break;
            }
        }
    }
    println!("Number of repos to check: {:?}", repos.len());
    Ok(repos)
}

fn get_repos_at_url(url: reqwest::Url, client: &reqwest::Client) -> Result<Response, Error> {
    client.get(url).send()
}

fn repo_list_from_string(json_str: &str) -> Result<Vec<GithubRepo>, String> {
    // This looks a bit weird due to supplying type hints to deserialize:
    let _: Vec<GithubRepo> = match serde_json::from_str(json_str) {
        Ok(v) => return Ok(v),
        Err(e) => return Err(format!("Couldn't deserialize repos from github: {}", e)),
    };
}

pub fn existing_release_pr_location(repo: &GithubRepo, client: &reqwest::Client) -> Option<String> {
    let repo_pr_url = format!("{}/{}", repo.url, "pulls");
    let url = match Url::parse_with_params(&repo_pr_url, &[("head", "master"), ("base", "release")])
    {
        Ok(new_url) => new_url,
        Err(e) => {
            println!("Couldn't create url for existing pr location: {}", e);
            return None;
        }
    };
    let mut res = match client.get(url).send() {
        Ok(response) => response,
        Err(e) => {
            println!("Error in request to github for existing PR location: {}", e);
            return None;
        }
    };
    delay_if_running_out_of_requests(res.headers());
    let mut buffer = String::new();
    match res.read_to_string(&mut buffer) {
        Ok(_) => (),
        Err(e) => println!("error finding existing pr for {}: {}", repo.name, e),
    }

    let pull_reqs: Vec<GithubPullRequest> = match serde_json::from_str(&buffer) {
        Ok(v) => v,
        Err(_) => Vec::new(),
    };

    if !pull_reqs.is_empty() {
        return Some(pull_reqs[0].html_url.clone());
    }

    None
}

pub fn get_commits_from_pr(
    repo: &GithubRepo,
    pr_number: &str,
    client: &reqwest::Client,
    rp_version: &str,
) -> String {
    let pr_commits_url = format!("{}/pulls/{}/commits", repo.url, pr_number);

    let mut res = match client.get(&pr_commits_url).send() {
        Ok(response) => response,
        Err(e) => {
            panic!("Error in request to github for compare page: {}", e);
        }
    };
    delay_if_running_out_of_requests(res.headers());

    let mut buffer = String::new();
    match res.read_to_string(&mut buffer) {
        Ok(_) => (),
        Err(e) => println!("error checking commit diff for {}: {}", pr_commits_url, e),
    }

    let prs: Vec<CommitInPR> = match serde_json::from_str(&buffer) {
        Ok(v) => v,
        Err(e) => panic!(format!("Couldn't deserialize repos from github: {}", e)),
    };

    let mut new_body = "automated release partay!\n\nPRs in this release:".to_string();

    for c in prs.into_iter() {
        if c.actual_commit.message.contains("Merge pull request #") {
            // remove the bits we don't need: go from "Merge pull request #1890 from..." to "#1890"
            let pr_number = c.actual_commit.message.split(' ').collect::<Vec<&str>>()[3];
            new_body.push_str(&format!("\n* {}", pr_number));
        }
    }

    new_body.push_str(&format!("\n\n---\nMade by `{}`.", rp_version));

    new_body
}

pub fn set_pr_body(repo: &GithubRepo, pr_number: &str, body: &str, client: &reqwest::Client) {
    let mut pr_body = HashMap::new();
    pr_body.insert("body", body);

    let repo_pr_url = format!("{}/pulls/{}", repo.url, pr_number);
    let res = match client.patch(&repo_pr_url).json(&pr_body).send() {
        Ok(response) => response,
        Err(e) => panic!(format!("Error in request to github creating new PR: {}", e)),
    };
    delay_if_running_out_of_requests(res.headers());
}

pub fn update_pr_body(
    repo: &GithubRepo,
    pr_number: &str,
    client: &reqwest::Client,
    rp_version: &str,
) {
    let new_body = get_commits_from_pr(repo, pr_number, client, rp_version);
    set_pr_body(repo, pr_number, &new_body, client);
}

// Try to create the release PR and return the URL of it:
pub fn create_release_pull_request(
    repo: &GithubRepo,
    client: &reqwest::Client,
) -> Result<String, String> {
    let mut pr_body = HashMap::new();
    pr_body.insert("title", "automated release partay");
    pr_body.insert("head", "master");
    pr_body.insert("base", "release");

    let repo_pr_url = format!("{}/{}", repo.url, "pulls");
    let mut res = match client.post(&repo_pr_url).json(&pr_body).send() {
        Ok(response) => response,
        Err(e) => return Err(format!("Error in request to github creating new PR: {}", e)),
    };

    delay_if_running_out_of_requests(res.headers());
    if res.status().is_success() {
        let mut buffer = String::new();
        match res.read_to_string(&mut buffer) {
            Ok(_) => (),
            Err(e) => println!(
                "error reading response after creating new release PR for {}: {}",
                repo.name, e
            ),
        }
        let pull_req: GithubPullRequest = match serde_json::from_str(&buffer) {
            Ok(v) => v,
            Err(e) => {
                return Err(format!(
                    "Couldn't deserialize create pull req response for {}: {}",
                    repo.name, e
                ));
            }
        };
        return Ok(pull_req.html_url);
    }
    // 422 unprocessable means it's there already
    // 422 unprocessable also means the branch is up to date

    Err("Release branch already up to date?".to_string())
}

#[cfg(test)]
mod tests {
    use super::*;
    use reqwest::header::HeaderMap;

    #[test]
    fn plenty_of_requests_left() {
        let mut plenty_left_headers = HeaderMap::new();
        plenty_left_headers.insert("X-RateLimit-Remaining", "10000".parse().unwrap());

        assert_eq!(
            false,
            close_to_running_out_of_requests(&plenty_left_headers)
        );
    }

    #[test]
    fn almost_no_requests_left() {
        let mut nothing_left_headers = HeaderMap::new();
        nothing_left_headers.insert("X-RateLimit-Remaining", "1".parse().unwrap());
        assert_eq!(
            true,
            close_to_running_out_of_requests(&nothing_left_headers)
        );
    }

    #[test]
    fn no_next_link() {
        assert_eq!(false, response_has_a_next_link(&HeaderMap::new()));
    }

    #[test]
    fn has_next_link() {
        let mut headers = HeaderMap::new();
        headers.insert(
            LINK,
            "<http://example.com/TheBook/chapter2>; rel=\"next\"; title=\"next page\""
                .parse()
                .unwrap(),
        );

        assert_eq!(true, response_has_a_next_link(&headers));
    }

    #[test]
    fn finds_next_link() {
        let mut headers = HeaderMap::new();
        headers.insert(
            LINK,
            "<http://example.com/TheBook/chapter3>; rel=\"next\"; title=\"next page\""
                .parse()
                .unwrap(),
        );

        let expected_uri = Url::parse("http://example.com/TheBook/chapter3").unwrap();
        assert_eq!(expected_uri, response_next_link(&headers).unwrap());
    }
}
<commit_msg>Add more details from github when something goes wrong.<commit_after>extern crate reqwest;
extern crate serde_json;

use self::reqwest::header::LINK;
use self::reqwest::{Error, Response, Url};
use reqwest::hyper_011::{header::Link, header::RelationType, Headers};

use std::collections::HashMap;
use std::io::Read;
use std::{thread, time};

#[derive(Deserialize, Debug)]
pub struct GithubRepo {
    id: i32,
    pub name: String,
    pub url: String,
}

#[derive(Deserialize, Debug)]
pub struct CompareCommitsResponse {
    pub status: String,
    pub behind_by: i32,
}

#[derive(Deserialize, Debug)]
pub struct GithubPullRequest {
    id: i32,
    pub url: String,
    pub html_url: String,
    pub head: Commit,
    pub base: Commit,
}

#[derive(Deserialize, Debug)]
pub struct Commit {
    pub sha: String,
}

#[derive(Deserialize, Debug)]
pub struct ActualCommitInPR {
    pub message: String,
}

#[derive(Deserialize, Debug)]
pub struct CommitInPR {
    pub sha: String,
    #[serde(rename = "commit")]
    pub actual_commit: ActualCommitInPR,
}

pub fn is_release_up_to_date_with_master(repo_url: &str, client: &reqwest::Client) -> bool {
    let repo_pr_url = format!("{}/{}/{}...{}", repo_url, "compare", "master", "release");
    let url = match Url::parse(&repo_pr_url) {
        Ok(new_url) => new_url,
        Err(e) => {
            println!("Couldn't create url for compare page: {}", e);
            return true;
        }
    };
    let mut res = match client.get(url.clone()).send() {
        Ok(response) => response,
        Err(e) => {
            println!("Error in request to github for compare page: {}", e);
            return true;
        }
    };
    delay_if_running_out_of_requests(res.headers());

    let mut buffer = String::new();
    match res.read_to_string(&mut buffer) {
        Ok(_) => (),
        Err(e) => println!("error checking commit diff for {}: {}", repo_url, e),
    }

    let commits_diff: CompareCommitsResponse = match serde_json::from_str(&buffer) {
        Ok(compare_response) => compare_response,
        Err(_) => return true,
    };

    if commits_diff.behind_by > 0 {
        return false;
    }

    true
}

fn delay_if_running_out_of_requests(response_headers: &reqwest::header::HeaderMap) {
    if close_to_running_out_of_requests(response_headers) {
        println!("Running low on requests, throttling back...");
        thread::sleep(time::Duration::from_millis(2000));
    }
}

// This is a bit simplistic at the moment by trying to prevent bottoming out completely.
// An improvement could be to use the fraction of requests left over the overall limit.
// EG: 55 requests left out of a limit of 60 is fine.
// 10 requests left out of 60 is time to throttle back.
// TODO: dump the `expects` and handle things more gracefully.
fn close_to_running_out_of_requests(response_headers: &reqwest::header::HeaderMap) -> bool {
    let requests_to_treat_as_running_out = 10;
    let remaining_requests = match response_headers.get("X-RateLimit-Remaining") {
        Some(remaining_req_from_github) => {
            let req_left = remaining_req_from_github
                .to_str()
                .expect("Rate limit should be able to be a string")
                .replace('"', ""); // the formatter puts quotes around the number.  EG: "55"
            let int_req_left = req_left
                .parse::<i32>()
                .expect("Expected number in X-RateLimit-Remaining field");
            if int_req_left == 0 {
                panic!("We're out of requests, sad! Exiting poorly.");
            }
            int_req_left
        }
        // If it's not specified, we'll say we have enough to keep going:
        None => requests_to_treat_as_running_out + 1,
    };
    remaining_requests < requests_to_treat_as_running_out
}

fn response_has_a_next_link(response_headers: &reqwest::header::HeaderMap) -> bool {
    if response_headers.get(LINK).is_none() {
        return false;
    }

    let headers = Headers::from(response_headers.clone());
    if let Some(link) = headers.get::<Link>() {
        return link.values().iter().any(|ref l| {
            if let Some(r) = l.rel() {
                if r.iter().any(|foo| foo == &RelationType::Next) {
                    return true;
                }
            }
            false
        });
    }
    false
}

// Expects caller to check to ensure the `next` link is present
fn response_next_link(response_headers: &reqwest::header::HeaderMap) -> Result<Url, String> {
    let headers = Headers::from(response_headers.clone());
    if let Some(link) = headers.get::<Link>() {
        for l in link.values() {
            if let Some(r) = l.rel() {
                // r will be a collection of relations
                for rel in r {
                    if rel == &RelationType::Next {
                        let uri = Url::parse(l.link()).expect("Should have a nextlink");
                        return Ok(uri);
                    }
                }
            }
        }
    }
    Err("Couldn't find a next link: does it exist?".to_string())
}

pub fn get_repos_at(repos_url: &str, client: &reqwest::Client) -> Result<Vec<GithubRepo>, String> {
    // We need to pass in the URL from the link headers to github API docs.
    // We'll construct it this first time.
    let url = match Url::parse_with_params(repos_url, &[("per_page", "50")]) {
        Ok(new_url) => new_url,
        Err(e) => return Err(format!("Couldn't parse uri {:?} : {:?}", repos_url, e)),
    };
    let mut response = get_repos_at_url(url, client).expect("request failed");

    if !response.status().is_success() {
        let mut buffer = String::new();
        match response.read_to_string(&mut buffer) {
            Ok(_) => (),
            Err(e) => println!(
                "error reading response from github when getting repo list: {}",
                e
            ),
        }
        panic!("Error: Github responded with {:?}", buffer);
    }
    delay_if_running_out_of_requests(response.headers());
    let mut buffer = String::new();
    match response.read_to_string(&mut buffer) {
        Ok(_) => (),
        Err(e) => println!(
            "error reading response from github when getting repo list: {}",
            e
        ),
    }
    let mut repos = repo_list_from_string(&buffer).expect("expected repos");

    if response_has_a_next_link(response.headers()) {
        loop {
            let paging_url = response_next_link(response.headers()).expect("a thing");
            response = get_repos_at_url(paging_url, client).expect("request failed");
            delay_if_running_out_of_requests(response.headers());
            buffer = String::new();
            match response.read_to_string(&mut buffer) {
                Ok(_) => (),
                Err(e) => println!(
                    "error reading response from github when getting repo list: {}",
                    e
                ),
            }
            repos.append(&mut repo_list_from_string(&buffer).expect("expected repos"));
            if !response_has_a_next_link(response.headers()) {
                break;
            }
        }
    }
    println!("Number of repos to check: {:?}", repos.len());
    Ok(repos)
}

fn get_repos_at_url(url: reqwest::Url, client: &reqwest::Client) -> Result<Response, Error> {
    client.get(url).send()
}

fn repo_list_from_string(json_str: &str) -> Result<Vec<GithubRepo>, String> {
    // This looks a bit weird due to supplying type hints to deserialize:
    let _: Vec<GithubRepo> = match serde_json::from_str(json_str) {
        Ok(v) => return Ok(v),
        Err(e) => return Err(format!("Couldn't deserialize repos from github: {}", e)),
    };
}

pub fn existing_release_pr_location(repo: &GithubRepo, client: &reqwest::Client) -> Option<String> {
    let repo_pr_url = format!("{}/{}", repo.url, "pulls");
    let url = match Url::parse_with_params(&repo_pr_url, &[("head", "master"), ("base", "release")])
    {
        Ok(new_url) => new_url,
        Err(e) => {
            println!("Couldn't create url for existing pr location: {}", e);
            return None;
        }
    };
    let mut res = match client.get(url).send() {
        Ok(response) => response,
        Err(e) => {
            println!("Error in request to github for existing PR location: {}", e);
            return None;
        }
    };
    delay_if_running_out_of_requests(res.headers());
    let mut buffer = String::new();
    match res.read_to_string(&mut buffer) {
        Ok(_) => (),
        Err(e) => println!("error finding existing pr for {}: {}", repo.name, e),
    }

    let pull_reqs: Vec<GithubPullRequest> = match serde_json::from_str(&buffer) {
        Ok(v) => v,
        Err(_) => Vec::new(),
    };

    if !pull_reqs.is_empty() {
        return Some(pull_reqs[0].html_url.clone());
    }

    None
}

pub fn get_commits_from_pr(
    repo: &GithubRepo,
    pr_number: &str,
    client: &reqwest::Client,
    rp_version: &str,
) -> String {
    let pr_commits_url = format!("{}/pulls/{}/commits", repo.url, pr_number);

    let mut res = match client.get(&pr_commits_url).send() {
        Ok(response) => response,
        Err(e) => {
            panic!("Error in request to github for compare page: {}", e);
        }
    };
    delay_if_running_out_of_requests(res.headers());

    let mut buffer = String::new();
    match res.read_to_string(&mut buffer) {
        Ok(_) => (),
        Err(e) => println!("error checking commit diff for {}: {}", pr_commits_url, e),
    }

    let prs: Vec<CommitInPR> = match serde_json::from_str(&buffer) {
        Ok(v) => v,
        Err(e) => panic!(format!("Couldn't deserialize repos from github: {}. Payload: {:#?}", e, buffer)),
    };

    let mut new_body = "automated release partay!\n\nPRs in this release:".to_string();

    for c in prs.into_iter() {
        if c.actual_commit.message.contains("Merge pull request #") {
            // remove the bits we don't need: go from "Merge pull request #1890 from..." to "#1890"
            let pr_number = c.actual_commit.message.split(' ').collect::<Vec<&str>>()[3];
            new_body.push_str(&format!("\n* {}", pr_number));
        }
    }

    new_body.push_str(&format!("\n\n---\nMade by `{}`.", rp_version));

    new_body
}

pub fn set_pr_body(repo: &GithubRepo, pr_number: &str, body: &str, client: &reqwest::Client) {
    let mut pr_body = HashMap::new();
    pr_body.insert("body", body);

    let repo_pr_url = format!("{}/pulls/{}", repo.url, pr_number);
    let res = match client.patch(&repo_pr_url).json(&pr_body).send() {
        Ok(response) => response,
        Err(e) => panic!(format!("Error in request to github creating new PR: {}", e)),
    };
    delay_if_running_out_of_requests(res.headers());
}

pub fn update_pr_body(
    repo: &GithubRepo,
    pr_number: &str,
    client: &reqwest::Client,
    rp_version: &str,
) {
    let new_body = get_commits_from_pr(repo, pr_number, client, rp_version);
    set_pr_body(repo, pr_number, &new_body, client);
}

// Try to create the release PR and return the URL of it:
pub fn create_release_pull_request(
    repo: &GithubRepo,
    client: &reqwest::Client,
) -> Result<String, String> {
    let mut pr_body = HashMap::new();
    pr_body.insert("title", "automated release partay");
    pr_body.insert("head", "master");
    pr_body.insert("base", "release");

    let repo_pr_url = format!("{}/{}", repo.url, "pulls");
    let mut res = match client.post(&repo_pr_url).json(&pr_body).send() {
        Ok(response) => response,
        Err(e) => return Err(format!("Error in request to github creating new PR: {}", e)),
    };

    delay_if_running_out_of_requests(res.headers());
    if res.status().is_success() {
        let mut buffer = String::new();
        match res.read_to_string(&mut buffer) {
            Ok(_) => (),
            Err(e) => println!(
                "error reading response after creating new release PR for {}: {}",
                repo.name, e
            ),
        }
        let pull_req: GithubPullRequest = match serde_json::from_str(&buffer) {
            Ok(v) => v,
            Err(e) => {
                return Err(format!(
                    "Couldn't deserialize create pull req response for {}: {}",
                    repo.name, e
                ));
            }
        };
        return Ok(pull_req.html_url);
    }
    // 422 unprocessable means it's there already
    // 422 unprocessable also means the branch is up to date

    Err("Release branch already up to date?".to_string())
}

#[cfg(test)]
mod tests {
    use super::*;
    use reqwest::header::HeaderMap;

    #[test]
    fn plenty_of_requests_left() {
        let mut plenty_left_headers = HeaderMap::new();
        plenty_left_headers.insert("X-RateLimit-Remaining", "10000".parse().unwrap());

        assert_eq!(
            false,
            close_to_running_out_of_requests(&plenty_left_headers)
        );
    }

    #[test]
    fn almost_no_requests_left() {
        let mut nothing_left_headers = HeaderMap::new();
        nothing_left_headers.insert("X-RateLimit-Remaining", "1".parse().unwrap());
        assert_eq!(
            true,
            close_to_running_out_of_requests(&nothing_left_headers)
        );
    }

    #[test]
    fn no_next_link() {
        assert_eq!(false, response_has_a_next_link(&HeaderMap::new()));
    }

    #[test]
    fn has_next_link() {
        let mut headers = HeaderMap::new();
        headers.insert(
            LINK,
            "<http://example.com/TheBook/chapter2>; rel=\"next\"; title=\"next page\""
                .parse()
                .unwrap(),
        );

        assert_eq!(true, response_has_a_next_link(&headers));
    }

    #[test]
    fn finds_next_link() {
        let mut headers = HeaderMap::new();
        headers.insert(
            LINK,
            "<http://example.com/TheBook/chapter3>; rel=\"next\"; title=\"next page\""
                .parse()
                .unwrap(),
        );

        let expected_uri = Url::parse("http://example.com/TheBook/chapter3").unwrap();
        assert_eq!(expected_uri, response_next_link(&headers).unwrap());
    }
}
<|endoftext|>


<commit_before>use scene::{BoxedScene, Scene};
use event::{Event, RenderArgs};
use conrod::UiContext;
use opengl_graphics::{Gl};
use graphics::{Context, AddColor, Draw, AddRectangle};
use conrod::{
    Background,
    Color,
    Colorable,
    Drawable,
    Label,
    Labelable,
    Positionable
};
use input::{Release, Keyboard, keyboard};
use uorustlibs::hues::{HueReader, HueGroup, Hue};
use uorustlibs::color::Color16;
use uorustlibs::color::Color as ColorTrait;

use std::io::IoResult;

pub struct HuesScene {
    reader: IoResult<HueReader>,
    index: u32,
    current_group: Option<IoResult<HueGroup>>
}

impl HuesScene {
    pub fn new() -> BoxedScene {
        let mut scene = box HuesScene {
            reader: HueReader::new(&Path::new("./assets/hues.mul")),
            current_group: None,
            index: 0
        };
        scene.load_group();
        scene
    }

    fn load_group(&mut self) {
        match self.reader {
            Ok(ref mut hue_reader) => {
                self.current_group = Some(hue_reader.read_hue_group(self.index))
            },
            Err(_) => ()
        }
    }

    fn render(&self, args: RenderArgs, uic: &mut UiContext, gl: &mut Gl) {
        uic.background().color(Color::black()).draw(gl);
        match self.reader {
            Ok(ref hue_reader) => {
                self.render_hue_group(args, uic, gl)
            },
            Err(ref error) => {
                self.draw_label(uic, gl, format!("{}", error).as_slice(), 0.0, 16.0);
            }
        };
    }

    fn render_hue_group(&self, args: RenderArgs, uic: &mut UiContext, gl: &mut Gl) {
        self.draw_label(uic, gl, format!("Hue group {}", self.index).as_slice(), 0.0, 0.0);

        match self.current_group {
            Some(Ok(ref hue_group)) => {
                self.draw_label(uic, gl, format!("Header {}", hue_group.header).as_slice(), 0.0, 16.0);
                for (idx, hue) in hue_group.entries.iter().enumerate() {
                    self.render_hue(args, uic, gl, idx as u32, hue);
                }
            },
            Some(Err(ref error)) => {
                self.draw_label(uic, gl, format!("{}", error).as_slice(), 0.0, 16.0);
            },
            None => {
                self.draw_label(uic, gl, "No Hue Group Loaded", 0.0, 16.0);
            }
        }
    }

    fn render_hue(&self, args: RenderArgs, uic: &mut UiContext, gl: &mut Gl, index: u32, hue: &Hue) {
        self.draw_label(uic, gl, hue.name.as_slice(), 0.0, (32 + (index * 16)) as f64);
        self.draw_label(uic, gl, format!("{} - {}", hue.table_start, hue.table_end).as_slice(), 256.0, (32 + (index * 16)) as f64);
        let c = Context::abs(args.width as f64, args.height as f64);

        for (col_idx, &color) in hue.color_table.iter().enumerate() {
            let (r, g, b) = color.to_tuple();
            c.rect((256.0 + 128.0) + (col_idx * 16) as f64, (32 + (index * 16)) as f64, 16.0, 16.0)
            .rgb(r as f32 / 255.0, g as f32 / 255.0, b as f32 / 255.0)
            .draw(gl);
        }
    }

    fn draw_label(&self, uic: &mut UiContext, gl: &mut Gl, label: &str, x: f64, y: f64) {
        uic.label(label)
            .position(x, y)
            .size(16u32)
            .color(Color::white())
            .draw(gl);
    }
}

impl Scene for HuesScene {
    fn handle_event(&mut self, e: &Event, ui_context: &mut UiContext, gl: &mut Gl) -> Option<BoxedScene> {
        match *e {
            Event::Render(args) => {
                self.render(args, ui_context, gl);
            },
            Event::Input(Release(Keyboard(key))) => {
                match key {
                    keyboard::Left => {
                        if self.index > 0 {
                            self.index -= 1;
                            self.load_group();
                        }
                    },
                    keyboard::Right => {
                        self.index += 1;
                        self.load_group();
                    },
                    _ => ()
                }
            },
            _ => ()
        };
        None
    }
}
<commit_msg>Updated to latest<commit_after>use scene::{BoxedScene, Scene};
use event::{Event, RenderArgs};
use conrod::UiContext;
use opengl_graphics::{Gl};
use graphics::{Context, AddColor, Draw, AddRectangle};
use conrod::{
    Background,
    Color,
    Colorable,
    Drawable,
    Label,
    Labelable,
    Positionable
};
use input::{Release, Keyboard, keyboard};
use uorustlibs::hues::{HueReader, HueGroup, Hue};
use uorustlibs::color::Color16;
use uorustlibs::color::Color as ColorTrait;

use std::io::IoResult;

pub struct HuesScene {
    reader: IoResult<HueReader>,
    index: u32,
    current_group: Option<IoResult<HueGroup>>
}

impl HuesScene {
    pub fn new() -> BoxedScene {
        let mut scene = box HuesScene {
            reader: HueReader::new(&Path::new("./assets/hues.mul")),
            current_group: None,
            index: 0
        };
        scene.load_group();
        scene
    }

    fn load_group(&mut self) {
        match self.reader {
            Ok(ref mut hue_reader) => {
                self.current_group = Some(hue_reader.read_hue_group(self.index))
            },
            Err(_) => ()
        }
    }

    fn render(&self, args: RenderArgs, uic: &mut UiContext, gl: &mut Gl) {
        uic.background().color(Color::black()).draw(gl);
        match self.reader {
            Ok(ref hue_reader) => {
                self.render_hue_group(args, uic, gl)
            },
            Err(ref error) => {
                self.draw_label(uic, gl, format!("{}", error).as_slice(), 0.0, 16.0);
            }
        };
    }

    fn render_hue_group(&self, args: RenderArgs, uic: &mut UiContext, gl: &mut Gl) {
        self.draw_label(uic, gl, format!("Hue group {}", self.index).as_slice(), 0.0, 0.0);

        match self.current_group {
            Some(Ok(ref hue_group)) => {
                self.draw_label(uic, gl, format!("Header {}", hue_group.header).as_slice(), 0.0, 16.0);
                for (idx, hue) in hue_group.entries.iter().enumerate() {
                    self.render_hue(args, uic, gl, idx as u32, hue);
                }
            },
            Some(Err(ref error)) => {
                self.draw_label(uic, gl, format!("{}", error).as_slice(), 0.0, 16.0);
            },
            None => {
                self.draw_label(uic, gl, "No Hue Group Loaded", 0.0, 16.0);
            }
        }
    }

    fn render_hue(&self, args: RenderArgs, uic: &mut UiContext, gl: &mut Gl, index: u32, hue: &Hue) {
        self.draw_label(uic, gl, hue.name.as_slice(), 0.0, (32 + (index * 16)) as f64);
        self.draw_label(uic, gl, format!("{} - {}", hue.table_start, hue.table_end).as_slice(), 256.0, (32 + (index * 16)) as f64);
        let c = Context::abs(args.width as f64, args.height as f64);

        for (col_idx, &color) in hue.color_table.iter().enumerate() {
            let (r, g, b, _) = color.to_rgba();
            c.rect((256.0 + 128.0) + (col_idx * 16) as f64, (32 + (index * 16)) as f64, 16.0, 16.0)
            .rgb(r as f32 / 255.0, g as f32 / 255.0, b as f32 / 255.0)
            .draw(gl);
        }
    }

    fn draw_label(&self, uic: &mut UiContext, gl: &mut Gl, label: &str, x: f64, y: f64) {
        uic.label(label)
            .position(x, y)
            .size(16u32)
            .color(Color::white())
            .draw(gl);
    }
}

impl Scene for HuesScene {
    fn handle_event(&mut self, e: &Event, ui_context: &mut UiContext, gl: &mut Gl) -> Option<BoxedScene> {
        match *e {
            Event::Render(args) => {
                self.render(args, ui_context, gl);
            },
            Event::Input(Release(Keyboard(key))) => {
                match key {
                    keyboard::Left => {
                        if self.index > 0 {
                            self.index -= 1;
                            self.load_group();
                        }
                    },
                    keyboard::Right => {
                        self.index += 1;
                        self.load_group();
                    },
                    _ => ()
                }
            },
            _ => ()
        };
        None
    }
}
<|endoftext|>


<commit_before>#![allow(unused_variables, dead_code)] // macros

/// Dbus macro for Layout code

use super::utils::{parse_uuid, parse_direction, parse_axis, lock_tree_dbus};

use dbus::tree::MethodErr;

use super::super::layout::{Layout, commands as layout_cmd};

dbus_interface! {
    path: "/org/way_cooler/Layout";
    name: "org.way_cooler.Layout";

    fn ActiveContainerId() -> container_id: DBusResult<String> {
        let tree = try!(lock_tree_dbus());
        match tree.active_id() {
            Some(id) => Ok(id.to_string()),
            None => Ok("".to_string())
        }
    }

    fn ToggleFloat(container_id: String) -> success: DBusResult<bool> {
        let maybe_uuid = try!(parse_uuid("container_id", &container_id));
        match maybe_uuid {
            Some(uuid) => {
                let mut tree = try!(lock_tree_dbus());
                tree.toggle_float()
                    .and(Ok(true))
                    .map_err(|err| {
                        MethodErr::failed(&format!("{:?}", err))
                    })
            },
            None => {
                layout_cmd::toggle_float();
                Ok(true)
            }
        }
    }

    fn MoveContainer(container_id: String, direction: String) -> success: DBusResult<bool> {
        let target_uuid = try!(parse_uuid("container_id", &container_id));
        let direction = try!(parse_direction("direction", direction.as_str()));
        let mut tree = try!(lock_tree_dbus());
        tree.move_active(target_uuid, direction)
            .and(Ok(true))
            .map_err(|err| MethodErr::failed(&format!("{:?}", err)))
    }

    fn SplitContainer(container_id: String, split_axis: String) -> success: DBusResult<bool> {
        let uuid = try!(parse_uuid("container_id", &container_id));
        let axis = try!(parse_axis("split_direction", split_axis.as_str()));
        // TODO Tree commands need to have these defined on the Tree,
        // for now this is _ok_, but we are swallowing an potential Tree lock error here.
        match axis {
            Layout::Horizontal => layout_cmd::split_horizontal(),
            Layout::Vertical => layout_cmd::split_vertical()
        }
        Ok(true)
    }

    fn ToggleCardinalTiling(container_id: String) -> success: DBusResult<bool> {
        let mut tree = try!(lock_tree_dbus());
        let uuid = try!(try!(parse_uuid("container_id", &container_id))
                        .or_else(|| tree.active_id())
                        .ok_or(MethodErr::failed(&"No active container")));
        tree.toggle_cardinal_tiling(uuid)
            .and(Ok(true))
            .map_err(|err| MethodErr::failed(&format!("{:?}", err)))
    }

    fn SwitchWorkspace(w_name: String) -> success: DBusResult<bool> {
        let mut tree = try!(lock_tree_dbus());
        tree.switch_to_workspace(w_name.as_str())
            .and(Ok(true))
            .map_err(|err| MethodErr::failed(&format!("{:?}", err)))
    }

    fn CloseView(view_id: String) -> success: DBusResult<bool> {
        let mut tree = try!(lock_tree_dbus());
        let uuid = try!(try!(parse_uuid("view_id", &view_id))
            .or_else(|| tree.active_id())
            .ok_or(MethodErr::failed(&"No active container")));
        tree.remove_view_by_id(uuid)
            .and(Ok(true))
            .map_err(|err| MethodErr::failed(&format!("{:?}", err)))
    }

    fn Focus(container_id: String) -> success: DBusResult<bool> {
        let mut tree = try!(lock_tree_dbus());
        let uuid = try!(try!(parse_uuid("container_id", &container_id))
                        .or_else(|| tree.active_id())
                        .ok_or(MethodErr::failed(&"No active container")));

        tree.focus(uuid)
            .and(Ok(true))
            .map_err(|err| MethodErr::failed(&format!("{:?}", err)))
    }

    fn FocusDir(direction: String) -> success: DBusResult<bool> {
        let direction = try!(parse_direction("direction", direction.as_str()));
        let mut tree = try!(lock_tree_dbus());
        tree.move_focus(direction)
            .and(Ok(true))
            .map_err(|err| MethodErr::failed(&format!("{:?}", err)))
    }

    fn SendToWorkspace(container_id: String, w_name: String) -> success: DBusResult<bool> {
        let mut tree = try!(lock_tree_dbus());
        let uuid = try!(try!(parse_uuid("container_id", &container_id))
                        .or_else(|| tree.active_id())
                        .ok_or(MethodErr::failed(&"No active container")));
        tree.send_to_workspace(uuid, w_name.as_str())
            .and(Ok(true))
            .map_err(|err| MethodErr::failed(&format!("{:?}", err)))
    }

    fn Debug() -> success: DBusResult<String> {
        Ok(format!("{}", layout_cmd::tree_as_json()))
    }

    fn ContainerInActiveWorkspace(container_id: String) -> success: DBusResult<bool> {
        let tree = try!(lock_tree_dbus());
        let uuid = try!(try!(parse_uuid("container_id", &container_id))
                        .or_else(|| tree.active_id())
                        .ok_or(MethodErr::failed(&"No active container")));
        tree.container_in_active_workspace(uuid)
            .map_err(|err| MethodErr::failed(&format!("{:?}", err)))
    }
}
<commit_msg>Added FullScreen D-Bus command<commit_after>#![allow(unused_variables, dead_code)] // macros

/// Dbus macro for Layout code

use super::utils::{parse_uuid, parse_direction, parse_axis, lock_tree_dbus};

use dbus::tree::MethodErr;

use super::super::layout::{Layout, commands as layout_cmd};

dbus_interface! {
    path: "/org/way_cooler/Layout";
    name: "org.way_cooler.Layout";

    fn ActiveContainerId() -> container_id: DBusResult<String> {
        let tree = try!(lock_tree_dbus());
        match tree.active_id() {
            Some(id) => Ok(id.to_string()),
            None => Ok("".to_string())
        }
    }

    fn ToggleFloat(container_id: String) -> success: DBusResult<bool> {
        let maybe_uuid = try!(parse_uuid("container_id", &container_id));
        match maybe_uuid {
            Some(uuid) => {
                let mut tree = try!(lock_tree_dbus());
                tree.toggle_float()
                    .and(Ok(true))
                    .map_err(|err| {
                        MethodErr::failed(&format!("{:?}", err))
                    })
            },
            None => {
                layout_cmd::toggle_float();
                Ok(true)
            }
        }
    }

    fn MoveContainer(container_id: String, direction: String) -> success: DBusResult<bool> {
        let target_uuid = try!(parse_uuid("container_id", &container_id));
        let direction = try!(parse_direction("direction", direction.as_str()));
        let mut tree = try!(lock_tree_dbus());
        tree.move_active(target_uuid, direction)
            .and(Ok(true))
            .map_err(|err| MethodErr::failed(&format!("{:?}", err)))
    }

    fn SplitContainer(container_id: String, split_axis: String) -> success: DBusResult<bool> {
        let uuid = try!(parse_uuid("container_id", &container_id));
        let axis = try!(parse_axis("split_direction", split_axis.as_str()));
        // TODO Tree commands need to have these defined on the Tree,
        // for now this is _ok_, but we are swallowing an potential Tree lock error here.
        match axis {
            Layout::Horizontal => layout_cmd::split_horizontal(),
            Layout::Vertical => layout_cmd::split_vertical()
        }
        Ok(true)
    }

    fn ToggleCardinalTiling(container_id: String) -> success: DBusResult<bool> {
        let mut tree = try!(lock_tree_dbus());
        let uuid = try!(try!(parse_uuid("container_id", &container_id))
                        .or_else(|| tree.active_id())
                        .ok_or(MethodErr::failed(&"No active container")));
        tree.toggle_cardinal_tiling(uuid)
            .and(Ok(true))
            .map_err(|err| MethodErr::failed(&format!("{:?}", err)))
    }

    fn SwitchWorkspace(w_name: String) -> success: DBusResult<bool> {
        let mut tree = try!(lock_tree_dbus());
        tree.switch_to_workspace(w_name.as_str())
            .and(Ok(true))
            .map_err(|err| MethodErr::failed(&format!("{:?}", err)))
    }

    fn CloseView(view_id: String) -> success: DBusResult<bool> {
        let mut tree = try!(lock_tree_dbus());
        let uuid = try!(try!(parse_uuid("view_id", &view_id))
            .or_else(|| tree.active_id())
            .ok_or(MethodErr::failed(&"No active container")));
        tree.remove_view_by_id(uuid)
            .and(Ok(true))
            .map_err(|err| MethodErr::failed(&format!("{:?}", err)))
    }

    fn Focus(container_id: String) -> success: DBusResult<bool> {
        let mut tree = try!(lock_tree_dbus());
        let uuid = try!(try!(parse_uuid("container_id", &container_id))
                        .or_else(|| tree.active_id())
                        .ok_or(MethodErr::failed(&"No active container")));

        tree.focus(uuid)
            .and(Ok(true))
            .map_err(|err| MethodErr::failed(&format!("{:?}", err)))
    }

    fn FocusDir(direction: String) -> success: DBusResult<bool> {
        let direction = try!(parse_direction("direction", direction.as_str()));
        let mut tree = try!(lock_tree_dbus());
        tree.move_focus(direction)
            .and(Ok(true))
            .map_err(|err| MethodErr::failed(&format!("{:?}", err)))
    }

    fn SendToWorkspace(container_id: String, w_name: String) -> success: DBusResult<bool> {
        let mut tree = try!(lock_tree_dbus());
        let uuid = try!(try!(parse_uuid("container_id", &container_id))
                        .or_else(|| tree.active_id())
                        .ok_or(MethodErr::failed(&"No active container")));
        tree.send_to_workspace(uuid, w_name.as_str())
            .and(Ok(true))
            .map_err(|err| MethodErr::failed(&format!("{:?}", err)))
    }

    fn Debug() -> success: DBusResult<String> {
        Ok(format!("{}", layout_cmd::tree_as_json()))
    }

    fn ContainerInActiveWorkspace(container_id: String) -> success: DBusResult<bool> {
        let tree = try!(lock_tree_dbus());
        let uuid = try!(try!(parse_uuid("container_id", &container_id))
                        .or_else(|| tree.active_id())
                        .ok_or(MethodErr::failed(&"No active container")));
        tree.container_in_active_workspace(uuid)
            .map_err(|err| MethodErr::failed(&format!("{:?}", err)))
    }

    fn FullScreen(container_id: String, toggle: bool) -> success: DBusResult<bool> {
        let mut tree = try!(lock_tree_dbus());
        let uuid = try!(try!(parse_uuid("container_id", &container_id))
                        .or_else(|| tree.active_id())
                        .ok_or(MethodErr::failed(&"No active container")));
        tree.set_fullscreen(uuid, toggle)
            .and(Ok(true))
            .map_err(|err| MethodErr::failed(&format!("{:?}", err)))
    }
}
<|endoftext|>


<commit_before>use cargo::core::resolver::ResolveError;
use cargo::core::{compiler::CompileMode, Workspace};
use cargo::ops::{self, CompileOptions};
use cargo::util::{config::Config, errors::ManifestError};

use crate::support::project;

/// Tests inclusion of a `ManifestError` pointing to a member manifest
/// when that manifest fails to deserialize.
#[test]
fn toml_deserialize_manifest_error() {
    let p = project()
        .file(
            "Cargo.toml",
            r#"
            [project]
            name = "foo"
            version = "0.1.0"
            authors = []

            [dependencies]
            bar = { path = "bar" }

            [workspace]
        "#,
        )
        .file("src/main.rs", "fn main() {}")
        .file(
            "bar/Cargo.toml",
            r#"
            [project]
            name = "bar"
            version = "0.1.0"
            authors = []

            [dependencies]
            foobar == "0.55"
        "#,
        )
        .file("bar/src/main.rs", "fn main() {}")
        .build();

    let root_manifest_path = p.root().join("Cargo.toml");
    let member_manifest_path = p.root().join("bar").join("Cargo.toml");

    let error = Workspace::new(&root_manifest_path, &Config::default().unwrap()).unwrap_err();
    eprintln!("{:?}", error);

    let manifest_err: &ManifestError = error.downcast_ref().expect("Not a ManifestError");
    assert_eq!(manifest_err.manifest_path(), &root_manifest_path);

    let causes: Vec<_> = manifest_err.manifest_causes().collect();
    assert_eq!(causes.len(), 1, "{:?}", causes);
    assert_eq!(causes[0].manifest_path(), &member_manifest_path);
}

/// Tests inclusion of a `ManifestError` pointing to a member manifest
/// when that manifest has an invalid dependency path.
#[test]
fn member_manifest_path_io_error() {
    let p = project()
        .file(
            "Cargo.toml",
            r#"
            [project]
            name = "foo"
            version = "0.1.0"
            authors = []

            [dependencies]
            bar = { path = "bar" }

            [workspace]
        "#,
        )
        .file("src/main.rs", "fn main() {}")
        .file(
            "bar/Cargo.toml",
            r#"
            [project]
            name = "bar"
            version = "0.1.0"
            authors = []

            [dependencies]
            foobar = { path = "nosuch" }
        "#,
        )
        .file("bar/src/main.rs", "fn main() {}")
        .build();

    let root_manifest_path = p.root().join("Cargo.toml");
    let member_manifest_path = p.root().join("bar").join("Cargo.toml");
    let missing_manifest_path = p.root().join("bar").join("nosuch").join("Cargo.toml");

    let error = Workspace::new(&root_manifest_path, &Config::default().unwrap()).unwrap_err();
    eprintln!("{:?}", error);

    let manifest_err: &ManifestError = error.downcast_ref().expect("Not a ManifestError");
    assert_eq!(manifest_err.manifest_path(), &root_manifest_path);

    let causes: Vec<_> = manifest_err.manifest_causes().collect();
    assert_eq!(causes.len(), 2, "{:?}", causes);
    assert_eq!(causes[0].manifest_path(), &member_manifest_path);
    assert_eq!(causes[1].manifest_path(), &missing_manifest_path);
}

/// Tests dependency version errors provide which package failed via a `ResolveError`.
#[test]
fn member_manifest_version_error() {
    let p = project()
        .file(
            "Cargo.toml",
            r#"
            [project]
            name = "foo"
            version = "0.1.0"
            authors = []

            [dependencies]
            bar = { path = "bar" }

            [workspace]
        "#,
        )
        .file("src/main.rs", "fn main() {}")
        .file(
            "bar/Cargo.toml",
            r#"
            [project]
            name = "bar"
            version = "0.1.0"
            authors = []

            [dependencies]
            i-dont-exist = "0.55"
        "#,
        )
        .file("bar/src/main.rs", "fn main() {}")
        .build();

    let config = Config::default().unwrap();
    let ws = Workspace::new(&p.root().join("Cargo.toml"), &config).unwrap();
    let compile_options = CompileOptions::new(&config, CompileMode::Build).unwrap();
    let member_bar = ws.members().find(|m| &*m.name() == "bar").unwrap();

    let error = ops::compile(&ws, &compile_options).map(|_| ()).unwrap_err();
    eprintln!("{:?}", error);

    let resolve_err: &ResolveError = error.downcast_ref().expect("Not a ResolveError");
    let package_path = resolve_err.package_path();
    assert_eq!(package_path.len(), 1, "package_path: {:?}", package_path);
    assert_eq!(package_path[0], member_bar.package_id());
}
<commit_msg>Fix member_manifest_version_error accessing the network.<commit_after>use cargo::core::resolver::ResolveError;
use cargo::core::{compiler::CompileMode, Shell, Workspace};
use cargo::ops::{self, CompileOptions};
use cargo::util::{config::Config, errors::ManifestError};

use crate::support::install::cargo_home;
use crate::support::project;
use crate::support::registry;

/// Tests inclusion of a `ManifestError` pointing to a member manifest
/// when that manifest fails to deserialize.
#[test]
fn toml_deserialize_manifest_error() {
    let p = project()
        .file(
            "Cargo.toml",
            r#"
            [project]
            name = "foo"
            version = "0.1.0"
            authors = []

            [dependencies]
            bar = { path = "bar" }

            [workspace]
        "#,
        )
        .file("src/main.rs", "fn main() {}")
        .file(
            "bar/Cargo.toml",
            r#"
            [project]
            name = "bar"
            version = "0.1.0"
            authors = []

            [dependencies]
            foobar == "0.55"
        "#,
        )
        .file("bar/src/main.rs", "fn main() {}")
        .build();

    let root_manifest_path = p.root().join("Cargo.toml");
    let member_manifest_path = p.root().join("bar").join("Cargo.toml");

    let error = Workspace::new(&root_manifest_path, &Config::default().unwrap()).unwrap_err();
    eprintln!("{:?}", error);

    let manifest_err: &ManifestError = error.downcast_ref().expect("Not a ManifestError");
    assert_eq!(manifest_err.manifest_path(), &root_manifest_path);

    let causes: Vec<_> = manifest_err.manifest_causes().collect();
    assert_eq!(causes.len(), 1, "{:?}", causes);
    assert_eq!(causes[0].manifest_path(), &member_manifest_path);
}

/// Tests inclusion of a `ManifestError` pointing to a member manifest
/// when that manifest has an invalid dependency path.
#[test]
fn member_manifest_path_io_error() {
    let p = project()
        .file(
            "Cargo.toml",
            r#"
            [project]
            name = "foo"
            version = "0.1.0"
            authors = []

            [dependencies]
            bar = { path = "bar" }

            [workspace]
        "#,
        )
        .file("src/main.rs", "fn main() {}")
        .file(
            "bar/Cargo.toml",
            r#"
            [project]
            name = "bar"
            version = "0.1.0"
            authors = []

            [dependencies]
            foobar = { path = "nosuch" }
        "#,
        )
        .file("bar/src/main.rs", "fn main() {}")
        .build();

    let root_manifest_path = p.root().join("Cargo.toml");
    let member_manifest_path = p.root().join("bar").join("Cargo.toml");
    let missing_manifest_path = p.root().join("bar").join("nosuch").join("Cargo.toml");

    let error = Workspace::new(&root_manifest_path, &Config::default().unwrap()).unwrap_err();
    eprintln!("{:?}", error);

    let manifest_err: &ManifestError = error.downcast_ref().expect("Not a ManifestError");
    assert_eq!(manifest_err.manifest_path(), &root_manifest_path);

    let causes: Vec<_> = manifest_err.manifest_causes().collect();
    assert_eq!(causes.len(), 2, "{:?}", causes);
    assert_eq!(causes[0].manifest_path(), &member_manifest_path);
    assert_eq!(causes[1].manifest_path(), &missing_manifest_path);
}

/// Tests dependency version errors provide which package failed via a `ResolveError`.
#[test]
fn member_manifest_version_error() {
    let p = project()
        .file(
            "Cargo.toml",
            r#"
            [project]
            name = "foo"
            version = "0.1.0"
            authors = []

            [dependencies]
            bar = { path = "bar" }

            [workspace]
        "#,
        )
        .file("src/main.rs", "fn main() {}")
        .file(
            "bar/Cargo.toml",
            r#"
            [project]
            name = "bar"
            version = "0.1.0"
            authors = []

            [dependencies]
            i-dont-exist = "0.55"
        "#,
        )
        .file("bar/src/main.rs", "fn main() {}")
        .build();

    // Prevent this test from accessing the network by setting up .cargo/config.
    registry::init();
    let config = Config::new(Shell::new(), cargo_home(), cargo_home());
    let ws = Workspace::new(&p.root().join("Cargo.toml"), &config).unwrap();
    let compile_options = CompileOptions::new(&config, CompileMode::Build).unwrap();
    let member_bar = ws.members().find(|m| &*m.name() == "bar").unwrap();

    let error = ops::compile(&ws, &compile_options).map(|_| ()).unwrap_err();
    eprintln!("{:?}", error);

    let resolve_err: &ResolveError = error.downcast_ref().expect("Not a ResolveError");
    let package_path = resolve_err.package_path();
    assert_eq!(package_path.len(), 1, "package_path: {:?}", package_path);
    assert_eq!(package_path[0], member_bar.package_id());
}
<|endoftext|>


<commit_before>use std::str;

bitflags! {
    pub struct Flags : u8 {
        const BACKSL = 1;
        const SQUOTE = 2;
        const DQUOTE = 4;
        const TRIM   = 8;
        const ARRAY  = 16;
    }
}

pub(crate) struct QuoteTerminator {
    buffer:     String,
    eof:        Option<String>,
    eof_buffer: String,
    read:       usize,
    flags:      Flags,
}

impl QuoteTerminator {
    pub(crate) fn new(input: String) -> QuoteTerminator {
        QuoteTerminator {
            buffer:     input,
            eof:        None,
            eof_buffer: String::new(),
            read:       0,
            flags:      Flags::empty(),
        }
    }

    pub(crate) fn append(&mut self, input: String) {
        if self.eof.is_none() {
            self.buffer.push_str(if self.flags.contains(TRIM) { input.trim() } else { &input });
        } else {
            self.eof_buffer.push_str(&input);
        }
    }

    pub(crate) fn check_termination(&mut self) -> bool {
        let mut eof_line = None;
        let eof = self.eof.clone();
        let status = if let Some(ref eof) = eof {
            let line = &self.eof_buffer;
            eof_line = Some([&line, "\n"].concat());
            line.trim() == eof
        } else {
            {
                let mut eof_found = false;
                let mut comment = false;
                {
                    let mut bytes = self.buffer.bytes().skip(self.read);
                    while let Some(character) = bytes.next() {
                        self.read += 1;
                        match character {
                            _ if self.flags.contains(BACKSL) => self.flags ^= BACKSL,
                            b'\\' => self.flags ^= BACKSL,
                            b'\'' if !self.flags.intersects(DQUOTE) => self.flags ^= SQUOTE,
                            b'"' if !self.flags.intersects(SQUOTE) => self.flags ^= DQUOTE,
                            b'<' if !self.flags.contains(SQUOTE | DQUOTE) => {
                                let as_bytes = self.buffer.as_bytes();
                                if Some(&b'<') == as_bytes.get(self.read) {
                                    self.read += 1;
                                    if Some(&b'<') != as_bytes.get(self.read) {
                                        let eof_phrase = unsafe {
                                            str::from_utf8_unchecked(&as_bytes[self.read..])
                                        };
                                        self.eof = Some(eof_phrase.trim().to_owned());
                                        eof_found = true;
                                        break;
                                    }
                                }
                            }
                            b'[' if !self.flags.intersects(DQUOTE | SQUOTE) => {
                                self.flags |= ARRAY;
                            }
                            b']' if !self.flags.intersects(DQUOTE | SQUOTE) => {
                                self.flags -= ARRAY;
                            }
                            b'#' if !self.flags.intersects(DQUOTE | SQUOTE) => {
                                if self.read > 1 {
                                    let character = self.buffer.as_bytes().get(self.read - 2).unwrap();
                                    if [b' ', b'\n'].contains(character) {
                                        comment = true;
                                        break
                                    }
                                } else {
                                    comment = true;
                                    break
                                }
                            }
                            _ => (),
                        }
                    }
                }
                if eof_found {
                    self.buffer.push('\n');
                    return false;
                } else if comment {
                    self.buffer.truncate(self.read - 1);
                    return !self.flags.intersects(SQUOTE | DQUOTE | ARRAY);
                }
            }

            if self.flags.intersects(SQUOTE | DQUOTE | ARRAY) {
                if let Some(b'\\') = self.buffer.bytes().last() {
                    let _ = self.buffer.pop();
                    self.read -= 1;
                    self.flags |= TRIM;
                } else {
                    self.read += 1;
                    self.buffer.push(if self.flags.contains(ARRAY) { ' ' } else { '\n' });
                }
                false
            } else {
                if let Some(b'\\') = self.buffer.bytes().last() {
                    let _ = self.buffer.pop();
                    self.read -= 1;
                    self.flags |= TRIM;
                    false
                } else {
                    // If the last two bytes are either '&&' or '||', we aren't terminated yet.
                    let bytes = self.buffer.as_bytes();
                    if bytes.len() >= 2 {
                        let bytes = &bytes[bytes.len() - 2..];
                        bytes != &[b'&', b'&'] && bytes != &[b'|', b'|']
                    } else {
                        true
                    }
                }
            }
        };

        if let Some(line) = eof_line {
            self.buffer.push_str(&line);
        }
        if self.eof.is_some() {
            self.eof_buffer.clear();
            if status {
                self.eof = None;
            }
        }
        status
    }

    pub(crate) fn consume(self) -> String { self.buffer }
}
<commit_msg>Ensure termination of multi-dimensional arrays<commit_after>use std::str;

bitflags! {
    pub struct Flags : u8 {
        const SQUOTE = 1;
        const DQUOTE = 2;
        const TRIM   = 4;
        const ARRAY  = 8;
        const COMM   = 16;
        const EOF    = 32;
    }
}

pub(crate) struct QuoteTerminator {
    buffer:     String,
    eof:        Option<String>,
    eof_buffer: String,
    array:      usize,
    read:       usize,
    flags:      Flags,
}

impl QuoteTerminator {
    pub(crate) fn new(input: String) -> QuoteTerminator {
        QuoteTerminator {
            buffer:     input,
            eof:        None,
            eof_buffer: String::new(),
            array:      0,
            read:       0,
            flags:      Flags::empty(),
        }
    }

    pub(crate) fn append(&mut self, input: String) {
        if self.eof.is_none() {
            self.buffer.push_str(if self.flags.contains(TRIM) { input.trim() } else { &input });
        } else {
            self.eof_buffer.push_str(&input);
        }
    }

    pub(crate) fn check_termination(&mut self) -> bool {
        let mut eof_line = None;
        let eof = self.eof.clone();
        let status = if let Some(ref eof) = eof {
            let line = &self.eof_buffer;
            eof_line = Some([&line, "\n"].concat());
            line.trim() == eof
        } else {
            {
                let mut instance = Flags::empty();
                {
                    let mut bytes = self.buffer.bytes().skip(self.read);
                    while let Some(character) = bytes.next() {
                        self.read += 1;
                        match character {
                            b'\\' => { let _ = bytes.next(); },
                            b'\'' if !self.flags.intersects(DQUOTE) => self.flags ^= SQUOTE,
                            b'"' if !self.flags.intersects(SQUOTE) => self.flags ^= DQUOTE,
                            b'<' if !self.flags.contains(SQUOTE | DQUOTE) => {
                                let as_bytes = self.buffer.as_bytes();
                                if Some(&b'<') == as_bytes.get(self.read) {
                                    self.read += 1;
                                    if Some(&b'<') != as_bytes.get(self.read) {
                                        let eof_phrase = unsafe {
                                            str::from_utf8_unchecked(&as_bytes[self.read..])
                                        };
                                        self.eof = Some(eof_phrase.trim().to_owned());
                                        instance |= EOF;
                                        break;
                                    }
                                }
                            }
                            b'[' if !self.flags.intersects(DQUOTE | SQUOTE) => {
                                self.flags |= ARRAY;
                                self.array += 1;
                            }
                            b']' if !self.flags.intersects(DQUOTE | SQUOTE) => {
                                self.array -= 1;
                                if self.array == 0 { self.flags -= ARRAY }
                            }
                            b'#' if !self.flags.intersects(DQUOTE | SQUOTE) => {
                                if self.read > 1 {
                                    let character = self.buffer.as_bytes().get(self.read - 2).unwrap();
                                    if [b' ', b'\n'].contains(character) {
                                        instance |= COMM;
                                        break
                                    }
                                } else {
                                    instance |= COMM;
                                    break
                                }
                            }
                            _ => (),
                        }
                    }
                }
                if instance.contains(EOF) {
                    self.buffer.push('\n');
                    return false;
                } else if instance.contains(COMM) {
                    self.buffer.truncate(self.read - 1);
                    return !self.flags.intersects(SQUOTE | DQUOTE | ARRAY);
                }
            }

            if self.flags.intersects(SQUOTE | DQUOTE | ARRAY) {
                if let Some(b'\\') = self.buffer.bytes().last() {
                    let _ = self.buffer.pop();
                    self.read -= 1;
                    self.flags |= TRIM;
                } else {
                    self.read += 1;
                    self.buffer.push(if self.flags.contains(ARRAY) { ' ' } else { '\n' });
                }
                false
            } else {
                if let Some(b'\\') = self.buffer.bytes().last() {
                    let _ = self.buffer.pop();
                    self.read -= 1;
                    self.flags |= TRIM;
                    false
                } else {
                    // If the last two bytes are either '&&' or '||', we aren't terminated yet.
                    let bytes = self.buffer.as_bytes();
                    if bytes.len() >= 2 {
                        let bytes = &bytes[bytes.len() - 2..];
                        bytes != &[b'&', b'&'] && bytes != &[b'|', b'|']
                    } else {
                        true
                    }
                }
            }
        };

        if let Some(line) = eof_line {
            self.buffer.push_str(&line);
        }
        if self.eof.is_some() {
            self.eof_buffer.clear();
            if status {
                self.eof = None;
            }
        }
        status
    }

    pub(crate) fn consume(self) -> String { self.buffer }
}
<|endoftext|>


<commit_before>use anymap::AnyMap;

pub struct Alloy {
    map: AnyMap
}

impl Alloy {
    pub fn new() -> Alloy {
        Alloy {
            map: AnyMap::new()
        }
    }
}

impl Alloy {
    pub fn find<'a, T: 'static>(&'a self) -> Option<&'a T> {
        self.map.find::<T>()
    }

    pub fn find_mut<'a, T: 'static>(&'a mut self) -> Option<&'a mut T> {
        self.map.find_mut::<T>()
    }

    pub fn insert<T: 'static>(&mut self, value: T) {
        self.map.insert::<T>(value)
    }

    pub fn remove<T: 'static>(&mut self) {
        self.remove::<T>()
    }
}

<commit_msg>(doc) Added rustdoc for alloy module.<commit_after>//! Exposes the `Alloy` type, a flexible storage container
//! that `Ingots` may use to expose new APIs or public data
//! to other `Ingots`.

use anymap::AnyMap;

pub struct Alloy {
    map: AnyMap
}

impl Alloy {
    pub fn new() -> Alloy {
        Alloy {
            map: AnyMap::new()
        }
    }
}

impl Alloy {
    pub fn find<'a, T: 'static>(&'a self) -> Option<&'a T> {
        self.map.find::<T>()
    }

    pub fn find_mut<'a, T: 'static>(&'a mut self) -> Option<&'a mut T> {
        self.map.find_mut::<T>()
    }

    pub fn insert<T: 'static>(&mut self, value: T) {
        self.map.insert::<T>(value)
    }

    pub fn remove<T: 'static>(&mut self) {
        self.remove::<T>()
    }
}

<|endoftext|>


<commit_before>// Copyright 2014 Dmitry "Divius" Tantsur <divius.inside@gmail.com>
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
//

//! DHT node table implementation based on Kademlia.
//!
//! See [original paper](http://pdos.csail.mit.edu/%7Epetar/papers/maymounkov-kademlia-lncs.pdf)
//! for details. The most essential difference is that when k-bucket is full,
//! no RPC call is done. It is up to upper-level code to ensure proper clean up
//! using `pop_oldest` call.

use std::num::Zero;
use std::rand;

use num;
use num::bigint::RandBigInt;

use super::GenericNodeTable;
use super::Node;

// TODO(divius): make public?
static BUCKET_SIZE: uint = 64;
static HASH_SIZE: uint = 160;

/// Kademlia node table.
///
/// Keeps nodes in a number of k-buckets (equal to bit size of ID in a system,
/// usually 160), where N-th k-bucket contains nodes with distance
/// from 2^N to 2^(N+1) from our node.
#[unstable]
pub struct KNodeTable {
    this_id: num::BigUint,
    hash_size: uint,
    // TODO(divius): convert to more appropriate data structure
    buckets: Vec<KBucket>,
}

/// K-bucket - structure for keeping last nodes in Kademlia.
struct KBucket {
    data: Vec<Node>,
    size: uint,
}

#[unstable]
impl KNodeTable {
    /// Create a new node table.
    ///
    /// `this_id` -- ID of the current node (used to calculate metrics).
    pub fn new(this_id: num::BigUint) -> KNodeTable {
        KNodeTable::with_details(this_id, BUCKET_SIZE, HASH_SIZE)
    }

    // TODO(divius): make public?
    fn with_details(this_id: num::BigUint, bucket_size: uint,
                    hash_size: uint) -> KNodeTable {
        KNodeTable {
            this_id: this_id,
            hash_size: hash_size,
            buckets: Vec::from_fn(hash_size,
                                  |_| KBucket::new(bucket_size)),
        }
    }

    #[inline]
    fn distance(id1: &num::BigUint, id2: &num::BigUint) -> num::BigUint {
        id1.bitxor(id2)
    }

    fn bucket_number(&self, id: &num::BigUint) -> uint {
        let diff = KNodeTable::distance(&self.this_id, id);
        debug_assert!(!diff.is_zero());
        let res = diff.bits() - 1;
        debug!("ID {} relative to own ID {} falls into bucket {}",
               id, self.this_id, res);
        res
    }
}

#[unstable]
impl GenericNodeTable for KNodeTable {
    fn random_id(&self) -> num::BigUint {
        let mut rng = rand::task_rng();
        rng.gen_biguint(self.hash_size)
    }

    fn update(&mut self, node: &Node) -> bool {
        assert!(node.id != self.this_id);
        let bucket = self.bucket_number(&node.id);
        self.buckets.get_mut(bucket).update(node)
    }

    fn find(&self, id: &num::BigUint, count: uint) -> Vec<Node> {
        debug_assert!(count > 0);
        assert!(*id != self.this_id)
        let bucket = self.bucket_number(id);
        self.buckets[bucket].find(id, count)
    }

    fn pop_oldest(&mut self) -> Vec<Node> {
        // For every full k-bucket, pop the last.
        // TODO(divius): TTL expiration?
        self.buckets.iter_mut()
            .filter(|b| { !b.data.is_empty() && b.size == b.data.len() })
            .map(|b| b.data.remove(0).unwrap())
            .collect()
    }
}

impl KBucket {
    pub fn new(k: uint) -> KBucket {
        assert!(k > 0)
        KBucket {
            data: Vec::new(),
            size: k
        }
    }

    pub fn update(&mut self, node: &Node) -> bool {
        if self.data.iter().any(|x| x.id == node.id) {
            self.update_position(node.clone());
            debug!("Promoted node {} to the top of kbucket", node);
            true
        }
        else if self.data.len() == self.size {
            debug!("Not adding new node {} to kbucket - no space left", node);
            false
        }
        else {
            self.data.push(node.clone());
            debug!("Added new node {} to kbucket", node);
            true
        }
    }

    pub fn find(&self, id: &num::BigUint, count: uint) -> Vec<Node> {
        let sort_fn = |a: &Node, b: &Node| {
            KNodeTable::distance(id, &a.id)
                .cmp(&KNodeTable::distance(id, &b.id))
        };
        let mut data_copy = self.data.clone();
        data_copy.sort_by(sort_fn);
        data_copy.slice(0, count).to_vec()
    }

    fn update_position(&mut self, node: Node) {
        // TODO(divius): 1. optimize, 2. make it less ugly
        let mut new_data = Vec::with_capacity(self.data.len());
        new_data.extend(self.data.iter()
                        .filter(|x| x.id != node.id)
                        .map(|x| x.clone()));
        new_data.push(node.clone());
        self.data = new_data;
    }
}

#[cfg(test)]
mod test {
    use num;

    use super::super::GenericNodeTable;
    use super::super::Node;

    use super::HASH_SIZE;
    use super::KBucket;
    use super::KNodeTable;

    use super::super::utils::test;

    fn prepare(count: uint) -> KBucket {
        KBucket {
            data: Vec::from_fn(count, |i| test::new_node(i)),
            size: 3,
        }
    }

    fn assert_node_list_eq(expected: &[&Node], actual: &Vec<Node>) {
        let act: Vec<num::BigUint> = actual.iter()
            .map(|x| x.id.clone()).collect();
        let exp: Vec<num::BigUint> = expected.iter()
            .map(|x| x.id.clone()).collect();
        assert_eq!(exp, act);
    }

    #[test]
    fn test_nodetable_new() {
        let n = KNodeTable::new(test::uint_to_id(42));
        assert_eq!(HASH_SIZE, n.buckets.len());
    }

    #[test]
    fn test_nodetable_bucket_number() {
        let n = KNodeTable::new(test::uint_to_id(42));
        let id = test::uint_to_id(41);
        // 42 xor 41 == 3
        assert_eq!(1, n.bucket_number(&id));
    }

    #[test]
    fn test_nodetable_pop_oldest() {
        let mut n = KNodeTable::with_details(
            test::uint_to_id(42), 2, HASH_SIZE);
        n.update(&test::new_node(41));
        n.update(&test::new_node(43));
        n.update(&test::new_node(40));
        assert_eq!(0, n.buckets[2].data.len());
        assert_eq!(2, n.buckets[1].data.len());
        assert_eq!(1, n.buckets[0].data.len());
        let nodes = n.pop_oldest();
        assert_eq!(1, nodes.len());
        assert_eq!(41, nodes[0].id.to_int().unwrap());
        assert_eq!(0, n.buckets[2].data.len());
        assert_eq!(1, n.buckets[1].data.len());
        assert_eq!(1, n.buckets[0].data.len());
        assert_eq!(40, n.buckets[1].data[0].id.to_int().unwrap());
    }

    #[test]
    fn test_nodetable_find() {
        let n = KNodeTable {
            buckets: vec![prepare(1), prepare(3), prepare(1)],
            this_id: test::uint_to_id(0),
            hash_size: HASH_SIZE,
        };
        // 0 xor 3 = 3, 1 xor 3 = 2, 2 xor 3 = 1
        let id = test::uint_to_id(3);
        assert_node_list_eq([&n.buckets[1].data[2]],
                            &n.find(&id, 1));
    }

    #[test]
    fn test_nodetable_update() {
        let mut n = KNodeTable::with_details(
            test::uint_to_id(42), 1, HASH_SIZE);
        let node = test::new_node(41);
        n.update(&node);
        assert_eq!(1, n.buckets[1].data.len());
        n.update(&node);
        assert_eq!(1, n.buckets[1].data.len());
    }

    #[test]
    fn test_nodetable_random_id() {
        let n = KNodeTable::with_details(
            test::uint_to_id(42), 1, HASH_SIZE);
        for _ in range(0u, 100u) {
            assert!(n.random_id().bits() <= HASH_SIZE);
        }
        assert!(n.random_id() != n.random_id());
    }

    #[test]
    fn test_kbucket_new() {
        let b = KBucket::new(3);
        assert_eq!(0, b.data.len());
        assert_eq!(3, b.size);
    }

    #[test]
    fn test_kbucket_update_unknown() {
        let mut b = prepare(1);
        let node = test::new_node(42);
        assert!(b.update(&node))
        assert_eq!(2, b.data.len());
        assert_eq!(node.id, b.data[1].id);
    }

    #[test]
    fn test_kbucket_update_known() {
        let mut b = prepare(2);
        let node = test::new_node(0);
        assert!(b.update(&node))
        assert_eq!(2, b.data.len());
        assert_eq!(node.id, b.data[1].id);
    }

    #[test]
    fn test_kbucket_update_conflict() {
        let mut b = prepare(3);  // 3 is size
        let node = test::new_node(42);
        assert!(!b.update(&node))
    }

    #[test]
    fn test_kbucket_find() {
        let b = prepare(3);
        // Nodes with ID's 0, 1, 2; assume our ID is also 2 (impossible IRL)
        let id = test::uint_to_id(2);
        // 0 xor 2 = 2, 1 xor 2 = 3, 2 xor 2 = 0
        assert_node_list_eq([&b.data[2]], &b.find(&id, 1));
        assert_node_list_eq([&b.data[2], &b.data[0]], &b.find(&id, 2));
    }
}
<commit_msg>Fix failure in KNodeTable<commit_after>// Copyright 2014 Dmitry "Divius" Tantsur <divius.inside@gmail.com>
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
//

//! DHT node table implementation based on Kademlia.
//!
//! See [original paper](http://pdos.csail.mit.edu/%7Epetar/papers/maymounkov-kademlia-lncs.pdf)
//! for details. The most essential difference is that when k-bucket is full,
//! no RPC call is done. It is up to upper-level code to ensure proper clean up
//! using `pop_oldest` call.

use std::cmp;
use std::num::Zero;
use std::rand;

use num;
use num::bigint::RandBigInt;

use super::GenericNodeTable;
use super::Node;

// TODO(divius): make public?
static BUCKET_SIZE: uint = 64;
static HASH_SIZE: uint = 160;

/// Kademlia node table.
///
/// Keeps nodes in a number of k-buckets (equal to bit size of ID in a system,
/// usually 160), where N-th k-bucket contains nodes with distance
/// from 2^N to 2^(N+1) from our node.
#[unstable]
pub struct KNodeTable {
    this_id: num::BigUint,
    hash_size: uint,
    // TODO(divius): convert to more appropriate data structure
    buckets: Vec<KBucket>,
}

/// K-bucket - structure for keeping last nodes in Kademlia.
struct KBucket {
    data: Vec<Node>,
    size: uint,
}

#[unstable]
impl KNodeTable {
    /// Create a new node table.
    ///
    /// `this_id` -- ID of the current node (used to calculate metrics).
    pub fn new(this_id: num::BigUint) -> KNodeTable {
        KNodeTable::with_details(this_id, BUCKET_SIZE, HASH_SIZE)
    }

    // TODO(divius): make public?
    fn with_details(this_id: num::BigUint, bucket_size: uint,
                    hash_size: uint) -> KNodeTable {
        KNodeTable {
            this_id: this_id,
            hash_size: hash_size,
            buckets: Vec::from_fn(hash_size,
                                  |_| KBucket::new(bucket_size)),
        }
    }

    #[inline]
    fn distance(id1: &num::BigUint, id2: &num::BigUint) -> num::BigUint {
        id1.bitxor(id2)
    }

    fn bucket_number(&self, id: &num::BigUint) -> uint {
        let diff = KNodeTable::distance(&self.this_id, id);
        debug_assert!(!diff.is_zero());
        let res = diff.bits() - 1;
        debug!("ID {} relative to own ID {} falls into bucket {}",
               id, self.this_id, res);
        res
    }
}

#[unstable]
impl GenericNodeTable for KNodeTable {
    fn random_id(&self) -> num::BigUint {
        let mut rng = rand::task_rng();
        rng.gen_biguint(self.hash_size)
    }

    fn update(&mut self, node: &Node) -> bool {
        assert!(node.id != self.this_id);
        let bucket = self.bucket_number(&node.id);
        self.buckets.get_mut(bucket).update(node)
    }

    fn find(&self, id: &num::BigUint, count: uint) -> Vec<Node> {
        debug_assert!(count > 0);
        assert!(*id != self.this_id)
        let bucket = self.bucket_number(id);
        self.buckets[bucket].find(id, count)
    }

    fn pop_oldest(&mut self) -> Vec<Node> {
        // For every full k-bucket, pop the last.
        // TODO(divius): TTL expiration?
        self.buckets.iter_mut()
            .filter(|b| { !b.data.is_empty() && b.size == b.data.len() })
            .map(|b| b.data.remove(0).unwrap())
            .collect()
    }
}

impl KBucket {
    pub fn new(k: uint) -> KBucket {
        assert!(k > 0)
        KBucket {
            data: Vec::new(),
            size: k
        }
    }

    pub fn update(&mut self, node: &Node) -> bool {
        if self.data.iter().any(|x| x.id == node.id) {
            self.update_position(node.clone());
            debug!("Promoted node {} to the top of kbucket", node);
            true
        }
        else if self.data.len() == self.size {
            debug!("Not adding new node {} to kbucket - no space left", node);
            false
        }
        else {
            self.data.push(node.clone());
            debug!("Added new node {} to kbucket", node);
            true
        }
    }

    pub fn find(&self, id: &num::BigUint, count: uint) -> Vec<Node> {
        let sort_fn = |a: &Node, b: &Node| {
            KNodeTable::distance(id, &a.id)
                .cmp(&KNodeTable::distance(id, &b.id))
        };
        let mut data_copy = self.data.clone();
        data_copy.sort_by(sort_fn);
        data_copy.slice(0, cmp::min(count, data_copy.len())).to_vec()
    }

    fn update_position(&mut self, node: Node) {
        // TODO(divius): 1. optimize, 2. make it less ugly
        let mut new_data = Vec::with_capacity(self.data.len());
        new_data.extend(self.data.iter()
                        .filter(|x| x.id != node.id)
                        .map(|x| x.clone()));
        new_data.push(node.clone());
        self.data = new_data;
    }
}

#[cfg(test)]
mod test {
    use num;

    use super::super::GenericNodeTable;
    use super::super::Node;

    use super::HASH_SIZE;
    use super::KBucket;
    use super::KNodeTable;

    use super::super::utils::test;

    fn prepare(count: uint) -> KBucket {
        KBucket {
            data: Vec::from_fn(count, |i| test::new_node(i)),
            size: 3,
        }
    }

    fn assert_node_list_eq(expected: &[&Node], actual: &Vec<Node>) {
        let act: Vec<num::BigUint> = actual.iter()
            .map(|x| x.id.clone()).collect();
        let exp: Vec<num::BigUint> = expected.iter()
            .map(|x| x.id.clone()).collect();
        assert_eq!(exp, act);
    }

    #[test]
    fn test_nodetable_new() {
        let n = KNodeTable::new(test::uint_to_id(42));
        assert_eq!(HASH_SIZE, n.buckets.len());
    }

    #[test]
    fn test_nodetable_bucket_number() {
        let n = KNodeTable::new(test::uint_to_id(42));
        let id = test::uint_to_id(41);
        // 42 xor 41 == 3
        assert_eq!(1, n.bucket_number(&id));
    }

    #[test]
    fn test_nodetable_pop_oldest() {
        let mut n = KNodeTable::with_details(
            test::uint_to_id(42), 2, HASH_SIZE);
        n.update(&test::new_node(41));
        n.update(&test::new_node(43));
        n.update(&test::new_node(40));
        assert_eq!(0, n.buckets[2].data.len());
        assert_eq!(2, n.buckets[1].data.len());
        assert_eq!(1, n.buckets[0].data.len());
        let nodes = n.pop_oldest();
        assert_eq!(1, nodes.len());
        assert_eq!(41, nodes[0].id.to_int().unwrap());
        assert_eq!(0, n.buckets[2].data.len());
        assert_eq!(1, n.buckets[1].data.len());
        assert_eq!(1, n.buckets[0].data.len());
        assert_eq!(40, n.buckets[1].data[0].id.to_int().unwrap());
    }

    #[test]
    fn test_nodetable_find() {
        let n = KNodeTable {
            buckets: vec![prepare(1), prepare(3), prepare(1)],
            this_id: test::uint_to_id(0),
            hash_size: HASH_SIZE,
        };
        // 0 xor 3 = 3, 1 xor 3 = 2, 2 xor 3 = 1
        let id = test::uint_to_id(3);
        assert_node_list_eq([&n.buckets[1].data[2]],
                            &n.find(&id, 1));
    }

    #[test]
    fn test_nodetable_update() {
        let mut n = KNodeTable::with_details(
            test::uint_to_id(42), 1, HASH_SIZE);
        let node = test::new_node(41);
        n.update(&node);
        assert_eq!(1, n.buckets[1].data.len());
        n.update(&node);
        assert_eq!(1, n.buckets[1].data.len());
    }

    #[test]
    fn test_nodetable_random_id() {
        let n = KNodeTable::with_details(
            test::uint_to_id(42), 1, HASH_SIZE);
        for _ in range(0u, 100u) {
            assert!(n.random_id().bits() <= HASH_SIZE);
        }
        assert!(n.random_id() != n.random_id());
    }

    #[test]
    fn test_kbucket_new() {
        let b = KBucket::new(3);
        assert_eq!(0, b.data.len());
        assert_eq!(3, b.size);
    }

    #[test]
    fn test_kbucket_update_unknown() {
        let mut b = prepare(1);
        let node = test::new_node(42);
        assert!(b.update(&node))
        assert_eq!(2, b.data.len());
        assert_eq!(node.id, b.data[1].id);
    }

    #[test]
    fn test_kbucket_update_known() {
        let mut b = prepare(2);
        let node = test::new_node(0);
        assert!(b.update(&node))
        assert_eq!(2, b.data.len());
        assert_eq!(node.id, b.data[1].id);
    }

    #[test]
    fn test_kbucket_update_conflict() {
        let mut b = prepare(3);  // 3 is size
        let node = test::new_node(42);
        assert!(!b.update(&node))
    }

    #[test]
    fn test_kbucket_find() {
        let b = prepare(3);
        // Nodes with ID's 0, 1, 2; assume our ID is also 2 (impossible IRL)
        let id = test::uint_to_id(2);
        // 0 xor 2 = 2, 1 xor 2 = 3, 2 xor 2 = 0
        assert_node_list_eq([&b.data[2]], &b.find(&id, 1));
        assert_node_list_eq([&b.data[2], &b.data[0]], &b.find(&id, 2));
    }

    #[test]
    fn test_kbucket_find_too_much() {
        let b = prepare(3);
        // Nodes with ID's 0, 1, 2; assume our ID is also 2 (impossible IRL)
        let id = test::uint_to_id(2);
        // 0 xor 2 = 2, 1 xor 2 = 3, 2 xor 2 = 0
        assert_node_list_eq([&b.data[2], &b.data[0], &b.data[1]],
                            &b.find(&id, 100));
    }
}
<|endoftext|>


<commit_before>pub mod containers;
#[cfg(test)]
mod tests;

mod tree;
<commit_msg>Fix build<commit_after>pub mod containers;

mod tree;
<|endoftext|>


<commit_before>// Copyright (C) 2016 Élisabeth HENRY.
//
// This file is part of Crowbook.
//
// Crowbook is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation, either version 2.1 of the License, or
// (at your option) any later version.
//
// Caribon is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Lesser General Public License for more details.
//
// You should have received ba copy of the GNU Lesser General Public License
// along with Crowbook.  If not, see <http://www.gnu.org/licenses/>.

//! Provides utility functions for escaping text to HTML or LaTeX

use std::borrow::Cow;

const NB_CHAR:char = ' '; // non breaking space
const NB_CHAR_NARROW:char = '\u{202F}'; // narrow non breaking space
const NB_CHAR_EM:char = '\u{2002}'; // demi em space

/// Escape non breaking spaces for HTML, so there is no problem for displaying them if the font or browser
/// doesn't know what to do with them
pub fn escape_nb_spaces<'a, S: Into<Cow<'a, str>>>(input: S) -> Cow<'a, str> {
    let input = input.into();
    if let Some(first) = input.chars().position(|c| match c {
        NB_CHAR | NB_CHAR_NARROW | NB_CHAR_EM => true,
        _ => false
    }) {
        let mut chars = input.chars().collect::<Vec<_>>();
        let rest = chars.split_off(first);
        let mut output = chars.into_iter().collect::<String>();
        for c in rest {
            match c {
                NB_CHAR_NARROW  => output.push_str(r#" "#),
                NB_CHAR_EM => output.push_str(r#" "#),
                NB_CHAR => output.push_str(r#" "#),
                _ => output.push(c),
            }
        }
        Cow::Owned(output)
    } else {
        input.into()
    }
}

/// Escape characters `<`, `>`, and `&`
///
/// # Examples
///
/// ```
/// use crowbook::escape::escape_html;
/// let s = escape_html("<foo> & <bar>");
/// assert_eq!(&s, "<foo> & <bar>");
/// ```
pub fn escape_html<'a, S: Into<Cow<'a, str>>>(input: S) -> Cow<'a, str> {
    let input = input.into();
    let first = input.chars().position(|c| match c {
        '<'| '>'| '&' => true,
        _ => false
    });
    if let Some(first) = first {
        let mut chars = input.chars().collect::<Vec<_>>();
        let rest = chars.split_off(first);
        let mut output = chars.into_iter().collect::<String>();
        for c in rest {
            match c {
                '<' => output.push_str("<"),
                '>' => output.push_str(">"),
                '&' => output.push_str("&"),
                _ => output.push(c),
            }
        }

        Cow::Owned(output)
    } else {
        input.into()
    }
}

/// Escape quotes
///
/// Replace `"` by `'`
pub fn escape_quotes<'a, S: Into<Cow<'a, str>>>(input: S) -> Cow<'a, str> {
    let input = input.into();
    if input.contains('"') {
        let mut output = String::with_capacity(input.len());
        for c in input.chars() {
            match c {
                '"' => output.push('\''),
                _ => output.push(c)
            }
        }
        Cow::Owned(output)
    } else {
        input
    }
}

/// Escape characters for LaTeX
///
/// # Examples
///
/// ```
/// use crowbook::escape::escape_tex;
/// let s = escape_tex("command --foo # calls command with option foo");
/// assert_eq!(&s, r"command -{}-foo \# calls command with option foo");
/// ```
pub fn escape_tex<'a, S: Into<Cow<'a, str>>>(input: S) -> Cow<'a, str> {
    let input = input.into();
    if input.contains(|c| match c {
        '&'|'%'|'$'|'#'|'_'|'{'|'}'|'~'|'^'|'\\'|'-' => true,
        _ => false
    }) {
        let mut output = String::with_capacity(input.len());
        let mut chars:Vec<char> = input.chars().collect();
        chars.push(' '); // add a dummy char for call to .windows()
        // for &[c, next] in chars.windows(2) { // still experimental, uncomment when stable
        for win in chars.windows(2) {
            let c = win[0];
            let next = win[1];
            match c {
                '-' => {
                    if next == '-' {
                        output.push_str(r"-{}"); // if next char is also a -, to avoid tex ligatures
                    } else {
                        output.push(c);
                    }
                },
                '&' => output.push_str(r"\&"),
                '%' => output.push_str(r"\%"),
                '$' => output.push_str(r"\$"),
                '#' => output.push_str(r"\#"),
                '_' => output.push_str(r"\_"),
                '{' => output.push_str(r"\{"),
                '}' => output.push_str(r"\}"),
                '~' => output.push_str(r"\textasciitilde{}" ),
                '^' => output.push_str(r"\textasciicircum{}"),
                '\\' => output.push_str(r"\textbackslash{}"),
                _  => output.push(c)
            }
        }
        Cow::Owned(output)
    } else {
        input
    }
}
<commit_msg>Make escape_nb_spaces and escape_quotes hidden<commit_after>// Copyright (C) 2016 Élisabeth HENRY.
//
// This file is part of Crowbook.
//
// Crowbook is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation, either version 2.1 of the License, or
// (at your option) any later version.
//
// Caribon is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Lesser General Public License for more details.
//
// You should have received ba copy of the GNU Lesser General Public License
// along with Crowbook.  If not, see <http://www.gnu.org/licenses/>.

//! Provides utility functions for escaping text to HTML or LaTeX

use std::borrow::Cow;

const NB_CHAR:char = ' '; // non breaking space
const NB_CHAR_NARROW:char = '\u{202F}'; // narrow non breaking space
const NB_CHAR_EM:char = '\u{2002}'; // demi em space

/// Escape non breaking spaces for HTML, so there is no problem for displaying them if the font or browser
/// doesn't know what to do with them
#[doc(hidden)]
pub fn escape_nb_spaces<'a, S: Into<Cow<'a, str>>>(input: S) -> Cow<'a, str> {
    let input = input.into();
    if let Some(first) = input.chars().position(|c| match c {
        NB_CHAR | NB_CHAR_NARROW | NB_CHAR_EM => true,
        _ => false
    }) {
        let mut chars = input.chars().collect::<Vec<_>>();
        let rest = chars.split_off(first);
        let mut output = chars.into_iter().collect::<String>();
        for c in rest {
            match c {
                NB_CHAR_NARROW  => output.push_str(r#" "#),
                NB_CHAR_EM => output.push_str(r#" "#),
                NB_CHAR => output.push_str(r#" "#),
                _ => output.push(c),
            }
        }
        Cow::Owned(output)
    } else {
        input.into()
    }
}

/// Escape characters `<`, `>`, and `&`
///
/// # Examples
///
/// ```
/// use crowbook::escape::escape_html;
/// let s = escape_html("<foo> & <bar>");
/// assert_eq!(&s, "<foo> & <bar>");
/// ```
pub fn escape_html<'a, S: Into<Cow<'a, str>>>(input: S) -> Cow<'a, str> {
    let input = input.into();
    let first = input.chars().position(|c| match c {
        '<'| '>'| '&' => true,
        _ => false
    });
    if let Some(first) = first {
        let mut chars = input.chars().collect::<Vec<_>>();
        let rest = chars.split_off(first);
        let mut output = chars.into_iter().collect::<String>();
        for c in rest {
            match c {
                '<' => output.push_str("<"),
                '>' => output.push_str(">"),
                '&' => output.push_str("&"),
                _ => output.push(c),
            }
        }

        Cow::Owned(output)
    } else {
        input.into()
    }
}

/// Escape quotes
///
/// Replace `"` by `'`
#[doc(hidden)]
pub fn escape_quotes<'a, S: Into<Cow<'a, str>>>(input: S) -> Cow<'a, str> {
    let input = input.into();
    if input.contains('"') {
        let mut output = String::with_capacity(input.len());
        for c in input.chars() {
            match c {
                '"' => output.push('\''),
                _ => output.push(c)
            }
        }
        Cow::Owned(output)
    } else {
        input
    }
}

/// Escape characters for LaTeX
///
/// # Examples
///
/// ```
/// use crowbook::escape::escape_tex;
/// let s = escape_tex("command --foo # calls command with option foo");
/// assert_eq!(&s, r"command -{}-foo \# calls command with option foo");
/// ```
pub fn escape_tex<'a, S: Into<Cow<'a, str>>>(input: S) -> Cow<'a, str> {
    let input = input.into();
    if input.contains(|c| match c {
        '&'|'%'|'$'|'#'|'_'|'{'|'}'|'~'|'^'|'\\'|'-' => true,
        _ => false
    }) {
        let mut output = String::with_capacity(input.len());
        let mut chars:Vec<char> = input.chars().collect();
        chars.push(' '); // add a dummy char for call to .windows()
        // for &[c, next] in chars.windows(2) { // still experimental, uncomment when stable
        for win in chars.windows(2) {
            let c = win[0];
            let next = win[1];
            match c {
                '-' => {
                    if next == '-' {
                        output.push_str(r"-{}"); // if next char is also a -, to avoid tex ligatures
                    } else {
                        output.push(c);
                    }
                },
                '&' => output.push_str(r"\&"),
                '%' => output.push_str(r"\%"),
                '$' => output.push_str(r"\$"),
                '#' => output.push_str(r"\#"),
                '_' => output.push_str(r"\_"),
                '{' => output.push_str(r"\{"),
                '}' => output.push_str(r"\}"),
                '~' => output.push_str(r"\textasciitilde{}" ),
                '^' => output.push_str(r"\textasciicircum{}"),
                '\\' => output.push_str(r"\textbackslash{}"),
                _  => output.push(c)
            }
        }
        Cow::Owned(output)
    } else {
        input
    }
}
<|endoftext|>