<commit_before>/*
    This file is a part of cargo-esr.

    Copyright (C) 2017 Mohammad AlSaleh <CE.Mohammad.AlSaleh at gmail.com>
    https://github.com/rust-alt/cargo-esr

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

use pipeliner::Pipeline;

use esr_from::{EsrFrom, DefEsrFrom};
use esr_util;
use esr_errors::*;

#[derive(Deserialize, Debug)]
struct PullRequestInfo {
    merged_at: Option<String>,
    number: usize,
}

#[derive(Deserialize, Debug)]
struct IssueInfo {
    closed_at: Option<String>,
    number: usize,
}

#[derive(Deserialize, Debug)]
struct ContributorInfo {
    contributions: usize,
}

// =================

type RepoPullRequests = Vec<PullRequestInfo>;

impl EsrFrom for RepoPullRequests {
    fn url_from_id(id: &str) -> String {
        let url = String::from("https://api.github.com/repos/:id/pulls?state=all&per_page=100");
        url.replace(":id", id)
    }
}

type RepoClosedIssues = Vec<IssueInfo>;

impl EsrFrom for RepoClosedIssues {
    fn url_from_id(id: &str) -> String {
        let url = String::from("https://api.github.com/repos/:id/issues?\
                               state=closed&per_page=1");
        url.replace(":id", id)
    }
}

type RepoContributors = Vec<ContributorInfo>;

impl EsrFrom for RepoContributors {
    fn url_from_id(id: &str) -> String {
        let url = String::from("https://api.github.com/repos/:id/contributors?per_page=100");
        url.replace(":id", id)
    }
}

#[derive(Deserialize, Debug)]
struct RepoGeneralInfo {
    subscribers_count: usize,
    created_at: String,
    // Same as created_at if the repo is empty
    pushed_at: String,
}

impl EsrFrom for RepoGeneralInfo {
    fn url_from_id(id: &str) -> String {
        let url = String::from("https://api.github.com/repos/:id");
        url.replace(":id", id)
    }
}

// =================

pub struct RepoInfo {
    general_info: RepoGeneralInfo,
    last_100_closed_issues: RepoClosedIssues,
    last_100_pull_requests: RepoPullRequests,
    top_100_contributors: RepoContributors,
}

impl RepoInfo {
    pub fn from_id_with_token(id: &str, token: &str) -> Result<Self> {
        let urls = vec![
            RepoGeneralInfo::url_from_id_and_token(id, token),
            RepoClosedIssues::url_from_id_and_token(id, token),
            RepoPullRequests::url_from_id_and_token(id, token),
            RepoContributors::url_from_id_and_token(id, token),
        ];

        // `.with_threads()` does not guarantee order. So, we use `.enumerate()` as an indexer
        let bytes_res: Result<Vec<_>> = urls
            .into_iter()
            .enumerate()
            .with_threads(4)
            .map(|(idx, url)| DefEsrFrom::bytes_from_url(&url).map(|bytes| (idx, bytes)))
            .collect();

        // Check for errors
        let bytes = bytes_res?;

        let &(_, ref bytes_general) = bytes.iter().find(|&&(idx,_)| idx == 0).ok_or("impossible")?;
        let &(_, ref bytes_closed_issues) = bytes.iter().find(|&&(idx,_)| idx == 1).ok_or("impossible")?;
        let &(_, ref bytes_pulls) = bytes.iter().find(|&&(idx,_)| idx == 2).ok_or("impossible")?;
        let &(_, ref bytes_contributors) = bytes.iter().find(|&&(idx,_)| idx == 3).ok_or("impossible")?;

        Ok(Self {
            general_info: RepoGeneralInfo::from_bytes(bytes_general)?,
            last_100_closed_issues: RepoClosedIssues::from_bytes(bytes_closed_issues)?,
            last_100_pull_requests: RepoPullRequests::from_bytes(bytes_pulls)?,
            top_100_contributors: RepoContributors::from_bytes(bytes_contributors)?,
        })
    }
}

pub struct RepoScoreInfo {
    subscribers: usize,
    contributors_up_to_100: usize,
    commits_from_upto_100_contributors: usize,
    secondary_contribution_pct: usize,
    merged_pull_requests_in_last_100: usize,
    months_since_last_pr_merged: f64,
    months_since_last_issue_closed: f64,
    push_span_in_months: f64,
    months_since_last_push: f64,
}

impl RepoScoreInfo {
    fn from_repo_info(repo_info: &RepoInfo) -> Result<Self> {
        let general_info = &repo_info.general_info;

        // Days active, months since last push
        let push_span_in_months =esr_util::span_in_months(&general_info.created_at,
                                                          &general_info.pushed_at)?;
        let months_since_last_push = esr_util::age_in_months(&general_info.pushed_at)?;

        // Get subscribers count
        let subscribers = general_info.subscribers_count;

        // Get contributor count, commit count using contributors info.
        if repo_info.top_100_contributors.is_empty() {
            Err("Empty contributors list")?;
        }

        let contributors_up_to_100 = repo_info.top_100_contributors.len();

        let commits_from_upto_100_contributors = repo_info.top_100_contributors
            .iter()
            .map(|contributor| contributor.contributions)
            .sum();

        // Secondary contribution pct
        let secondary_contribution_commits = (commits_from_upto_100_contributors -
                                              repo_info.top_100_contributors[0].contributions) as
                                             f64;
        let secondary_contribution_pct_f64 = secondary_contribution_commits * 100.0 /
                                             (commits_from_upto_100_contributors as f64);
        let secondary_contribution_pct = secondary_contribution_pct_f64.ceil() as usize;

        // merged pull requests in last 100, months since last merged
        let merged_pull_requests_in_last_100 = repo_info.last_100_pull_requests
            .iter()
            .filter(|pr| pr.merged_at.is_some())
            .count();

        let last_pr_merged_opt = repo_info.last_100_pull_requests
            .iter()
            .filter(|pr| pr.merged_at.is_some())
            .nth(0);

        let months_since_last_pr_merged = match last_pr_merged_opt {
            Some(pr) => esr_util::age_in_months(pr.merged_at.as_ref().ok_or("Impossible")?)?,
            None => esr_util::age_in_months(&general_info.created_at)?,
        };

        // months since last closed
        let last_issue_closed_opt = repo_info.last_100_closed_issues.get(0);

        let months_since_last_issue_closed = match last_issue_closed_opt {
            Some(issue) => esr_util::age_in_months(issue.closed_at.as_ref().ok_or("Impossible")?)?,
            None => esr_util::age_in_months(&general_info.created_at)?,
        };

        // Done
        Ok(Self {
            subscribers,
            contributors_up_to_100,
            commits_from_upto_100_contributors,
            secondary_contribution_pct,
            push_span_in_months,
            merged_pull_requests_in_last_100,
            months_since_last_pr_merged,
            months_since_last_issue_closed,
            months_since_last_push,
        })
    }

    fn mk_score(&self) -> (Vec<(String, String, String)>, f64, f64) {
        let mut positive_score = 0.0;
        let mut negative_score = 0.0;
        let mut table = Vec::with_capacity(9);

        // +ve
        score_add!(table, positive_score, self.subscribers, 0.5);
        score_add!(table, positive_score, self.contributors_up_to_100, 3.0);
        score_add!(table,
                   positive_score,
                   self.commits_from_upto_100_contributors,
                   0.1);

        // We only take secondary contribution into account if the repo has >= 100 commits
        if self.commits_from_upto_100_contributors >= 100 {
            score_add!(table, positive_score, self.secondary_contribution_pct, 5.0);
        }

        score_add!(table,
                   positive_score,
                   self.push_span_in_months.powf(0.5),
                   5.0);
        score_add!(table,
                   positive_score,
                   self.merged_pull_requests_in_last_100,
                   2.0);

        // -ve
        score_add!(table,
                   negative_score,
                   self.months_since_last_pr_merged.powf(1.5),
                   -1.0);
        score_add!(table,
                   negative_score,
                   self.months_since_last_issue_closed.powf(1.5),
                   -1.0);
        score_add!(table,
                   negative_score,
                   self.months_since_last_push.powf(1.5),
                   -4.0);

        (table, positive_score, negative_score)
    }
}

pub struct RepoInfoWithScore {
    repo_info: RepoInfo,
    repo_score_info: RepoScoreInfo,
    score_positive: f64,
    score_negative: f64,
    score_table: Vec<(String, String, String)>,
}

impl RepoInfoWithScore {
    pub fn from_id_with_token(id: &str, token: &str) -> Result<Self> {
        let repo_info = RepoInfo::from_id_with_token(id, token)?;
        let repo_score_info = RepoScoreInfo::from_repo_info(&repo_info)?;
        let (score_table, score_positive, score_negative) = repo_score_info.mk_score();

        Ok(Self {
            repo_info,
            repo_score_info,
            score_positive,
            score_negative,
            score_table,
        })
    }

    pub fn get_info(&self) -> &RepoInfo {
        &self.repo_info
    }

    pub fn get_score_info(&self) -> &RepoScoreInfo {
        &self.repo_score_info
    }

    pub fn get_score_tuple(&self) -> (f64, f64) {
        (self.score_positive, self.score_negative)
    }

    pub fn get_score_table(&self) -> &[(String, String, String)] {
        &self.score_table
    }
}
<commit_msg>Implement a from_id() for RepoInfo that always return an error<commit_after>/*
    This file is a part of cargo-esr.

    Copyright (C) 2017 Mohammad AlSaleh <CE.Mohammad.AlSaleh at gmail.com>
    https://github.com/rust-alt/cargo-esr

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

use pipeliner::Pipeline;

use esr_from::{EsrFrom, DefEsrFrom};
use esr_util;
use esr_errors::*;

#[derive(Deserialize, Debug)]
struct PullRequestInfo {
    merged_at: Option<String>,
    number: usize,
}

#[derive(Deserialize, Debug)]
struct IssueInfo {
    closed_at: Option<String>,
    number: usize,
}

#[derive(Deserialize, Debug)]
struct ContributorInfo {
    contributions: usize,
}

// =================

type RepoPullRequests = Vec<PullRequestInfo>;

impl EsrFrom for RepoPullRequests {
    fn url_from_id(id: &str) -> String {
        let url = String::from("https://api.github.com/repos/:id/pulls?state=all&per_page=100");
        url.replace(":id", id)
    }
}

type RepoClosedIssues = Vec<IssueInfo>;

impl EsrFrom for RepoClosedIssues {
    fn url_from_id(id: &str) -> String {
        let url = String::from("https://api.github.com/repos/:id/issues?\
                               state=closed&per_page=1");
        url.replace(":id", id)
    }
}

type RepoContributors = Vec<ContributorInfo>;

impl EsrFrom for RepoContributors {
    fn url_from_id(id: &str) -> String {
        let url = String::from("https://api.github.com/repos/:id/contributors?per_page=100");
        url.replace(":id", id)
    }
}

#[derive(Deserialize, Debug)]
struct RepoGeneralInfo {
    subscribers_count: usize,
    created_at: String,
    // Same as created_at if the repo is empty
    pushed_at: String,
}

impl EsrFrom for RepoGeneralInfo {
    fn url_from_id(id: &str) -> String {
        let url = String::from("https://api.github.com/repos/:id");
        url.replace(":id", id)
    }
}

// =================

pub struct RepoInfo {
    general_info: RepoGeneralInfo,
    last_100_closed_issues: RepoClosedIssues,
    last_100_pull_requests: RepoPullRequests,
    top_100_contributors: RepoContributors,
}

impl RepoInfo {
    pub fn from_id(_: &str) -> Result<Self> {
        Err("Unimplemented: use  from_id_with_token()")?
    }

    pub fn from_id_with_token(id: &str, token: &str) -> Result<Self> {
        let urls = vec![
            RepoGeneralInfo::url_from_id_and_token(id, token),
            RepoClosedIssues::url_from_id_and_token(id, token),
            RepoPullRequests::url_from_id_and_token(id, token),
            RepoContributors::url_from_id_and_token(id, token),
        ];

        // `.with_threads()` does not guarantee order. So, we use `.enumerate()` as an indexer
        let bytes_res: Result<Vec<_>> = urls
            .into_iter()
            .enumerate()
            .with_threads(4)
            .map(|(idx, url)| DefEsrFrom::bytes_from_url(&url).map(|bytes| (idx, bytes)))
            .collect();

        // Check for errors
        let bytes = bytes_res?;

        let &(_, ref bytes_general) = bytes.iter().find(|&&(idx,_)| idx == 0).ok_or("impossible")?;
        let &(_, ref bytes_closed_issues) = bytes.iter().find(|&&(idx,_)| idx == 1).ok_or("impossible")?;
        let &(_, ref bytes_pulls) = bytes.iter().find(|&&(idx,_)| idx == 2).ok_or("impossible")?;
        let &(_, ref bytes_contributors) = bytes.iter().find(|&&(idx,_)| idx == 3).ok_or("impossible")?;

        Ok(Self {
            general_info: RepoGeneralInfo::from_bytes(bytes_general)?,
            last_100_closed_issues: RepoClosedIssues::from_bytes(bytes_closed_issues)?,
            last_100_pull_requests: RepoPullRequests::from_bytes(bytes_pulls)?,
            top_100_contributors: RepoContributors::from_bytes(bytes_contributors)?,
        })
    }
}

pub struct RepoScoreInfo {
    subscribers: usize,
    contributors_up_to_100: usize,
    commits_from_upto_100_contributors: usize,
    secondary_contribution_pct: usize,
    merged_pull_requests_in_last_100: usize,
    months_since_last_pr_merged: f64,
    months_since_last_issue_closed: f64,
    push_span_in_months: f64,
    months_since_last_push: f64,
}

impl RepoScoreInfo {
    fn from_repo_info(repo_info: &RepoInfo) -> Result<Self> {
        let general_info = &repo_info.general_info;

        // Days active, months since last push
        let push_span_in_months =esr_util::span_in_months(&general_info.created_at,
                                                          &general_info.pushed_at)?;
        let months_since_last_push = esr_util::age_in_months(&general_info.pushed_at)?;

        // Get subscribers count
        let subscribers = general_info.subscribers_count;

        // Get contributor count, commit count using contributors info.
        if repo_info.top_100_contributors.is_empty() {
            Err("Empty contributors list")?;
        }

        let contributors_up_to_100 = repo_info.top_100_contributors.len();

        let commits_from_upto_100_contributors = repo_info.top_100_contributors
            .iter()
            .map(|contributor| contributor.contributions)
            .sum();

        // Secondary contribution pct
        let secondary_contribution_commits = (commits_from_upto_100_contributors -
                                              repo_info.top_100_contributors[0].contributions) as
                                             f64;
        let secondary_contribution_pct_f64 = secondary_contribution_commits * 100.0 /
                                             (commits_from_upto_100_contributors as f64);
        let secondary_contribution_pct = secondary_contribution_pct_f64.ceil() as usize;

        // merged pull requests in last 100, months since last merged
        let merged_pull_requests_in_last_100 = repo_info.last_100_pull_requests
            .iter()
            .filter(|pr| pr.merged_at.is_some())
            .count();

        let last_pr_merged_opt = repo_info.last_100_pull_requests
            .iter()
            .filter(|pr| pr.merged_at.is_some())
            .nth(0);

        let months_since_last_pr_merged = match last_pr_merged_opt {
            Some(pr) => esr_util::age_in_months(pr.merged_at.as_ref().ok_or("Impossible")?)?,
            None => esr_util::age_in_months(&general_info.created_at)?,
        };

        // months since last closed
        let last_issue_closed_opt = repo_info.last_100_closed_issues.get(0);

        let months_since_last_issue_closed = match last_issue_closed_opt {
            Some(issue) => esr_util::age_in_months(issue.closed_at.as_ref().ok_or("Impossible")?)?,
            None => esr_util::age_in_months(&general_info.created_at)?,
        };

        // Done
        Ok(Self {
            subscribers,
            contributors_up_to_100,
            commits_from_upto_100_contributors,
            secondary_contribution_pct,
            push_span_in_months,
            merged_pull_requests_in_last_100,
            months_since_last_pr_merged,
            months_since_last_issue_closed,
            months_since_last_push,
        })
    }

    fn mk_score(&self) -> (Vec<(String, String, String)>, f64, f64) {
        let mut positive_score = 0.0;
        let mut negative_score = 0.0;
        let mut table = Vec::with_capacity(9);

        // +ve
        score_add!(table, positive_score, self.subscribers, 0.5);
        score_add!(table, positive_score, self.contributors_up_to_100, 3.0);
        score_add!(table,
                   positive_score,
                   self.commits_from_upto_100_contributors,
                   0.1);

        // We only take secondary contribution into account if the repo has >= 100 commits
        if self.commits_from_upto_100_contributors >= 100 {
            score_add!(table, positive_score, self.secondary_contribution_pct, 5.0);
        }

        score_add!(table,
                   positive_score,
                   self.push_span_in_months.powf(0.5),
                   5.0);
        score_add!(table,
                   positive_score,
                   self.merged_pull_requests_in_last_100,
                   2.0);

        // -ve
        score_add!(table,
                   negative_score,
                   self.months_since_last_pr_merged.powf(1.5),
                   -1.0);
        score_add!(table,
                   negative_score,
                   self.months_since_last_issue_closed.powf(1.5),
                   -1.0);
        score_add!(table,
                   negative_score,
                   self.months_since_last_push.powf(1.5),
                   -4.0);

        (table, positive_score, negative_score)
    }
}

pub struct RepoInfoWithScore {
    repo_info: RepoInfo,
    repo_score_info: RepoScoreInfo,
    score_positive: f64,
    score_negative: f64,
    score_table: Vec<(String, String, String)>,
}

impl RepoInfoWithScore {
    pub fn from_id_with_token(id: &str, token: &str) -> Result<Self> {
        let repo_info = RepoInfo::from_id_with_token(id, token)?;
        let repo_score_info = RepoScoreInfo::from_repo_info(&repo_info)?;
        let (score_table, score_positive, score_negative) = repo_score_info.mk_score();

        Ok(Self {
            repo_info,
            repo_score_info,
            score_positive,
            score_negative,
            score_table,
        })
    }

    pub fn get_info(&self) -> &RepoInfo {
        &self.repo_info
    }

    pub fn get_score_info(&self) -> &RepoScoreInfo {
        &self.repo_score_info
    }

    pub fn get_score_tuple(&self) -> (f64, f64) {
        (self.score_positive, self.score_negative)
    }

    pub fn get_score_table(&self) -> &[(String, String, String)] {
        &self.score_table
    }
}
<|endoftext|>


<commit_before>#[crate_id = "euler"];
#[crate_type = "bin"];

#[feature(managed_boxes)];

extern mod extra;
extern mod common;

use std::{io, os};
use std::iter::Range;
use extra::{time, term};
use extra::term::Terminal;
use problem::Problem;

mod problem;

static NSEC_PER_SEC: u64 = 1000000000;
fn nanosec_to_str(nsec: u64) -> ~str {
    return format!("{}.{:09}",
         (nsec / NSEC_PER_SEC) as uint,
         (nsec % NSEC_PER_SEC) as uint);
}

fn bench<T>(f: proc() -> T) -> (u64, T) {
    let start_time = time::precise_time_ns();
    let result     = f();
    let end_time   = time::precise_time_ns();
    return (end_time - start_time, result);
}

fn color_print(color: term::color::Color, s: &str) {
    match Terminal::new(io::stdout()) {
        Ok(ref mut term) => {
            term.fg(color);
            term.write(s.as_bytes());
            term.reset();
        }
        Err(_) => { io::stdout().write(s.as_bytes()) }
    }
}

fn print_result(name: &str, time: u64, comp_answer: &str, time_color: bool, correct: bool) {
    let out = &mut io::stdout();
    write!(out, "[");
    if correct {
        color_print(term::color::GREEN, "OK");
    } else {
        color_print(term::color::RED, "NG");
    }
    write!(out, "] {:5}", name);
    if (time_color && time > 10 * NSEC_PER_SEC) {
        color_print(term::color::RED, format!(" {:13}", nanosec_to_str(time)));
    } else if time_color && time > NSEC_PER_SEC {
        color_print(term::color::YELLOW, format!(" {:13}", nanosec_to_str(time)));
    } else {
        write!(out, " {:13}", nanosec_to_str(time));
    }
    writeln!(out, " {:20}", comp_answer);
}

struct ArgIterator<'a> {
    args: &'a [~str],
    idx: uint,
    cur_range: Range<uint>,
}

impl<'a> ArgIterator<'a> {
    pub fn new<'a>(args: &'a [~str]) -> ArgIterator<'a> {
        let mut it = ArgIterator { args: args, idx: 0, cur_range: range(0u, 0) };
        it.update_range();
        return it;
    }

    fn update_range(&mut self) {
        self.cur_range = range(0u, 0);
        if self.idx >= self.args.len() { return; }

        if !self.args[self.idx].contains_char('-') {
            let n = from_str::<uint>(self.args[self.idx]);
            for &n in n.iter() {
                self.cur_range = range(n, n + 1);
            }
            return;
        }

        let mut ns = ~[];
        for ss in self.args[self.idx].split('-') {
            match from_str::<uint>(ss) {
                Some(n) => { ns.push(n); }
                None    => { return; }
            }
        }
        if ns.len() > 2 { return; }
        self.cur_range = range(ns[0], ns[1] + 1);
    }
}

impl<'a> Iterator<uint> for ArgIterator<'a> {
    fn next(&mut self) -> Option<uint> {
        loop {
            if self.idx >= self.args.len() { return None; }
            match self.cur_range.next() {
                Some(n) => { return Some(n); }
                None    => {
                    self.idx += 1;
                    self.update_range();
                }
            }
        }
    }
}

fn solve_all<T: Iterator<&'static Problem<'static>>>(mut it: T) {
    let mut total_time  = 0;
    let mut solve_cnt   = 0;
    let mut all_correct = true;

    for p in it {
        let (time, answer) = do bench { (p.solve)() };
        let correct = p.answer == answer;
        print_result(p.id.to_str(), time, answer, true, correct);

        total_time += time;
        solve_cnt  += 1;
        all_correct &= correct;
    }

    if solve_cnt > 1 {
        print_result("AVG", total_time / solve_cnt, "", true, all_correct);
        print_result("TOTAL", total_time, "", false, all_correct);
    }
}

fn main() {
    let args = os::args();
    let args = args.tail();

    if args.is_empty() {
        solve_all(range(0, problem::PROBLEMS.len())
                  .map(|i| problem::PROBLEMS[i]))
    } else {
        solve_all(ArgIterator::new(args)
                  .filter_map(|n| problem::PROBLEMS.bsearch(|&p| p.id.cmp(&n)))
                  .map(|i| problem::PROBLEMS[i]));
    };
}
<commit_msg>すべての解凍が OK でない場合に、終了コードを 1 にするよう変更<commit_after>#[crate_id = "euler"];
#[crate_type = "bin"];

#[feature(managed_boxes)];

extern mod extra;
extern mod common;

use std::{io, os};
use std::iter::Range;
use extra::{time, term};
use extra::term::Terminal;
use problem::Problem;

mod problem;

static NSEC_PER_SEC: u64 = 1000000000;
fn nanosec_to_str(nsec: u64) -> ~str {
    return format!("{}.{:09}",
         (nsec / NSEC_PER_SEC) as uint,
         (nsec % NSEC_PER_SEC) as uint);
}

fn bench<T>(f: proc() -> T) -> (u64, T) {
    let start_time = time::precise_time_ns();
    let result     = f();
    let end_time   = time::precise_time_ns();
    return (end_time - start_time, result);
}

fn color_print(color: term::color::Color, s: &str) {
    match Terminal::new(io::stdout()) {
        Ok(ref mut term) => {
            term.fg(color);
            term.write(s.as_bytes());
            term.reset();
        }
        Err(_) => { io::stdout().write(s.as_bytes()) }
    }
}

fn print_result(name: &str, time: u64, comp_answer: &str, time_color: bool, correct: bool) {
    let out = &mut io::stdout();
    write!(out, "[");
    if correct {
        color_print(term::color::GREEN, "OK");
    } else {
        color_print(term::color::RED, "NG");
    }
    write!(out, "] {:5}", name);
    if (time_color && time > 10 * NSEC_PER_SEC) {
        color_print(term::color::RED, format!(" {:13}", nanosec_to_str(time)));
    } else if time_color && time > NSEC_PER_SEC {
        color_print(term::color::YELLOW, format!(" {:13}", nanosec_to_str(time)));
    } else {
        write!(out, " {:13}", nanosec_to_str(time));
    }
    writeln!(out, " {:20}", comp_answer);
}

struct ArgIterator<'a> {
    args: &'a [~str],
    idx: uint,
    cur_range: Range<uint>,
}

impl<'a> ArgIterator<'a> {
    pub fn new<'a>(args: &'a [~str]) -> ArgIterator<'a> {
        let mut it = ArgIterator { args: args, idx: 0, cur_range: range(0u, 0) };
        it.update_range();
        return it;
    }

    fn update_range(&mut self) {
        self.cur_range = range(0u, 0);
        if self.idx >= self.args.len() { return; }

        if !self.args[self.idx].contains_char('-') {
            let n = from_str::<uint>(self.args[self.idx]);
            for &n in n.iter() {
                self.cur_range = range(n, n + 1);
            }
            return;
        }

        let mut ns = ~[];
        for ss in self.args[self.idx].split('-') {
            match from_str::<uint>(ss) {
                Some(n) => { ns.push(n); }
                None    => { return; }
            }
        }
        if ns.len() > 2 { return; }
        self.cur_range = range(ns[0], ns[1] + 1);
    }
}

impl<'a> Iterator<uint> for ArgIterator<'a> {
    fn next(&mut self) -> Option<uint> {
        loop {
            if self.idx >= self.args.len() { return None; }
            match self.cur_range.next() {
                Some(n) => { return Some(n); }
                None    => {
                    self.idx += 1;
                    self.update_range();
                }
            }
        }
    }
}

fn solve_all<T: Iterator<&'static Problem<'static>>>(mut it: T) {
    let mut total_time  = 0;
    let mut solve_cnt   = 0;
    let mut all_correct = true;

    for p in it {
        let (time, answer) = do bench { (p.solve)() };
        let correct = p.answer == answer;
        print_result(p.id.to_str(), time, answer, true, correct);

        total_time += time;
        solve_cnt  += 1;
        all_correct &= correct;
    }

    if solve_cnt > 1 {
        print_result("AVG", total_time / solve_cnt, "", true, all_correct);
        print_result("TOTAL", total_time, "", false, all_correct);
    }

    if !all_correct {
        os::set_exit_status(1);
    }
}

fn main() {
    let args = os::args();
    let args = args.tail();

    if args.is_empty() {
        solve_all(range(0, problem::PROBLEMS.len())
                  .map(|i| problem::PROBLEMS[i]))
    } else {
        solve_all(ArgIterator::new(args)
                  .filter_map(|n| problem::PROBLEMS.bsearch(|&p| p.id.cmp(&n)))
                  .map(|i| problem::PROBLEMS[i]));
    };
}
<|endoftext|>


<commit_before>use std::sync::Arc;
use rand::{Rng, XorShiftRng, SeedableRng};
use quickcheck;
use quickcheck::TestResult;
use zobrist::Zobrist;
use player::Player;
use field;
use field::{Pos, Field};
use construct_field::construct_field;

#[test]
fn simple_surround() {
  let field = construct_field(
    "
    .a.
    cBa
    .a.
    "
  );
  assert_eq!(field.captured_count(Player::Red), 1);
  assert_eq!(field.captured_count(Player::Black), 0);
  assert!(!field.cell(field.to_pos(1, 1)).is_putting_allowed());
  assert!(!field.cell(field.to_pos(0, 1)).is_putting_allowed());
  assert!(!field.cell(field.to_pos(1, 0)).is_putting_allowed());
  assert!(!field.cell(field.to_pos(1, 2)).is_putting_allowed());
  assert!(!field.cell(field.to_pos(2, 1)).is_putting_allowed());
}

#[test]
fn surround_empty_territory() {
  let field = construct_field(
    "
    .a.
    a.a
    .a.
    "
  );
  assert_eq!(field.captured_count(Player::Red), 0);
  assert_eq!(field.captured_count(Player::Black), 0);
  assert!(field.cell(field.to_pos(1, 1)).is_putting_allowed());
  assert!(field.cell(field.to_pos(1, 1)).is_empty_base());
  assert_eq!(field.cell(field.to_pos(1, 1)).get_empty_base_player(), Some(Player::Red));
  assert!(!field.cell(field.to_pos(0, 1)).is_putting_allowed());
  assert!(!field.cell(field.to_pos(1, 0)).is_putting_allowed());
  assert!(!field.cell(field.to_pos(1, 2)).is_putting_allowed());
  assert!(!field.cell(field.to_pos(2, 1)).is_putting_allowed());
}

#[test]
fn move_priority() {
  let field = construct_field(
    "
    .aB.
    aCaB
    .aB.
    "
  );
  assert_eq!(field.captured_count(Player::Red), 0);
  assert_eq!(field.captured_count(Player::Black), 1);
}

#[test]
fn move_priority_big() {
  let field = construct_field(
    "
    .B..
    BaB.
    aCaB
    .aB.
    "
  );
  assert_eq!(field.captured_count(Player::Red), 0);
  assert_eq!(field.captured_count(Player::Black), 2);
}

#[test]
fn onion_surroundings() {
  let field = construct_field(
    "
    ...c...
    ..cBc..
    .cBaBc.
    ..cBc..
    ...c...
    "
  );
  assert_eq!(field.captured_count(Player::Red), 4);
  assert_eq!(field.captured_count(Player::Black), 0);
}

#[test]
fn apply_control_surrounding_in_same_turn() {
  let field = construct_field(
    "
    .a.
    aBa
    .a.
    "
  );
  assert_eq!(field.captured_count(Player::Red), 1);
  assert_eq!(field.captured_count(Player::Black), 0);
}

#[test]
fn double_surround() {
  let field = construct_field(
    "
    .a.a.
    aAbAa
    .a.a.
    "
  );
  assert_eq!(field.captured_count(Player::Red), 2);
  assert_eq!(field.captured_count(Player::Black), 0);
}

#[test]
fn double_surround_with_empty_part() {
  let field = construct_field(
    "
    .b.b..
    b.zAb.
    .b.b..
    "
  );
  assert_eq!(field.captured_count(Player::Red), 1);
  assert_eq!(field.captured_count(Player::Black), 0);
  assert!(field.cell(field.to_pos(1, 1)).is_putting_allowed());
  assert!(!field.cell(field.to_pos(3, 1)).is_putting_allowed());
}

#[test]
fn should_not_leave_empty_inside() {
  let field = construct_field(
    "
    .aaaa..
    a....a.
    a.b...a
    .z.bC.a
    a.b...a
    a....a.
    .aaaa..
    "
  );
  assert_eq!(field.captured_count(Player::Red), 1);
  assert_eq!(field.captured_count(Player::Black), 0);
  assert!(!field.cell(field.to_pos(2, 3)).is_putting_allowed());
  assert!(!field.cell(field.to_pos(2, 4)).is_putting_allowed());
  assert!(!field.cell(field.to_pos(2, 2)).is_putting_allowed());
  assert!(!field.cell(field.to_pos(1, 3)).is_putting_allowed());
  assert!(!field.cell(field.to_pos(3, 3)).is_putting_allowed());
  assert!(!field.cell(field.to_pos(1, 1)).is_putting_allowed());
}

#[test]
fn a_hole_inside_a_surrounding() {
  let field = construct_field(
    "
    ....c....
    ...c.c...
    ..c...c..
    .c..a..c.
    c..a.a..c
    .c..a..c.
    ..c...c..
    ...cBc...
    ....d....
    "
  );
  assert_eq!(field.captured_count(Player::Red), 1);
  assert_eq!(field.captured_count(Player::Black), 0);
  assert!(!field.cell(field.to_pos(4, 4)).is_putting_allowed());
  assert!(!field.cell(field.to_pos(4, 1)).is_putting_allowed());
}

#[test]
fn a_hole_inside_a_surrounding_after_control_surrounding() {
  let field = construct_field(
    "
    ....b....
    ...b.b...
    ..b...b..
    .b..a..b.
    b..a.a..b
    .b..a..b.
    ..b...b..
    ...bCb...
    ....b....
    "
  );
  assert_eq!(field.captured_count(Player::Red), 1);
  assert_eq!(field.captured_count(Player::Black), 0);
  assert!(!field.cell(field.to_pos(4, 4)).is_putting_allowed());
  assert!(!field.cell(field.to_pos(4, 1)).is_putting_allowed());
}

#[test]
fn surrounding_does_not_expand() {
  let field = construct_field(
    "
    ....a....
    ...a.a...
    ..a.a.a..
    .a.a.a.a.
    a.a.aBa.a
    .a.a.a.a.
    ..a.a.a..
    ...a.a...
    ....a....
    "
  );
  assert_eq!(field.captured_count(Player::Red), 1);
  assert_eq!(field.captured_count(Player::Black), 0);
  assert!(field.cell(field.to_pos(6, 3)).is_putting_allowed());
  assert!(field.cell(field.to_pos(4, 3)).is_putting_allowed());
  assert!(field.cell(field.to_pos(4, 5)).is_putting_allowed());
  assert!(field.cell(field.to_pos(6, 5)).is_putting_allowed());
  assert!(!field.cell(field.to_pos(5, 4)).is_putting_allowed());
}

#[test]
fn two_surroundings_with_common_border() {
  let field = construct_field(
    "
    .a..
    aAa.
    .bAa
    ..a.
    "
  );
  assert_eq!(field.captured_count(Player::Red), 2);
  assert_eq!(field.captured_count(Player::Black), 0);
}

#[test]
fn three_surroundings_with_common_borders() {
  let field = construct_field(
    "
    ..a..
    .aAa.
    ..bAa
    .aAa.
    ..a..
    "
  );
  assert_eq!(field.captured_count(Player::Red), 3);
  assert_eq!(field.captured_count(Player::Black), 0);
}

#[test]
fn undo_check() {
  fn prop(width_seed: u32, height_seed: u32, seed: u64) -> TestResult {
    let width = width_seed % 30;
    let height = height_seed % 30;
    if width < 3 || height < 3 {
      return TestResult::discard();
    }
    let seed_array = [3, seed as u32, 7, (seed >> 32) as u32];
    let mut rng = XorShiftRng::from_seed(seed_array);
    let mut moves = (field::min_pos(width) .. field::max_pos(width, height) + 1).collect::<Vec<Pos>>();
    rng.shuffle(&mut moves);
    let zobrist = Arc::new(Zobrist::new(field::length(width, height) * 2, &mut rng));
    let mut field = Field::new(width, height, zobrist);
    let mut player = Player::Red;
    for pos in moves {
      if field.is_putting_allowed(pos) {
        player = player.next();
        let field_before = field.clone();
        field.put_point(pos, player);
        field.undo();
        if field_before != field {
          return TestResult::failed();
        }
        field.put_point(pos, player);
      }
    }
    TestResult::passed()
  }
  quickcheck::quickcheck(prop as fn(u32, u32, u64) -> TestResult);
}
<commit_msg>Refactor quickcheck usage.<commit_after>use std::sync::Arc;
use quickcheck;
use quickcheck::{Arbitrary, Gen, TestResult};
use zobrist::Zobrist;
use player::Player;
use field;
use field::{Pos, Field};
use construct_field::construct_field;

#[test]
fn simple_surround() {
  let field = construct_field(
    "
    .a.
    cBa
    .a.
    "
  );
  assert_eq!(field.captured_count(Player::Red), 1);
  assert_eq!(field.captured_count(Player::Black), 0);
  assert!(!field.cell(field.to_pos(1, 1)).is_putting_allowed());
  assert!(!field.cell(field.to_pos(0, 1)).is_putting_allowed());
  assert!(!field.cell(field.to_pos(1, 0)).is_putting_allowed());
  assert!(!field.cell(field.to_pos(1, 2)).is_putting_allowed());
  assert!(!field.cell(field.to_pos(2, 1)).is_putting_allowed());
}

#[test]
fn surround_empty_territory() {
  let field = construct_field(
    "
    .a.
    a.a
    .a.
    "
  );
  assert_eq!(field.captured_count(Player::Red), 0);
  assert_eq!(field.captured_count(Player::Black), 0);
  assert!(field.cell(field.to_pos(1, 1)).is_putting_allowed());
  assert!(field.cell(field.to_pos(1, 1)).is_empty_base());
  assert_eq!(field.cell(field.to_pos(1, 1)).get_empty_base_player(), Some(Player::Red));
  assert!(!field.cell(field.to_pos(0, 1)).is_putting_allowed());
  assert!(!field.cell(field.to_pos(1, 0)).is_putting_allowed());
  assert!(!field.cell(field.to_pos(1, 2)).is_putting_allowed());
  assert!(!field.cell(field.to_pos(2, 1)).is_putting_allowed());
}

#[test]
fn move_priority() {
  let field = construct_field(
    "
    .aB.
    aCaB
    .aB.
    "
  );
  assert_eq!(field.captured_count(Player::Red), 0);
  assert_eq!(field.captured_count(Player::Black), 1);
}

#[test]
fn move_priority_big() {
  let field = construct_field(
    "
    .B..
    BaB.
    aCaB
    .aB.
    "
  );
  assert_eq!(field.captured_count(Player::Red), 0);
  assert_eq!(field.captured_count(Player::Black), 2);
}

#[test]
fn onion_surroundings() {
  let field = construct_field(
    "
    ...c...
    ..cBc..
    .cBaBc.
    ..cBc..
    ...c...
    "
  );
  assert_eq!(field.captured_count(Player::Red), 4);
  assert_eq!(field.captured_count(Player::Black), 0);
}

#[test]
fn apply_control_surrounding_in_same_turn() {
  let field = construct_field(
    "
    .a.
    aBa
    .a.
    "
  );
  assert_eq!(field.captured_count(Player::Red), 1);
  assert_eq!(field.captured_count(Player::Black), 0);
}

#[test]
fn double_surround() {
  let field = construct_field(
    "
    .a.a.
    aAbAa
    .a.a.
    "
  );
  assert_eq!(field.captured_count(Player::Red), 2);
  assert_eq!(field.captured_count(Player::Black), 0);
}

#[test]
fn double_surround_with_empty_part() {
  let field = construct_field(
    "
    .b.b..
    b.zAb.
    .b.b..
    "
  );
  assert_eq!(field.captured_count(Player::Red), 1);
  assert_eq!(field.captured_count(Player::Black), 0);
  assert!(field.cell(field.to_pos(1, 1)).is_putting_allowed());
  assert!(!field.cell(field.to_pos(3, 1)).is_putting_allowed());
}

#[test]
fn should_not_leave_empty_inside() {
  let field = construct_field(
    "
    .aaaa..
    a....a.
    a.b...a
    .z.bC.a
    a.b...a
    a....a.
    .aaaa..
    "
  );
  assert_eq!(field.captured_count(Player::Red), 1);
  assert_eq!(field.captured_count(Player::Black), 0);
  assert!(!field.cell(field.to_pos(2, 3)).is_putting_allowed());
  assert!(!field.cell(field.to_pos(2, 4)).is_putting_allowed());
  assert!(!field.cell(field.to_pos(2, 2)).is_putting_allowed());
  assert!(!field.cell(field.to_pos(1, 3)).is_putting_allowed());
  assert!(!field.cell(field.to_pos(3, 3)).is_putting_allowed());
  assert!(!field.cell(field.to_pos(1, 1)).is_putting_allowed());
}

#[test]
fn a_hole_inside_a_surrounding() {
  let field = construct_field(
    "
    ....c....
    ...c.c...
    ..c...c..
    .c..a..c.
    c..a.a..c
    .c..a..c.
    ..c...c..
    ...cBc...
    ....d....
    "
  );
  assert_eq!(field.captured_count(Player::Red), 1);
  assert_eq!(field.captured_count(Player::Black), 0);
  assert!(!field.cell(field.to_pos(4, 4)).is_putting_allowed());
  assert!(!field.cell(field.to_pos(4, 1)).is_putting_allowed());
}

#[test]
fn a_hole_inside_a_surrounding_after_control_surrounding() {
  let field = construct_field(
    "
    ....b....
    ...b.b...
    ..b...b..
    .b..a..b.
    b..a.a..b
    .b..a..b.
    ..b...b..
    ...bCb...
    ....b....
    "
  );
  assert_eq!(field.captured_count(Player::Red), 1);
  assert_eq!(field.captured_count(Player::Black), 0);
  assert!(!field.cell(field.to_pos(4, 4)).is_putting_allowed());
  assert!(!field.cell(field.to_pos(4, 1)).is_putting_allowed());
}

#[test]
fn surrounding_does_not_expand() {
  let field = construct_field(
    "
    ....a....
    ...a.a...
    ..a.a.a..
    .a.a.a.a.
    a.a.aBa.a
    .a.a.a.a.
    ..a.a.a..
    ...a.a...
    ....a....
    "
  );
  assert_eq!(field.captured_count(Player::Red), 1);
  assert_eq!(field.captured_count(Player::Black), 0);
  assert!(field.cell(field.to_pos(6, 3)).is_putting_allowed());
  assert!(field.cell(field.to_pos(4, 3)).is_putting_allowed());
  assert!(field.cell(field.to_pos(4, 5)).is_putting_allowed());
  assert!(field.cell(field.to_pos(6, 5)).is_putting_allowed());
  assert!(!field.cell(field.to_pos(5, 4)).is_putting_allowed());
}

#[test]
fn two_surroundings_with_common_border() {
  let field = construct_field(
    "
    .a..
    aAa.
    .bAa
    ..a.
    "
  );
  assert_eq!(field.captured_count(Player::Red), 2);
  assert_eq!(field.captured_count(Player::Black), 0);
}

#[test]
fn three_surroundings_with_common_borders() {
  let field = construct_field(
    "
    ..a..
    .aAa.
    ..bAa
    .aAa.
    ..a..
    "
  );
  assert_eq!(field.captured_count(Player::Red), 3);
  assert_eq!(field.captured_count(Player::Black), 0);
}

#[derive(Clone, PartialEq, Debug)]
struct FieldArbitrary {
  width: u32,
  height: u32,
  moves: Vec<Pos>,
  zobrist: Arc<Zobrist>
}

impl Iterator for FieldArbitrary {
  type Item = FieldArbitrary;
  fn next(&mut self) -> Option<FieldArbitrary> {
    if self.moves.is_empty() {
      None
    } else {
      let result = self.clone();
      self.moves.pop();
      Some(result)
    }
  }
  fn count(self) -> usize {
    self.moves.len()
  }
}

impl Arbitrary for FieldArbitrary {
  fn arbitrary<G: Gen>(gen: &mut G) -> FieldArbitrary {
    let width = gen.next_u32() % 27 + 3;
    let height = gen.next_u32() % 27 + 3;
    let mut moves = (field::min_pos(width) .. field::max_pos(width, height) + 1).collect::<Vec<Pos>>();
    gen.shuffle(&mut moves);
    let zobrist = Arc::new(Zobrist::new(field::length(width, height) * 2, gen));
    FieldArbitrary {
      width: width,
      height: height,
      moves: moves,
      zobrist: zobrist
    }
  }
  fn shrink(&self) -> Box<Iterator<Item = FieldArbitrary>> {
    let mut result = self.clone();
    result.moves.pop();
    Box::new(result)
  }
}

#[test]
fn undo_check() {
  fn prop(field_arbitrary: FieldArbitrary) -> TestResult {
    let mut field = Field::new(field_arbitrary.width, field_arbitrary.height, field_arbitrary.zobrist.clone());
    let mut player = Player::Red;
    for &pos in &field_arbitrary.moves {
      if field.is_putting_allowed(pos) {
        let field_before = field.clone();
        field.put_point(pos, player);
        field.undo();
        if field_before != field {
          return TestResult::failed();
        }
        field.put_point(pos, player);
        player = player.next();
      }
    }
    TestResult::passed()
  }
  quickcheck::quickcheck(prop as fn(FieldArbitrary) -> TestResult);
}
<|endoftext|>


<commit_before>extern crate reqwest;
extern crate serde_json;

use self::reqwest::header::{Authorization, Link, UserAgent};
use self::reqwest::{Error, Response, Url};

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

static USERAGENT: &'static str = "release-party-br";

#[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,
    pub label: String,
}

pub fn is_release_up_to_date_with_master(repo_url: &str, token: &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())
        .expect("Couldn't make a request builder for comparison page url")
        .header(UserAgent::new(USERAGENT.to_string()))
        .header(Authorization(format!("token {}", token)))
        .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::Headers) {
    if close_to_running_out_of_requests(response_headers) {
        println!("Running low on requests, throttling back...");
        thread::sleep(time::Duration::from_millis(1000));
    }
}

// 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::Headers) -> bool {
    let requests_to_treat_as_running_out = 10;
    let remaining_requests = match response_headers.get_raw("X-RateLimit-Remaining") {
        Some(remaining_req_from_github) => {
            let req_left = String::from_utf8(
                remaining_req_from_github
                    .one()
                    .expect("Should have a single entry for X-RateLimit-Remaining")
                    .to_vec(),
            ).expect("Should be able to parse slice as string")
                .replace('"', ""); // the formatter puts quotes around the number.  EG: "55"
            req_left
                .parse::<i32>()
                .expect("Expected number in X-RateLimit-Remaining field")
        }
        // 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::Headers) -> bool {
    if response_headers.get::<Link>().is_none() {
        return false;
    }
    for link in response_headers
        .get::<Link>()
        .expect("links in has next")
        .values()
    {
        let next_link = link.rel();
        match next_link {
            Some(v) => {
                let link_something = v.first().expect("should have value in links headers");
                if link_something == &reqwest::header::RelationType::Next {
                    return true;
                }
            }
            None => return false,
        }
    }
    false
}

// Expects caller to check to ensure the `next` link is present
fn response_next_link(response_headers: &reqwest::header::Headers) -> Result<Url, String> {
    for link in response_headers
        .get::<Link>()
        .expect("links in response next")
        .values()
    {
        let next_link = link.rel();
        match next_link {
            Some(v) => {
                let link_something = v.first().expect("should have value in links headers");
                if link_something == &reqwest::header::RelationType::Next {
                    let uri = Url::parse(link.link()).expect("Should have been able to parse the nextlink");
                    return Ok(uri);
                }
            }
            None => (),
        }
    }
    Err("Couldn't find a next link: does it exist?".to_string())
}

pub fn get_repos_at(repos_url: &str, token: &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, token, client).expect("request failed");
    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, token, 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, token: &str, client: &reqwest::Client) -> Result<Response, Error> {
    client
        .get(url)
        .expect("Couldn't make a request builder for repos page url")
        .header(UserAgent::new(USERAGENT.to_string()))
        .header(Authorization(format!("token {}", token)))
        .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, token: &str, 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)
        .expect("Couldn't make a request builder for existing PRs page url")
        .header(UserAgent::new(USERAGENT.to_string()))
        .header(Authorization(format!("token {}", token)))
        .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
}

// Try to create the release PR and return the URL of it:
pub fn create_release_pull_request(repo: &GithubRepo, token: &str, 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)
        .expect("Couldn't make a request builder for creating PR url")
        .header(UserAgent::new(USERAGENT.to_string()))
        .header(Authorization(format!("token {}", token)))
        .json(&pr_body)
        .expect("Couldn't make the JSON payload for creating a PR")
        .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 hyper::header::Headers;

    #[test]
    fn plenty_of_requests_left() {
        header! { (XRateLimitRemaining, "X-RateLimit-Remaining") => [String] };
        let mut plenty_left_headers = Headers::new();

        plenty_left_headers.set(XRateLimitRemaining("10000".to_owned()));
        assert_eq!(false, close_to_running_out_of_requests(&plenty_left_headers));
    }

    #[test]
    fn no_requests_left() {
        header! { (XRateLimitRemaining, "X-RateLimit-Remaining") => [String] };
        let mut plenty_left_headers = Headers::new();

        plenty_left_headers.set(XRateLimitRemaining("0".to_owned()));
        assert_eq!(true, close_to_running_out_of_requests(&plenty_left_headers));
    }
}<commit_msg>Test some more low hanging fruit.<commit_after>extern crate reqwest;
extern crate serde_json;

use self::reqwest::header::{Authorization, Link, UserAgent};
use self::reqwest::{Error, Response, Url};

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

static USERAGENT: &'static str = "release-party-br";

#[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,
    pub label: String,
}

pub fn is_release_up_to_date_with_master(repo_url: &str, token: &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())
        .expect("Couldn't make a request builder for comparison page url")
        .header(UserAgent::new(USERAGENT.to_string()))
        .header(Authorization(format!("token {}", token)))
        .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::Headers) {
    if close_to_running_out_of_requests(response_headers) {
        println!("Running low on requests, throttling back...");
        thread::sleep(time::Duration::from_millis(1000));
    }
}

// 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::Headers) -> bool {
    let requests_to_treat_as_running_out = 10;
    let remaining_requests = match response_headers.get_raw("X-RateLimit-Remaining") {
        Some(remaining_req_from_github) => {
            let req_left = String::from_utf8(
                remaining_req_from_github
                    .one()
                    .expect("Should have a single entry for X-RateLimit-Remaining")
                    .to_vec(),
            ).expect("Should be able to parse slice as string")
                .replace('"', ""); // the formatter puts quotes around the number.  EG: "55"
            req_left
                .parse::<i32>()
                .expect("Expected number in X-RateLimit-Remaining field")
        }
        // 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::Headers) -> bool {
    if response_headers.get::<Link>().is_none() {
        return false;
    }
    for link in response_headers
        .get::<Link>()
        .expect("links in has next")
        .values()
    {
        let next_link = link.rel();
        match next_link {
            Some(v) => {
                let link_something = v.first().expect("should have value in links headers");
                if link_something == &reqwest::header::RelationType::Next {
                    return true;
                }
            }
            None => return false,
        }
    }
    false
}

// Expects caller to check to ensure the `next` link is present
fn response_next_link(response_headers: &reqwest::header::Headers) -> Result<Url, String> {
    for link in response_headers
        .get::<Link>()
        .expect("links in response next")
        .values()
    {
        let next_link = link.rel();
        match next_link {
            Some(v) => {
                let link_something = v.first().expect("should have value in links headers");
                if link_something == &reqwest::header::RelationType::Next {
                    let uri = Url::parse(link.link()).expect("Should have been able to parse the nextlink");
                    return Ok(uri);
                }
            }
            None => (),
        }
    }
    Err("Couldn't find a next link: does it exist?".to_string())
}

pub fn get_repos_at(repos_url: &str, token: &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, token, client).expect("request failed");
    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, token, 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, token: &str, client: &reqwest::Client) -> Result<Response, Error> {
    client
        .get(url)
        .expect("Couldn't make a request builder for repos page url")
        .header(UserAgent::new(USERAGENT.to_string()))
        .header(Authorization(format!("token {}", token)))
        .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, token: &str, 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)
        .expect("Couldn't make a request builder for existing PRs page url")
        .header(UserAgent::new(USERAGENT.to_string()))
        .header(Authorization(format!("token {}", token)))
        .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
}

// Try to create the release PR and return the URL of it:
pub fn create_release_pull_request(repo: &GithubRepo, token: &str, 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)
        .expect("Couldn't make a request builder for creating PR url")
        .header(UserAgent::new(USERAGENT.to_string()))
        .header(Authorization(format!("token {}", token)))
        .json(&pr_body)
        .expect("Couldn't make the JSON payload for creating a PR")
        .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 hyper::header::{Headers, Link, LinkValue, RelationType};

    #[test]
    fn plenty_of_requests_left() {
        header! { (XRateLimitRemaining, "X-RateLimit-Remaining") => [String] };
        let mut plenty_left_headers = Headers::new();

        plenty_left_headers.set(XRateLimitRemaining("10000".to_owned()));
        assert_eq!(false, close_to_running_out_of_requests(&plenty_left_headers));
    }

    #[test]
    fn no_requests_left() {
        header! { (XRateLimitRemaining, "X-RateLimit-Remaining") => [String] };
        let mut plenty_left_headers = Headers::new();

        plenty_left_headers.set(XRateLimitRemaining("0".to_owned()));
        assert_eq!(true, close_to_running_out_of_requests(&plenty_left_headers));
    }

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

    #[test]
    fn has_next_link() {
        let link_value = LinkValue::new("http://example.com/TheBook/chapter2")
            .push_rel(RelationType::Next)
            .set_title("next page");

        let mut no_next_link_headers = Headers::new();
        no_next_link_headers.set(
            Link::new(vec![link_value])
        );

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

    #[test]
    fn finds_next_link() {
        let link_value = LinkValue::new("http://example.com/TheBook/chapter3")
            .push_rel(RelationType::Next)
            .set_title("next page");

        let mut no_next_link_headers = Headers::new();
        no_next_link_headers.set(
            Link::new(vec![link_value])
        );
        let expected_uri = Url::parse("http://example.com/TheBook/chapter3").unwrap();
        assert_eq!(expected_uri, response_next_link(&no_next_link_headers).unwrap());
    }
}<|endoftext|>


<commit_before>use itertools::Itertools;
use std::hash::Hash;
use std::iter::FromIterator;

use generate::{HashIndexer, IndexVertices, IntoTriangles, IntoVertices, Topological, Triangulate};
use graph::geometry::{Attribute, FromGeometry, Geometry, IntoGeometry};
use graph::storage::{EdgeKey, FaceKey, Storage, VertexKey};
use graph::topology::{FaceMut, FaceRef};

#[derive(Clone, Debug)]
pub struct Vertex<T>
where
    T: Attribute,
{
    pub geometry: T,
    pub(super) edge: Option<EdgeKey>,
}

impl<T> Vertex<T>
where
    T: Attribute,
{
    fn new(geometry: T) -> Self {
        Vertex {
            geometry: geometry,
            edge: None,
        }
    }
}

impl<T, U> FromGeometry<Vertex<U>> for Vertex<T>
where
    T: Attribute + FromGeometry<U>,
    U: Attribute,
{
    fn from_geometry(vertex: Vertex<U>) -> Self {
        Vertex {
            geometry: vertex.geometry.into_geometry(),
            edge: vertex.edge,
        }
    }
}

#[derive(Clone, Debug)]
pub struct Edge<T>
where
    T: Attribute,
{
    pub geometry: T,
    pub(super) vertex: VertexKey,
    pub(super) opposite: Option<EdgeKey>,
    pub(super) next: Option<EdgeKey>,
    pub(super) face: Option<FaceKey>,
}

impl<T> Edge<T>
where
    T: Attribute,
{
    fn new(vertex: VertexKey, geometry: T) -> Self {
        Edge {
            geometry: geometry,
            vertex: vertex,
            opposite: None,
            next: None,
            face: None,
        }
    }
}

impl<T, U> FromGeometry<Edge<U>> for Edge<T>
where
    T: Attribute + FromGeometry<U>,
    U: Attribute,
{
    fn from_geometry(edge: Edge<U>) -> Self {
        Edge {
            geometry: edge.geometry.into_geometry(),
            vertex: edge.vertex,
            opposite: edge.opposite,
            next: edge.next,
            face: edge.face,
        }
    }
}

#[derive(Clone, Debug)]
pub struct Face<T>
where
    T: Attribute,
{
    pub geometry: T,
    pub(super) edge: EdgeKey,
}

impl<T> Face<T>
where
    T: Attribute,
{
    fn new(edge: EdgeKey, geometry: T) -> Self {
        Face {
            geometry: geometry,
            edge: edge,
        }
    }
}

impl<T, U> FromGeometry<Face<U>> for Face<T>
where
    T: Attribute + FromGeometry<U>,
    U: Attribute,
{
    fn from_geometry(face: Face<U>) -> Self {
        Face {
            geometry: face.geometry.into_geometry(),
            edge: face.edge,
        }
    }
}

pub struct Mesh<G = ()>
where
    G: Geometry,
{
    pub(super) vertices: Storage<VertexKey, Vertex<G::Vertex>>,
    pub(super) edges: Storage<EdgeKey, Edge<G::Edge>>,
    pub(super) faces: Storage<FaceKey, Face<G::Face>>,
}

impl<G> Mesh<G>
where
    G: Geometry,
{
    pub fn new() -> Self {
        Mesh {
            vertices: Storage::new(),
            edges: Storage::new(),
            faces: Storage::new(),
        }
    }

    pub fn vertex_count(&self) -> usize {
        self.vertices.len()
    }

    pub fn edge_count(&self) -> usize {
        self.edges.len()
    }

    pub fn face_count(&self) -> usize {
        self.faces.len()
    }

    pub fn face(&self, face: FaceKey) -> Option<FaceRef<G>> {
        self.faces.get(&face).map(|_| FaceRef::new(self, face))
    }

    pub fn face_mut(&mut self, face: FaceKey) -> Option<FaceMut<G>> {
        match self.faces.contains_key(&face) {
            true => Some(FaceMut::new(self, face)),
            _ => None,
        }
    }

    pub(crate) fn insert_vertex(&mut self, geometry: G::Vertex) -> VertexKey {
        self.vertices.insert_with_generator(Vertex::new(geometry))
    }

    pub(crate) fn insert_edge(
        &mut self,
        vertices: (VertexKey, VertexKey),
        geometry: G::Edge,
    ) -> Result<EdgeKey, ()> {
        let (a, b) = vertices;
        let ab = (a, b).into();
        let ba = (b, a).into();
        let mut edge = Edge::new(b, geometry);
        if let Some(opposite) = self.edges.get_mut(&ba) {
            edge.opposite = Some(ba);
            opposite.opposite = Some(ab);
        }
        self.edges.insert_with_key(&ab, edge);
        self.vertices.get_mut(&a).unwrap().edge = Some(ab);
        Ok(ab)
    }

    pub(crate) fn insert_face(
        &mut self,
        edges: &[EdgeKey],
        geometry: G::Face,
    ) -> Result<FaceKey, ()> {
        if edges.len() < 3 {
            return Err(());
        }
        let face = self.faces
            .insert_with_generator(Face::new(edges[0], geometry));
        for index in 0..edges.len() {
            // TODO: Connecting these edges creates a cycle. This means code
            //       must be able to detect these cycles. Is this okay?
            self.connect_edges_in_face(face, (edges[index], edges[(index + 1) % edges.len()]));
        }
        Ok(face)
    }

    // TODO: This code orphans vertices; it does not remove vertices with no
    //       remaining associated edges. `FaceView::extrude` relies on this
    //       behavior.  Is this okay?
    pub(crate) fn remove_face(&mut self, face: FaceKey) -> Result<(), ()> {
        // Get all of the edges forming the face.
        let edges = {
            self.face(face)
                .unwrap()
                .edges()
                .map(|edge| edge.key)
                .collect::<Vec<_>>()
        };
        // For each edge, disconnect its opposite edge and originating vertex,
        // then remove it from the graph.
        for edge in edges {
            let (a, b) = {
                let edge = self.edges.get(&edge).unwrap();
                (
                    edge.vertex,
                    // When the last edge is reached, its key for the next edge
                    // will be invalid, so do not `unwrap` the result of the
                    // lookup.
                    edge.next
                        .and_then(|edge| self.edges.get(&edge))
                        .map(|edge| edge.vertex),
                )
            };
            // Disconnect the opposite edge, if any.
            if let Some(b) = b {
                let ba = (b, a).into();
                if let Some(opposite) = self.edges.get_mut(&ba) {
                    opposite.opposite = None;
                }
            }
            // Disconnect the originating vertex, if any.
            let vertex = self.vertices.get_mut(&a).unwrap();
            if vertex
                .edge
                .map(|outgoing| outgoing == edge)
                .unwrap_or(false)
            {
                vertex.edge = None;
            }
            self.edges.remove(&edge);
        }
        self.faces.remove(&face);
        Ok(())
    }

    fn connect_edges_in_face(&mut self, face: FaceKey, edges: (EdgeKey, EdgeKey)) {
        let edge = self.edges.get_mut(&edges.0).unwrap();
        edge.next = Some(edges.1);
        edge.face = Some(face);
    }
}

impl<G> AsRef<Mesh<G>> for Mesh<G>
where
    G: Geometry,
{
    fn as_ref(&self) -> &Self {
        self
    }
}

impl<G> AsMut<Mesh<G>> for Mesh<G>
where
    G: Geometry,
{
    fn as_mut(&mut self) -> &mut Self {
        self
    }
}

impl<G, H> FromGeometry<Mesh<H>> for Mesh<G>
where
    G: Geometry,
    G::Vertex: FromGeometry<H::Vertex>,
    G::Edge: FromGeometry<H::Edge>,
    G::Face: FromGeometry<H::Face>,
    H: Geometry,
{
    fn from_geometry(mesh: Mesh<H>) -> Self {
        let Mesh {
            vertices,
            edges,
            faces,
        } = mesh;
        // TODO: The new geometry should be recomputed or finalized here.
        Mesh {
            vertices: vertices.map_values(|vertex| vertex.into_geometry()),
            edges: edges.map_values(|edge| edge.into_geometry()),
            faces: faces.map_values(|face| face.into_geometry()),
        }
    }
}

impl<G, T> FromIterator<T> for Mesh<G>
where
    G: Geometry,
    T: IntoTriangles + IntoVertices + Topological,
    T::Vertex: Eq + Hash + Into<G::Vertex>,
{
    fn from_iter<I>(input: I) -> Self
    where
        I: IntoIterator<Item = T>,
    {
        let mut mesh = Mesh::new();
        let (indeces, vertices) = input
            .into_iter()
            .triangulate()
            .index_vertices(HashIndexer::default());
        let vertices = vertices
            .into_iter()
            .map(|vertex| mesh.insert_vertex(vertex.into()))
            .collect::<Vec<_>>();
        for mut triangle in &indeces.into_iter().chunks(3) {
            // Map from the indeces into the original buffers to the keys
            // referring to the vertices in the mesh.
            let (a, b, c) = (
                vertices[triangle.next().unwrap()],
                vertices[triangle.next().unwrap()],
                vertices[triangle.next().unwrap()],
            );
            let (ab, bc, ca) = (
                mesh.insert_edge((a, b), G::Edge::default()).unwrap(),
                mesh.insert_edge((b, c), G::Edge::default()).unwrap(),
                mesh.insert_edge((c, a), G::Edge::default()).unwrap(),
            );
            mesh.insert_face(&[ab, bc, ca], G::Face::default()).unwrap();
        }
        mesh
    }
}

#[cfg(test)]
mod tests {
    use r32;
    use generate::*;
    use graph::*;

    #[test]
    fn collect_topology_into_mesh() {
        let mesh = sphere::UVSphere::<f32>::with_unit_radius(3, 2)
            .spatial_polygons() // 6 triangles, 18 vertices.
            .ordered::<(r32, r32, r32)>()
            .triangulate()
            .collect::<Mesh<(r32, r32, r32)>>();

        assert_eq!(5, mesh.vertex_count());
        assert_eq!(18, mesh.edge_count());
        assert_eq!(6, mesh.face_count());
    }
}
<commit_msg>Remove connection to opposite edges in a simpler way.<commit_after>use itertools::Itertools;
use std::hash::Hash;
use std::iter::FromIterator;

use generate::{HashIndexer, IndexVertices, IntoTriangles, IntoVertices, Topological, Triangulate};
use graph::geometry::{Attribute, FromGeometry, Geometry, IntoGeometry};
use graph::storage::{EdgeKey, FaceKey, Storage, VertexKey};
use graph::topology::{FaceMut, FaceRef};

#[derive(Clone, Debug)]
pub struct Vertex<T>
where
    T: Attribute,
{
    pub geometry: T,
    pub(super) edge: Option<EdgeKey>,
}

impl<T> Vertex<T>
where
    T: Attribute,
{
    fn new(geometry: T) -> Self {
        Vertex {
            geometry: geometry,
            edge: None,
        }
    }
}

impl<T, U> FromGeometry<Vertex<U>> for Vertex<T>
where
    T: Attribute + FromGeometry<U>,
    U: Attribute,
{
    fn from_geometry(vertex: Vertex<U>) -> Self {
        Vertex {
            geometry: vertex.geometry.into_geometry(),
            edge: vertex.edge,
        }
    }
}

#[derive(Clone, Debug)]
pub struct Edge<T>
where
    T: Attribute,
{
    pub geometry: T,
    pub(super) vertex: VertexKey,
    pub(super) opposite: Option<EdgeKey>,
    pub(super) next: Option<EdgeKey>,
    pub(super) face: Option<FaceKey>,
}

impl<T> Edge<T>
where
    T: Attribute,
{
    fn new(vertex: VertexKey, geometry: T) -> Self {
        Edge {
            geometry: geometry,
            vertex: vertex,
            opposite: None,
            next: None,
            face: None,
        }
    }
}

impl<T, U> FromGeometry<Edge<U>> for Edge<T>
where
    T: Attribute + FromGeometry<U>,
    U: Attribute,
{
    fn from_geometry(edge: Edge<U>) -> Self {
        Edge {
            geometry: edge.geometry.into_geometry(),
            vertex: edge.vertex,
            opposite: edge.opposite,
            next: edge.next,
            face: edge.face,
        }
    }
}

#[derive(Clone, Debug)]
pub struct Face<T>
where
    T: Attribute,
{
    pub geometry: T,
    pub(super) edge: EdgeKey,
}

impl<T> Face<T>
where
    T: Attribute,
{
    fn new(edge: EdgeKey, geometry: T) -> Self {
        Face {
            geometry: geometry,
            edge: edge,
        }
    }
}

impl<T, U> FromGeometry<Face<U>> for Face<T>
where
    T: Attribute + FromGeometry<U>,
    U: Attribute,
{
    fn from_geometry(face: Face<U>) -> Self {
        Face {
            geometry: face.geometry.into_geometry(),
            edge: face.edge,
        }
    }
}

pub struct Mesh<G = ()>
where
    G: Geometry,
{
    pub(super) vertices: Storage<VertexKey, Vertex<G::Vertex>>,
    pub(super) edges: Storage<EdgeKey, Edge<G::Edge>>,
    pub(super) faces: Storage<FaceKey, Face<G::Face>>,
}

impl<G> Mesh<G>
where
    G: Geometry,
{
    pub fn new() -> Self {
        Mesh {
            vertices: Storage::new(),
            edges: Storage::new(),
            faces: Storage::new(),
        }
    }

    pub fn vertex_count(&self) -> usize {
        self.vertices.len()
    }

    pub fn edge_count(&self) -> usize {
        self.edges.len()
    }

    pub fn face_count(&self) -> usize {
        self.faces.len()
    }

    pub fn face(&self, face: FaceKey) -> Option<FaceRef<G>> {
        self.faces.get(&face).map(|_| FaceRef::new(self, face))
    }

    pub fn face_mut(&mut self, face: FaceKey) -> Option<FaceMut<G>> {
        match self.faces.contains_key(&face) {
            true => Some(FaceMut::new(self, face)),
            _ => None,
        }
    }

    pub(crate) fn insert_vertex(&mut self, geometry: G::Vertex) -> VertexKey {
        self.vertices.insert_with_generator(Vertex::new(geometry))
    }

    pub(crate) fn insert_edge(
        &mut self,
        vertices: (VertexKey, VertexKey),
        geometry: G::Edge,
    ) -> Result<EdgeKey, ()> {
        let (a, b) = vertices;
        let ab = (a, b).into();
        let ba = (b, a).into();
        let mut edge = Edge::new(b, geometry);
        if let Some(opposite) = self.edges.get_mut(&ba) {
            edge.opposite = Some(ba);
            opposite.opposite = Some(ab);
        }
        self.edges.insert_with_key(&ab, edge);
        self.vertices.get_mut(&a).unwrap().edge = Some(ab);
        Ok(ab)
    }

    pub(crate) fn insert_face(
        &mut self,
        edges: &[EdgeKey],
        geometry: G::Face,
    ) -> Result<FaceKey, ()> {
        if edges.len() < 3 {
            return Err(());
        }
        let face = self.faces
            .insert_with_generator(Face::new(edges[0], geometry));
        for index in 0..edges.len() {
            // TODO: Connecting these edges creates a cycle. This means code
            //       must be able to detect these cycles. Is this okay?
            self.connect_edges_in_face(face, (edges[index], edges[(index + 1) % edges.len()]));
        }
        Ok(face)
    }

    // TODO: This code orphans vertices; it does not remove vertices with no
    //       remaining associated edges. `FaceView::extrude` relies on this
    //       behavior.  Is this okay?
    pub(crate) fn remove_face(&mut self, face: FaceKey) -> Result<(), ()> {
        // Get all of the edges forming the face.
        let edges = {
            self.face(face)
                .unwrap()
                .edges()
                .map(|edge| edge.key)
                .collect::<Vec<_>>()
        };
        // For each edge, disconnect its opposite edge and originating vertex,
        // then remove it from the graph.
        for edge in edges {
            let (a, opposite) = {
                let edge = self.edges.get(&edge).unwrap();
                (edge.vertex, edge.opposite)
            };
            if let Some(edge) = opposite.map(|opposite| self.edges.get_mut(&opposite).unwrap()) {
                edge.opposite = None;
            }
            // Disconnect the originating vertex, if any.
            let vertex = self.vertices.get_mut(&a).unwrap();
            if vertex
                .edge
                .map(|outgoing| outgoing == edge)
                .unwrap_or(false)
            {
                vertex.edge = None;
            }
            self.edges.remove(&edge);
        }
        self.faces.remove(&face);
        Ok(())
    }

    fn connect_edges_in_face(&mut self, face: FaceKey, edges: (EdgeKey, EdgeKey)) {
        let edge = self.edges.get_mut(&edges.0).unwrap();
        edge.next = Some(edges.1);
        edge.face = Some(face);
    }
}

impl<G> AsRef<Mesh<G>> for Mesh<G>
where
    G: Geometry,
{
    fn as_ref(&self) -> &Self {
        self
    }
}

impl<G> AsMut<Mesh<G>> for Mesh<G>
where
    G: Geometry,
{
    fn as_mut(&mut self) -> &mut Self {
        self
    }
}

impl<G, H> FromGeometry<Mesh<H>> for Mesh<G>
where
    G: Geometry,
    G::Vertex: FromGeometry<H::Vertex>,
    G::Edge: FromGeometry<H::Edge>,
    G::Face: FromGeometry<H::Face>,
    H: Geometry,
{
    fn from_geometry(mesh: Mesh<H>) -> Self {
        let Mesh {
            vertices,
            edges,
            faces,
        } = mesh;
        // TODO: The new geometry should be recomputed or finalized here.
        Mesh {
            vertices: vertices.map_values(|vertex| vertex.into_geometry()),
            edges: edges.map_values(|edge| edge.into_geometry()),
            faces: faces.map_values(|face| face.into_geometry()),
        }
    }
}

impl<G, T> FromIterator<T> for Mesh<G>
where
    G: Geometry,
    T: IntoTriangles + IntoVertices + Topological,
    T::Vertex: Eq + Hash + Into<G::Vertex>,
{
    fn from_iter<I>(input: I) -> Self
    where
        I: IntoIterator<Item = T>,
    {
        let mut mesh = Mesh::new();
        let (indeces, vertices) = input
            .into_iter()
            .triangulate()
            .index_vertices(HashIndexer::default());
        let vertices = vertices
            .into_iter()
            .map(|vertex| mesh.insert_vertex(vertex.into()))
            .collect::<Vec<_>>();
        for mut triangle in &indeces.into_iter().chunks(3) {
            // Map from the indeces into the original buffers to the keys
            // referring to the vertices in the mesh.
            let (a, b, c) = (
                vertices[triangle.next().unwrap()],
                vertices[triangle.next().unwrap()],
                vertices[triangle.next().unwrap()],
            );
            let (ab, bc, ca) = (
                mesh.insert_edge((a, b), G::Edge::default()).unwrap(),
                mesh.insert_edge((b, c), G::Edge::default()).unwrap(),
                mesh.insert_edge((c, a), G::Edge::default()).unwrap(),
            );
            mesh.insert_face(&[ab, bc, ca], G::Face::default()).unwrap();
        }
        mesh
    }
}

#[cfg(test)]
mod tests {
    use r32;
    use generate::*;
    use graph::*;

    #[test]
    fn collect_topology_into_mesh() {
        let mesh = sphere::UVSphere::<f32>::with_unit_radius(3, 2)
            .spatial_polygons() // 6 triangles, 18 vertices.
            .ordered::<(r32, r32, r32)>()
            .triangulate()
            .collect::<Mesh<(r32, r32, r32)>>();

        assert_eq!(5, mesh.vertex_count());
        assert_eq!(18, mesh.edge_count());
        assert_eq!(6, mesh.face_count());
    }
}
<|endoftext|>


<commit_before>use std::io;

use tokio_io::codec::{Encoder, Decoder};
use bytes::{BytesMut, BigEndian, ByteOrder, BufMut};

#[derive(Clone, Copy, Debug)]
pub struct U31(u32);

impl U31 {
    #[inline]
    pub fn min_value() -> U31 {
        U31(0)
    }
    #[inline]
    pub fn initial_value() -> U31 {
        U31(0xffff)
    }
    #[inline]
    pub fn max_value() -> U31 {
        U31(0x7fffffff)
    }
    #[inline]
    pub fn as_u32(&self) -> u32 {
        self.0
    }
}

impl<'a> From<&'a [u8; 4]> for U31 {
    #[inline]
    fn from(src: &'a [u8; 4]) -> U31 {
        let n = BigEndian::read_u32(src);
        U31(n & 0x7fffffff)
    }
}

impl From<u32> for U31 {
    #[inline]
    fn from(src: u32) -> U31 {
        if src > Self::max_value().as_u32() {
            Self::max_value()
        } else {
            U31(src)
        }
    }
}

pub struct U31Codec;

impl Decoder for U31Codec {
    type Item = (bool, U31);
    type Error = io::Error;

    fn decode(&mut self, src: &mut BytesMut) -> Result<Option<Self::Item>, Self::Error> {
        if src.len() < 4 {
            Ok(None)
        } else {
            let tmp = unsafe { &*(&src.split_to(4) as &[u8] as *const [u8] as *const [u8; 4]) };
            let b = (tmp[0] & 0x80) != 0;
            let n = U31::from(tmp);
            Ok(Some((b, n)))
        }
    }
}

impl Encoder for U31Codec {
    type Item = (bool, U31);
    type Error = io::Error;

    fn encode(&mut self, item: Self::Item, dst: &mut BytesMut) -> Result<(), Self::Error> {
        let r = if item.0 {
            (item.1).0 | 0x80000000
        } else {
            (item.1).0
        };
        dst.put_u32::<BigEndian>(r);
        Ok(())
    }
}

#[derive(Clone, Copy, Debug)]
pub struct U24(u32);

impl U24 {
    #[inline]
    pub fn min_value() -> U24 {
        U24(0)
    }
    #[inline]
    pub fn initial_value() -> U24 {
        U24(0x4000)
    }
    #[inline]
    pub fn max_value() -> U24 {
        U24(0xffffff)
    }
    #[inline]
    pub fn as_u32(&self) -> u32 {
        self.0
    }
}

impl<'a> From<&'a [u8; 3]> for U24 {
    #[inline]
    fn from(src: &'a [u8; 3]) -> U24 {
        let mut buf = [0u8; 4];
        buf[1..].copy_from_slice(src);
        U24(BigEndian::read_u32(&buf))
    }
}

impl From<u32> for U24 {
    #[inline]
    fn from(src: u32) -> U24 {
        if src > Self::max_value().as_u32() {
            Self::max_value()
        } else {
            U24(src)
        }
    }
}

pub struct U24Codec;

impl Decoder for U24Codec {
    type Item = U24;
    type Error = io::Error;

    fn decode(&mut self, src: &mut BytesMut) -> Result<Option<Self::Item>, Self::Error> {
        if src.len() < 3 {
            Ok(None)
        } else {
            let tmp = unsafe { &*(&src.split_to(3) as &[u8] as *const [u8] as *const [u8; 3]) };
            let n = U24::from(tmp);
            Ok(Some(n))
        }
    }
}

impl Encoder for U24Codec {
    type Item = U24;
    type Error = io::Error;

    fn encode(&mut self, item: Self::Item, dst: &mut BytesMut) -> Result<(), Self::Error> {
        let mut buf = [0u8; 4];
        BigEndian::write_u32(&mut buf, item.0);
        dst.extend_from_slice(&buf[1..]);
        Ok(())
    }
}
<commit_msg>Add test for `U31` and `U24`<commit_after>use std::io;

use tokio_io::codec::{Encoder, Decoder};
use bytes::{BytesMut, BigEndian, ByteOrder, BufMut};

#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd)]
pub struct U31(u32);

impl U31 {
    #[inline]
    pub fn min_value() -> U31 {
        U31(0)
    }
    #[inline]
    pub fn initial_value() -> U31 {
        U31(0xffff)
    }
    #[inline]
    pub fn max_value() -> U31 {
        U31(0x7fffffff)
    }
    #[inline]
    pub fn as_u32(&self) -> u32 {
        self.0
    }
}

impl<'a> From<&'a [u8; 4]> for U31 {
    #[inline]
    fn from(src: &'a [u8; 4]) -> U31 {
        let n = BigEndian::read_u32(src);
        U31(n & 0x7fffffff)
    }
}

impl From<u32> for U31 {
    #[inline]
    fn from(src: u32) -> U31 {
        if src > Self::max_value().as_u32() {
            Self::max_value()
        } else {
            U31(src)
        }
    }
}

pub struct U31Codec;

impl Decoder for U31Codec {
    type Item = (bool, U31);
    type Error = io::Error;

    fn decode(&mut self, src: &mut BytesMut) -> Result<Option<Self::Item>, Self::Error> {
        if src.len() < 4 {
            Ok(None)
        } else {
            let tmp = unsafe { &*(&src.split_to(4) as &[u8] as *const [u8] as *const [u8; 4]) };
            let b = (tmp[0] & 0x80) != 0;
            let n = U31::from(tmp);
            Ok(Some((b, n)))
        }
    }
}

impl Encoder for U31Codec {
    type Item = (bool, U31);
    type Error = io::Error;

    fn encode(&mut self, item: Self::Item, dst: &mut BytesMut) -> Result<(), Self::Error> {
        let r = if item.0 {
            (item.1).0 | 0x80000000
        } else {
            (item.1).0
        };
        dst.put_u32::<BigEndian>(r);
        Ok(())
    }
}

#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd)]
pub struct U24(u32);

impl U24 {
    #[inline]
    pub fn min_value() -> U24 {
        U24(0)
    }
    #[inline]
    pub fn initial_value() -> U24 {
        U24(0x4000)
    }
    #[inline]
    pub fn max_value() -> U24 {
        U24(0xffffff)
    }
    #[inline]
    pub fn as_u32(&self) -> u32 {
        self.0
    }
}

impl<'a> From<&'a [u8; 3]> for U24 {
    #[inline]
    fn from(src: &'a [u8; 3]) -> U24 {
        let mut buf = [0u8; 4];
        buf[1..].copy_from_slice(src);
        U24(BigEndian::read_u32(&buf))
    }
}

impl From<u32> for U24 {
    #[inline]
    fn from(src: u32) -> U24 {
        if src > Self::max_value().as_u32() {
            Self::max_value()
        } else {
            U24(src)
        }
    }
}

pub struct U24Codec;

impl Decoder for U24Codec {
    type Item = U24;
    type Error = io::Error;

    fn decode(&mut self, src: &mut BytesMut) -> Result<Option<Self::Item>, Self::Error> {
        if src.len() < 3 {
            Ok(None)
        } else {
            let tmp = unsafe { &*(&src.split_to(3) as &[u8] as *const [u8] as *const [u8; 3]) };
            let n = U24::from(tmp);
            Ok(Some(n))
        }
    }
}

impl Encoder for U24Codec {
    type Item = U24;
    type Error = io::Error;

    fn encode(&mut self, item: Self::Item, dst: &mut BytesMut) -> Result<(), Self::Error> {
        let mut buf = [0u8; 4];
        BigEndian::write_u32(&mut buf, item.0);
        dst.extend_from_slice(&buf[1..]);
        Ok(())
    }
}

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

    #[test]
    fn u31() {
        let data: &[u8] = &[255, 255, 255, 255];
        let r = U31Codec.decode(&mut data.into()).unwrap().expect("u31");
        assert_eq!(r, (true, U31::max_value()));
        let mut buf = BytesMut::with_capacity(4);
        U31Codec.encode(r, &mut buf).unwrap();
        assert_eq!(buf, data);
    }

    #[test]
    fn u24() {
        let data: &[u8] = &[255, 255, 255];
        let r = U24Codec.decode(&mut data.into()).unwrap().expect("u24");
        assert_eq!(r, U24::max_value());
        let mut buf = BytesMut::with_capacity(3);
        U24Codec.encode(r, &mut buf).unwrap();
        assert_eq!(buf, data);
    }
}
<|endoftext|>