<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_crate::{CrateInfoWithScore, CrateGeneralInfo};
use esr_github::RepoInfoWithScore;
use esr_printer::{EsrPrinter, EsrFormatter};
use esr_util;
use esr_errors::Result;

use std::f64;

pub enum Scores {
    CrateAndRepo(CrateInfoWithScore, Result<RepoInfoWithScore>),
    CrateOnly(CrateInfoWithScore),
    RepoOnly(RepoInfoWithScore),
}

impl Scores {
    pub fn from_id_with_token(id: &str, gh_token: &str) -> Result<Self> {
        let cr_score = CrateInfoWithScore::from_id(id)?;
        let repo_score = cr_score.get_info().github_id()
            .ok_or("failed to get GitHub id")
            .map(|gh_id| RepoInfoWithScore::from_id_with_token(&gh_id, gh_token))?;

        Ok(Scores::CrateAndRepo(cr_score, repo_score))
    }

    pub fn from_id_crate_only(id: &str) -> Result<Self> {
        let cr_score = CrateInfoWithScore::from_id(id)?;
        Ok(Scores::CrateOnly(cr_score))
    }

    pub fn from_id_with_token_repo_only(id: &str, gh_token: &str) -> Result<Self> {
        let cr_score = CrateInfoWithScore::from_id(id)?;
        let gh_id = cr_score.get_info().github_id().ok_or("repo-only score requested but failed to get GitHub id")?;
        let repo_score = RepoInfoWithScore::from_id_with_token(&gh_id, gh_token)?;

        Ok(Scores::RepoOnly(repo_score))
    }

    pub fn from_repo_with_token(repo: &str, gh_token: &str) -> Result<Self> {
        let gh_id = esr_util::github_repo(repo).ok_or("repo score requested but failed to get a valid GitHub repo path")?;
        let repo_score = RepoInfoWithScore::from_id_with_token(&gh_id, gh_token)?;

        Ok(Scores::RepoOnly(repo_score))
    }

    // ====================
    fn score_crate(&self) -> [EsrFormatter; 4] {
        match *self {
            Scores::CrateAndRepo(ref cr_score, _) | Scores::CrateOnly(ref cr_score) => {
                let (pos, neg) = cr_score.get_score_tuple();
                EsrPrinter::score_overview("Crate Score", pos, neg)
            },
            Scores::RepoOnly(_) => unreachable!(),
        }
    }

    fn score_repo(&self) -> Vec<EsrFormatter> {
        match *self {
            Scores::CrateAndRepo (_, Ok(ref repo_score)) | Scores::RepoOnly(ref repo_score) => {
                let (pos, neg) = repo_score.get_score_tuple();
                EsrPrinter::score_overview("Repo Score ", pos, neg).to_vec()
            },
            Scores::CrateAndRepo (_, Err(_)) => EsrPrinter::score_error("Repo Score ").to_vec(),
            Scores::CrateOnly(_) => EsrPrinter::score_na("Repo Score ").to_vec(),
        }
    }

    pub fn print_detailed_scores(&self, formatted: bool) {
        // Unfortunately, `if let` is not as powerful as `match`. So, we have to
        // to do this *_opt dance.
        let cr_score_opt = match *self {
            Scores::CrateAndRepo(ref cr_score, _) | Scores::CrateOnly(ref cr_score) => Some(cr_score),
            Scores::RepoOnly(_) => None,
        };

        let repo_score_opt = match *self {
            Scores::CrateAndRepo(_, Ok(ref repo_score)) | Scores::RepoOnly(ref repo_score) => Some(repo_score),
            Scores::CrateAndRepo(_, Err(_)) | Scores::CrateOnly(_) => None,
        };

        if let Some(cr_score) = cr_score_opt {
            let id = cr_score.get_info().get_id();
            EsrFormatter::print_grp(&self.info_pair(id, false, formatted).1, formatted);

            let table = cr_score.get_score_table();
            EsrFormatter::print_grp(&EsrPrinter::score_details("Crate Score Details", table), formatted);
            EsrFormatter::print_grp(&self.score_crate(), formatted);
        }

        if let Some(repo_score) = repo_score_opt {
            let table = repo_score.get_score_table();
            EsrFormatter::print_grp(&EsrPrinter::score_details("Repo Score Details", table), formatted);
        }

        EsrFormatter::print_grp(&*self.score_repo(), formatted);
    }

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

    pub fn collect_scores(crates: &[CrateGeneralInfo], token: &str,
                          crate_only: bool,
                          repo_only: bool,
                          limit: usize) -> Vec<(String, Result<Self>)> {
        let id_token_pair: Vec<_> = crates.iter()
            .map(|cr| (String::from(cr.get_id()), String::from(token)))
            .collect();

        if crate_only {
            id_token_pair.into_iter()
                .with_threads(limit)
                .map(move |(id, _)| (id.clone(), Scores::from_id_crate_only(&id)))
                .collect()
        } else if repo_only {
            id_token_pair.into_iter()
                .with_threads(limit)
                .map(move |(id, token)| (id.clone(), Scores::from_id_with_token_repo_only(&id, &token)))
                .collect()
        } else {
            id_token_pair.into_iter()
                .with_threads(limit)
                .map(move |(id, token)| (id.clone(), Scores::from_id_with_token(&id, &token)))
                .collect()
        }
    }

    fn info_pair(&self, id: &str, sort_positive: bool, formatted: bool) -> (f64, Vec<EsrFormatter>) {
        match *self {
            Scores::CrateAndRepo(ref cr_score, _) | Scores::CrateOnly(ref cr_score) => {
                let cr_info = cr_score.get_info();
                let (pos, neg) = cr_score.get_score_tuple();

                let empty_or_all_yanked_formatted = match cr_info.empty_or_all_yanked() {
                    true => EsrPrinter::all_yanked(),
                    false => EsrFormatter::trail_only("\n "),
                };

                let sort_score = match sort_positive {
                    true => pos,
                    false => pos + neg,
                };

                let releases = cr_score.get_score_info().get_releases();
                let non_yanked = cr_score.get_score_info().get_non_yanked_releases();
                let stable = cr_score.get_score_info().get_stable_releases();
                let yanked = releases - non_yanked;
                let non_yanked_pre = non_yanked - stable;
                let releases_formatted = EsrPrinter::releases(stable, non_yanked_pre, yanked);

                let max_ver = Some(cr_info.get_max_version());
                let max_ver_age = cr_info.max_version_age();
                let max_ver_msg = EsrPrinter::release(max_ver, max_ver_age);

                let last_stable_version = cr_info.last_stable_version();
                let last_stable_version_age = cr_info.last_stable_version_age();
                let last_stable_version_msg = EsrPrinter::release(last_stable_version, last_stable_version_age);

                let dependants = cr_score.get_score_info().get_dependants();
                let d_b_n_o = cr_score.get_score_info().get_dependants_from_non_owners();
                let dependants_msg = format!("{} ({} from non owners)", dependants, d_b_n_o);

                let desc = cr_info.get_description().map(|desc_str| {
                    if formatted {
                        EsrPrinter::desc(desc_str)
                    } else {
                        desc_str.into()
                    }
                }).unwrap_or("N/A".into());

                let mut info_formatter = Vec::with_capacity(32);
                info_formatter.push(EsrPrinter::id(id));
                info_formatter.push(empty_or_all_yanked_formatted);
                info_formatter.extend_from_slice(&self.score_crate());
                info_formatter.extend_from_slice(&self.score_repo());
                info_formatter.extend_from_slice(&*EsrPrinter::msg_pair_complex("Releases   ", &releases_formatted));
                info_formatter.extend_from_slice(&EsrPrinter::msg_pair("Max Version", &max_ver_msg));
                info_formatter.extend_from_slice(&EsrPrinter::msg_pair("Last Stable", &last_stable_version_msg));
                info_formatter.extend_from_slice(&EsrPrinter::msg_pair("Dependants ", &dependants_msg));
                info_formatter.extend_from_slice(&EsrPrinter::msg_pair("License    ", cr_info.get_license().unwrap_or("N/A")));
                info_formatter.extend_from_slice(&EsrPrinter::msg_pair("Repository ", cr_info.get_repository().unwrap_or("N/A")));
                info_formatter.extend_from_slice(&EsrPrinter::msg_pair("Description", &*desc));

                (sort_score, info_formatter)
            },
            Scores::RepoOnly(ref repo_score) => {
                let (pos, neg) = repo_score.get_score_tuple();
                let sort_score = match sort_positive {
                    true => pos,
                    false => pos + neg,
                };

                let mut info_formatter = Vec::with_capacity(32);
                info_formatter.push(EsrPrinter::id(id));
                info_formatter.push(EsrFormatter::trail_only("\n "));
                info_formatter.extend_from_slice(&self.score_repo());
                (sort_score, info_formatter)
            },
        }
    }

    pub fn print_search_results(results: &[(String, Result<Self>)], sort_positive: bool, limit: usize, formatted: bool) {
        let mut results_vec = Vec::with_capacity(32);
        for res in results {
            match *res {
                (ref id, Ok(ref score_info)) => {
                    results_vec.push(score_info.info_pair(id, sort_positive, formatted));
                },
                (ref id, Err(ref e)) => {
                    results_vec.push((f64::MIN, vec![EsrPrinter::err(&format!("{}: Failed to get score info: {}.", id, e))]));
                },
            }
        }

        // Negation to get scores in reverse.
        // `* 10000.0` to not lose order accuracy after casting.
        results_vec.sort_by_key(|&(sort_score, _)| -(sort_score * 10000.0) as i64);
        for (num, result) in results_vec.iter().take(limit).enumerate() {
            EsrPrinter::id(&format!("({})", num + 1)).print(formatted);
            EsrFormatter::print_grp(&result.1, formatted);
        }
    }
}
<commit_msg>esr_score: Avoid failure if crate+repo scores wanted but only crate scores available<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_crate::{CrateInfoWithScore, CrateGeneralInfo};
use esr_github::RepoInfoWithScore;
use esr_printer::{EsrPrinter, EsrFormatter};
use esr_util;
use esr_errors::Result;

use std::f64;

pub enum Scores {
    CrateAndRepo(CrateInfoWithScore, Result<RepoInfoWithScore>),
    CrateOnly(CrateInfoWithScore),
    RepoOnly(RepoInfoWithScore),
}

impl Scores {
    pub fn from_id_with_token(id: &str, gh_token: &str) -> Result<Self> {
        let cr_score = CrateInfoWithScore::from_id(id)?;
        let repo_score_res = cr_score.get_info().github_id()
            .ok_or("Failed to get GitHub id")
            .map(|gh_id| RepoInfoWithScore::from_id_with_token(&gh_id, gh_token));

        match repo_score_res {
            Ok(repo_score) => Ok(Scores::CrateAndRepo(cr_score, repo_score)),
            Err(_) => Ok(Scores::CrateOnly(cr_score)),
        }
    }

    pub fn from_id_crate_only(id: &str) -> Result<Self> {
        let cr_score = CrateInfoWithScore::from_id(id)?;
        Ok(Scores::CrateOnly(cr_score))
    }

    pub fn from_id_with_token_repo_only(id: &str, gh_token: &str) -> Result<Self> {
        let cr_score = CrateInfoWithScore::from_id(id)?;
        let gh_id = cr_score.get_info().github_id().ok_or("repo-only score requested but failed to get GitHub id")?;
        let repo_score = RepoInfoWithScore::from_id_with_token(&gh_id, gh_token)?;

        Ok(Scores::RepoOnly(repo_score))
    }

    pub fn from_repo_with_token(repo: &str, gh_token: &str) -> Result<Self> {
        let gh_id = esr_util::github_repo(repo).ok_or("repo score requested but failed to get a valid GitHub repo path")?;
        let repo_score = RepoInfoWithScore::from_id_with_token(&gh_id, gh_token)?;

        Ok(Scores::RepoOnly(repo_score))
    }

    // ====================
    fn score_crate(&self) -> [EsrFormatter; 4] {
        match *self {
            Scores::CrateAndRepo(ref cr_score, _) | Scores::CrateOnly(ref cr_score) => {
                let (pos, neg) = cr_score.get_score_tuple();
                EsrPrinter::score_overview("Crate Score", pos, neg)
            },
            Scores::RepoOnly(_) => unreachable!(),
        }
    }

    fn score_repo(&self) -> Vec<EsrFormatter> {
        match *self {
            Scores::CrateAndRepo (_, Ok(ref repo_score)) | Scores::RepoOnly(ref repo_score) => {
                let (pos, neg) = repo_score.get_score_tuple();
                EsrPrinter::score_overview("Repo Score ", pos, neg).to_vec()
            },
            Scores::CrateAndRepo (_, Err(_)) => EsrPrinter::score_error("Repo Score ").to_vec(),
            Scores::CrateOnly(_) => EsrPrinter::score_na("Repo Score ").to_vec(),
        }
    }

    pub fn print_detailed_scores(&self, formatted: bool) {
        // Unfortunately, `if let` is not as powerful as `match`. So, we have to
        // to do this *_opt dance.
        let cr_score_opt = match *self {
            Scores::CrateAndRepo(ref cr_score, _) | Scores::CrateOnly(ref cr_score) => Some(cr_score),
            Scores::RepoOnly(_) => None,
        };

        let repo_score_opt = match *self {
            Scores::CrateAndRepo(_, Ok(ref repo_score)) | Scores::RepoOnly(ref repo_score) => Some(repo_score),
            Scores::CrateAndRepo(_, Err(_)) | Scores::CrateOnly(_) => None,
        };

        if let Some(cr_score) = cr_score_opt {
            let id = cr_score.get_info().get_id();
            EsrFormatter::print_grp(&self.info_pair(id, false, formatted).1, formatted);

            let table = cr_score.get_score_table();
            EsrFormatter::print_grp(&EsrPrinter::score_details("Crate Score Details", table), formatted);
            EsrFormatter::print_grp(&self.score_crate(), formatted);
        }

        if let Some(repo_score) = repo_score_opt {
            let table = repo_score.get_score_table();
            EsrFormatter::print_grp(&EsrPrinter::score_details("Repo Score Details", table), formatted);
        }

        EsrFormatter::print_grp(&*self.score_repo(), formatted);
    }

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

    pub fn collect_scores(crates: &[CrateGeneralInfo], token: &str,
                          crate_only: bool,
                          repo_only: bool,
                          limit: usize) -> Vec<(String, Result<Self>)> {
        let id_token_pair: Vec<_> = crates.iter()
            .map(|cr| (String::from(cr.get_id()), String::from(token)))
            .collect();

        if crate_only {
            id_token_pair.into_iter()
                .with_threads(limit)
                .map(move |(id, _)| (id.clone(), Scores::from_id_crate_only(&id)))
                .collect()
        } else if repo_only {
            id_token_pair.into_iter()
                .with_threads(limit)
                .map(move |(id, token)| (id.clone(), Scores::from_id_with_token_repo_only(&id, &token)))
                .collect()
        } else {
            id_token_pair.into_iter()
                .with_threads(limit)
                .map(move |(id, token)| (id.clone(), Scores::from_id_with_token(&id, &token)))
                .collect()
        }
    }

    fn info_pair(&self, id: &str, sort_positive: bool, formatted: bool) -> (f64, Vec<EsrFormatter>) {
        match *self {
            Scores::CrateAndRepo(ref cr_score, _) | Scores::CrateOnly(ref cr_score) => {
                let cr_info = cr_score.get_info();
                let (pos, neg) = cr_score.get_score_tuple();

                let empty_or_all_yanked_formatted = match cr_info.empty_or_all_yanked() {
                    true => EsrPrinter::all_yanked(),
                    false => EsrFormatter::trail_only("\n "),
                };

                let sort_score = match sort_positive {
                    true => pos,
                    false => pos + neg,
                };

                let releases = cr_score.get_score_info().get_releases();
                let non_yanked = cr_score.get_score_info().get_non_yanked_releases();
                let stable = cr_score.get_score_info().get_stable_releases();
                let yanked = releases - non_yanked;
                let non_yanked_pre = non_yanked - stable;
                let releases_formatted = EsrPrinter::releases(stable, non_yanked_pre, yanked);

                let max_ver = Some(cr_info.get_max_version());
                let max_ver_age = cr_info.max_version_age();
                let max_ver_msg = EsrPrinter::release(max_ver, max_ver_age);

                let last_stable_version = cr_info.last_stable_version();
                let last_stable_version_age = cr_info.last_stable_version_age();
                let last_stable_version_msg = EsrPrinter::release(last_stable_version, last_stable_version_age);

                let dependants = cr_score.get_score_info().get_dependants();
                let d_b_n_o = cr_score.get_score_info().get_dependants_from_non_owners();
                let dependants_msg = format!("{} ({} from non owners)", dependants, d_b_n_o);

                let desc = cr_info.get_description().map(|desc_str| {
                    if formatted {
                        EsrPrinter::desc(desc_str)
                    } else {
                        desc_str.into()
                    }
                }).unwrap_or("N/A".into());

                let mut info_formatter = Vec::with_capacity(32);
                info_formatter.push(EsrPrinter::id(id));
                info_formatter.push(empty_or_all_yanked_formatted);
                info_formatter.extend_from_slice(&self.score_crate());
                info_formatter.extend_from_slice(&self.score_repo());
                info_formatter.extend_from_slice(&*EsrPrinter::msg_pair_complex("Releases   ", &releases_formatted));
                info_formatter.extend_from_slice(&EsrPrinter::msg_pair("Max Version", &max_ver_msg));
                info_formatter.extend_from_slice(&EsrPrinter::msg_pair("Last Stable", &last_stable_version_msg));
                info_formatter.extend_from_slice(&EsrPrinter::msg_pair("Dependants ", &dependants_msg));
                info_formatter.extend_from_slice(&EsrPrinter::msg_pair("License    ", cr_info.get_license().unwrap_or("N/A")));
                info_formatter.extend_from_slice(&EsrPrinter::msg_pair("Repository ", cr_info.get_repository().unwrap_or("N/A")));
                info_formatter.extend_from_slice(&EsrPrinter::msg_pair("Description", &*desc));

                (sort_score, info_formatter)
            },
            Scores::RepoOnly(ref repo_score) => {
                let (pos, neg) = repo_score.get_score_tuple();
                let sort_score = match sort_positive {
                    true => pos,
                    false => pos + neg,
                };

                let mut info_formatter = Vec::with_capacity(32);
                info_formatter.push(EsrPrinter::id(id));
                info_formatter.push(EsrFormatter::trail_only("\n "));
                info_formatter.extend_from_slice(&self.score_repo());
                (sort_score, info_formatter)
            },
        }
    }

    pub fn print_search_results(results: &[(String, Result<Self>)], sort_positive: bool, limit: usize, formatted: bool) {
        let mut results_vec = Vec::with_capacity(32);
        for res in results {
            match *res {
                (ref id, Ok(ref score_info)) => {
                    results_vec.push(score_info.info_pair(id, sort_positive, formatted));
                },
                (ref id, Err(ref e)) => {
                    results_vec.push((f64::MIN, vec![EsrPrinter::err(&format!("{}: Failed to get score info: {}.", id, e))]));
                },
            }
        }

        // Negation to get scores in reverse.
        // `* 10000.0` to not lose order accuracy after casting.
        results_vec.sort_by_key(|&(sort_score, _)| -(sort_score * 10000.0) as i64);
        for (num, result) in results_vec.iter().take(limit).enumerate() {
            EsrPrinter::id(&format!("({})", num + 1)).print(formatted);
            EsrFormatter::print_grp(&result.1, formatted);
        }
    }
}
<|endoftext|>


<commit_before>use crate::{
    buffer::Buffer, connection::Connection, connection_status::ConnectionState, Error, Result,
};
use amq_protocol::frame::{gen_frame, parse_frame, AMQPFrame, GenError, Offset};
use log::{error, trace};
use mio::{Evented, Events, Poll, PollOpt, Ready, Registration, SetReadiness, Token};
use parking_lot::Mutex;
use std::{
    io::{Read, Write},
    sync::{
        atomic::{AtomicBool, Ordering},
        Arc,
    },
    thread::{self, Builder as ThreadBuilder, JoinHandle},
    time::{Duration, Instant},
};

pub(crate) const SOCKET: Token = Token(1);
const DATA: Token = Token(2);
const CONTINUE: Token = Token(3);

const FRAMES_STORAGE: usize = 32;

type ThreadHandle = JoinHandle<Result<()>>;

#[derive(Clone, Debug)]
pub(crate) struct IoLoopHandle {
    handle: Arc<Mutex<Option<ThreadHandle>>>,
}

impl Default for IoLoopHandle {
    fn default() -> Self {
        Self {
            handle: Arc::new(Mutex::new(None)),
        }
    }
}

impl IoLoopHandle {
    pub(crate) fn register(&self, handle: JoinHandle<Result<()>>) {
        *self.handle.lock() = Some(handle);
    }

    pub(crate) fn wait(&self) -> Result<()> {
        if let Some(handle) = self.handle.lock().take() {
            handle.join().expect("io loop")?
        }
        Ok(())
    }
}

#[derive(Debug, PartialEq)]
enum Status {
    Initial,
    Setup,
    Stop,
}

pub struct IoLoop<T> {
    connection: Connection,
    socket: T,
    status: Status,
    poll: Poll,
    registration: Registration,
    set_readiness: SetReadiness,
    hb_handle: Option<JoinHandle<()>>,
    frame_size: usize,
    receive_buffer: Buffer,
    send_buffer: Buffer,
    can_write: bool,
    can_read: bool,
    has_data: bool,
    send_heartbeat: Arc<AtomicBool>,
    poll_timeout: Option<Duration>,
}

impl<T: Evented + Read + Write + Send + 'static> IoLoop<T> {
    pub(crate) fn new(
        connection: Connection,
        socket: T,
        poll: Option<(Poll, Token)>,
    ) -> Result<Self> {
        let (poll, registered) = poll.map(|t| Ok((t.0, true))).unwrap_or_else(|| {
            Poll::new()
                .map(|poll| (poll, false))
                .map_err(Arc::new)
                .map_err(Error::IOError)
        })?;
        let frame_size = std::cmp::max(8192, connection.configuration().frame_max() as usize);
        let (registration, set_readiness) = Registration::new2();
        let inner = Self {
            connection,
            socket,
            status: Status::Initial,
            poll,
            registration,
            set_readiness,
            hb_handle: None,
            frame_size,
            receive_buffer: Buffer::with_capacity(FRAMES_STORAGE * frame_size),
            send_buffer: Buffer::with_capacity(FRAMES_STORAGE * frame_size),
            can_write: false,
            can_read: false,
            has_data: false,
            send_heartbeat: Arc::new(AtomicBool::new(false)),
            poll_timeout: None,
        };
        if registered {
            inner
                .poll
                .reregister(&inner.socket, SOCKET, Ready::all(), PollOpt::edge())
                .map_err(Arc::new)
                .map_err(Error::IOError)?;
        } else {
            inner
                .poll
                .register(&inner.socket, SOCKET, Ready::all(), PollOpt::edge())
                .map_err(Arc::new)
                .map_err(Error::IOError)?;
        }
        inner
            .poll
            .register(&inner.connection, DATA, Ready::readable(), PollOpt::edge())
            .map_err(Arc::new)
            .map_err(Error::IOError)?;
        inner
            .poll
            .register(
                &inner.registration,
                CONTINUE,
                Ready::readable(),
                PollOpt::edge(),
            )
            .map_err(Arc::new)
            .map_err(Error::IOError)?;
        Ok(inner)
    }

    fn start_heartbeat(&mut self, interval: Duration) -> Result<()> {
        let connection = self.connection.clone();
        let send_hartbeat = self.send_heartbeat.clone();
        let hb_handle = ThreadBuilder::new()
            .name("heartbeat".to_owned())
            .spawn(move || {
                while connection.status().connected() {
                    let start = Instant::now();
                    let mut remaining = interval;

                    loop {
                        thread::park_timeout(remaining);
                        let elapsed = start.elapsed();
                        if elapsed >= remaining {
                            break;
                        }
                        remaining -= interval - elapsed;
                    }

                    send_hartbeat.store(true, Ordering::Relaxed);
                }
            })
            .map_err(Arc::new)
            .map_err(Error::IOError)?;
        self.hb_handle = Some(hb_handle);
        Ok(())
    }

    fn heartbeat(&mut self) -> Result<()> {
        if self.send_heartbeat.load(Ordering::Relaxed) {
            trace!("send heartbeat");
            self.connection.send_heartbeat()?;
            self.send_heartbeat.store(false, Ordering::Relaxed);
        }
        Ok(())
    }

    fn ensure_setup(&mut self) -> Result<()> {
        if self.status != Status::Setup && self.connection.status().connected() {
            let frame_max = self.connection.configuration().frame_max() as usize;
            self.frame_size = std::cmp::max(self.frame_size, frame_max);
            self.receive_buffer.grow(FRAMES_STORAGE * self.frame_size);
            self.send_buffer.grow(FRAMES_STORAGE * self.frame_size);
            let heartbeat = self.connection.configuration().heartbeat();
            if heartbeat != 0 {
                trace!("io_loop: start heartbeat");
                let heartbeat = Duration::from_secs(u64::from(heartbeat));
                self.start_heartbeat(heartbeat)?;
                self.poll_timeout = Some(heartbeat);
                trace!("io_loop: heartbeat started");
            }
            self.status = Status::Setup;
        }
        Ok(())
    }

    fn can_write(&self) -> bool {
        self.can_write && self.has_data && !self.connection.status().blocked()
    }

    fn can_read(&self) -> bool {
        self.can_read
    }

    fn can_parse(&self) -> bool {
        self.receive_buffer.available_data() > 0
    }

    fn should_continue(&self) -> bool {
        let connection_status = self.connection.status();
        (self.status == Status::Initial
            || connection_status.connected()
            || connection_status.closing())
            && self.status != Status::Stop
            && !connection_status.errored()
    }

    pub fn start(mut self) -> Result<()> {
        self.connection.clone().set_io_loop(
            ThreadBuilder::new()
                .name("io_loop".to_owned())
                .spawn(move || {
                    let mut events = Events::with_capacity(1024);
                    while self.should_continue() {
                        self.run(&mut events)?;
                    }
                    if let Some(hb_handle) = self.hb_handle.take() {
                        hb_handle.thread().unpark();
                        hb_handle.join().expect("heartbeat loop failed");
                    }
                    Ok(())
                })
                .map_err(Arc::new)
                .map_err(Error::IOError)?,
        );
        Ok(())
    }

    fn poll(&mut self, events: &mut Events) -> Result<()> {
        trace!("io_loop poll");
        self.poll
            .poll(events, self.poll_timeout)
            .map_err(Arc::new)
            .map_err(Error::IOError)?;
        trace!("io_loop poll done");
        for event in events.iter() {
            match event.token() {
                SOCKET => {
                    if event.readiness().is_readable() {
                        self.can_read = true;
                    }
                    if event.readiness().is_writable() {
                        self.can_write = true;
                    }
                }
                DATA => self.has_data = true,
                _ => {}
            }
        }
        Ok(())
    }

    fn run(&mut self, events: &mut Events) -> Result<()> {
        trace!("io_loop run");
        self.ensure_setup()?;
        self.poll(events)?;
        self.do_run()
    }

    fn do_run(&mut self) -> Result<()> {
        trace!(
            "io_loop do_run; can_read={}, can_write={}, has_data={}",
            self.can_read,
            self.can_write,
            self.has_data
        );
        loop {
            self.heartbeat()?;
            self.write()?;
            if self.connection.status().closed() {
                self.status = Status::Stop;
            }
            if self.should_continue() {
                self.read()?;
            }
            self.parse()?;
            if self.stop_looping() {
                self.maybe_continue()?;
                break;
            }
        }
        trace!(
            "io_loop do_run done; can_read={}, can_write={}, has_data={}, status={:?}",
            self.can_read,
            self.can_write,
            self.has_data,
            self.status
        );
        Ok(())
    }

    fn stop_looping(&self) -> bool {
        !self.can_read()
            || !self.can_write()
            || self.status == Status::Stop
            || self.connection.status().errored()
    }

    fn has_pending_operations(&self) -> bool {
        self.status != Status::Stop && (self.can_read() || self.can_parse() || self.can_write())
    }

    fn maybe_continue(&mut self) -> Result<()> {
        if self.has_pending_operations() {
            trace!(
                "io_loop send continue; can_read={}, can_write={}, has_data={}",
                self.can_read,
                self.can_write,
                self.has_data
            );
            self.send_continue()?;
        }
        Ok(())
    }

    fn write(&mut self) -> Result<()> {
        if self.can_write() {
            if let Err(e) = self.write_to_stream() {
                if e.wouldblock() {
                    self.can_write = false
                } else {
                    error!("error writing: {:?}", e);
                    if let ConnectionState::SentProtocolHeader(pinky, ..) =
                        self.connection.status().state()
                    {
                        pinky.swear(Err(Error::ConnectionRefused));
                        self.status = Status::Stop;
                    }
                    self.connection.set_error(e.clone())?;
                    return Err(e);
                }
            }
            self.send_buffer.shift_unless_available(self.frame_size);
        }
        Ok(())
    }

    fn read(&mut self) -> Result<()> {
        if self.can_read() {
            if let Err(e) = self.read_from_stream() {
                if e.wouldblock() {
                    self.can_read = false
                } else {
                    error!("error reading: {:?}", e);
                    self.connection.set_error(e.clone())?;
                    return Err(e);
                }
            }
            self.receive_buffer.shift_unless_available(self.frame_size);
        }
        Ok(())
    }

    fn send_continue(&mut self) -> Result<()> {
        self.set_readiness
            .set_readiness(Ready::readable())
            .map_err(Arc::new)
            .map_err(Error::IOError)
    }

    fn write_to_stream(&mut self) -> Result<()> {
        self.serialize()?;

        let sz = self
            .socket
            .write(&self.send_buffer.data())
            .map_err(Arc::new)
            .map_err(Error::IOError)?;

        trace!("wrote {} bytes", sz);
        self.send_buffer.consume(sz);
        if sz > 0 && self.send_buffer.available_data() > 0 {
            // We didn't write all the data yet
            self.connection.set_readable()?;
        }
        Ok(())
    }

    fn read_from_stream(&mut self) -> Result<()> {
        match self.connection.status().state() {
            ConnectionState::Closed => Ok(()),
            ConnectionState::Error => Err(Error::InvalidConnectionState(ConnectionState::Error)),
            _ => self
                .socket
                .read(&mut self.receive_buffer.space())
                .map(|sz| {
                    trace!("read {} bytes", sz);
                    self.receive_buffer.fill(sz);
                })
                .map_err(Arc::new)
                .map_err(Error::IOError),
        }
    }

    fn serialize(&mut self) -> Result<()> {
        if let Some((send_id, next_msg)) = self.connection.next_frame() {
            trace!("will write to buffer: {:?}", next_msg);
            let checkpoint = self.send_buffer.checkpoint();
            let res = gen_frame(&next_msg)((&mut self.send_buffer).into());
            match res.map(|w| w.into_inner().1) {
                Ok(_) => {
                    self.connection.mark_sent(send_id);
                    Ok(())
                }
                Err(e) => {
                    self.send_buffer.rollback(checkpoint);
                    match e {
                        GenError::BufferTooSmall(_) => {
                            // Requeue msg
                            self.connection.requeue_frame(send_id, next_msg)?;
                            self.send_buffer.shift();
                            Ok(())
                        }
                        e => {
                            error!("error generating frame: {:?}", e);
                            let error = Error::SerialisationError(Arc::new(e));
                            self.connection.set_error(error.clone())?;
                            Err(error)
                        }
                    }
                }
            }
        } else {
            self.has_data = false;
            Ok(())
        }
    }

    fn parse(&mut self) -> Result<()> {
        if self.can_parse() {
            if let Some(frame) = self.do_parse()? {
                self.connection.handle_frame(frame)?;
            }
        }
        Ok(())
    }

    fn do_parse(&mut self) -> Result<Option<AMQPFrame>> {
        match parse_frame(self.receive_buffer.data()) {
            Ok((i, f)) => {
                let consumed = self.receive_buffer.data().offset(i);
                self.receive_buffer.consume(consumed);
                Ok(Some(f))
            }
            Err(e) => {
                if e.is_incomplete() {
                    self.receive_buffer.shift();
                    Ok(None)
                } else {
                    error!("parse error: {:?}", e);
                    let error = Error::ParsingError(format!("{:?}", e));
                    self.connection.set_error(error.clone())?;
                    Err(error)
                }
            }
        }
    }
}
<commit_msg>io_loop: properly handle critical errors in read too<commit_after>use crate::{
    buffer::Buffer, connection::Connection, connection_status::ConnectionState, Error, Result,
};
use amq_protocol::frame::{gen_frame, parse_frame, AMQPFrame, GenError, Offset};
use log::{error, trace};
use mio::{Evented, Events, Poll, PollOpt, Ready, Registration, SetReadiness, Token};
use parking_lot::Mutex;
use std::{
    io::{Read, Write},
    sync::{
        atomic::{AtomicBool, Ordering},
        Arc,
    },
    thread::{self, Builder as ThreadBuilder, JoinHandle},
    time::{Duration, Instant},
};

pub(crate) const SOCKET: Token = Token(1);
const DATA: Token = Token(2);
const CONTINUE: Token = Token(3);

const FRAMES_STORAGE: usize = 32;

type ThreadHandle = JoinHandle<Result<()>>;

#[derive(Clone, Debug)]
pub(crate) struct IoLoopHandle {
    handle: Arc<Mutex<Option<ThreadHandle>>>,
}

impl Default for IoLoopHandle {
    fn default() -> Self {
        Self {
            handle: Arc::new(Mutex::new(None)),
        }
    }
}

impl IoLoopHandle {
    pub(crate) fn register(&self, handle: JoinHandle<Result<()>>) {
        *self.handle.lock() = Some(handle);
    }

    pub(crate) fn wait(&self) -> Result<()> {
        if let Some(handle) = self.handle.lock().take() {
            handle.join().expect("io loop")?
        }
        Ok(())
    }
}

#[derive(Debug, PartialEq)]
enum Status {
    Initial,
    Setup,
    Stop,
}

pub struct IoLoop<T> {
    connection: Connection,
    socket: T,
    status: Status,
    poll: Poll,
    registration: Registration,
    set_readiness: SetReadiness,
    hb_handle: Option<JoinHandle<()>>,
    frame_size: usize,
    receive_buffer: Buffer,
    send_buffer: Buffer,
    can_write: bool,
    can_read: bool,
    has_data: bool,
    send_heartbeat: Arc<AtomicBool>,
    poll_timeout: Option<Duration>,
}

impl<T: Evented + Read + Write + Send + 'static> IoLoop<T> {
    pub(crate) fn new(
        connection: Connection,
        socket: T,
        poll: Option<(Poll, Token)>,
    ) -> Result<Self> {
        let (poll, registered) = poll.map(|t| Ok((t.0, true))).unwrap_or_else(|| {
            Poll::new()
                .map(|poll| (poll, false))
                .map_err(Arc::new)
                .map_err(Error::IOError)
        })?;
        let frame_size = std::cmp::max(8192, connection.configuration().frame_max() as usize);
        let (registration, set_readiness) = Registration::new2();
        let inner = Self {
            connection,
            socket,
            status: Status::Initial,
            poll,
            registration,
            set_readiness,
            hb_handle: None,
            frame_size,
            receive_buffer: Buffer::with_capacity(FRAMES_STORAGE * frame_size),
            send_buffer: Buffer::with_capacity(FRAMES_STORAGE * frame_size),
            can_write: false,
            can_read: false,
            has_data: false,
            send_heartbeat: Arc::new(AtomicBool::new(false)),
            poll_timeout: None,
        };
        if registered {
            inner
                .poll
                .reregister(&inner.socket, SOCKET, Ready::all(), PollOpt::edge())
                .map_err(Arc::new)
                .map_err(Error::IOError)?;
        } else {
            inner
                .poll
                .register(&inner.socket, SOCKET, Ready::all(), PollOpt::edge())
                .map_err(Arc::new)
                .map_err(Error::IOError)?;
        }
        inner
            .poll
            .register(&inner.connection, DATA, Ready::readable(), PollOpt::edge())
            .map_err(Arc::new)
            .map_err(Error::IOError)?;
        inner
            .poll
            .register(
                &inner.registration,
                CONTINUE,
                Ready::readable(),
                PollOpt::edge(),
            )
            .map_err(Arc::new)
            .map_err(Error::IOError)?;
        Ok(inner)
    }

    fn start_heartbeat(&mut self, interval: Duration) -> Result<()> {
        let connection = self.connection.clone();
        let send_hartbeat = self.send_heartbeat.clone();
        let hb_handle = ThreadBuilder::new()
            .name("heartbeat".to_owned())
            .spawn(move || {
                while connection.status().connected() {
                    let start = Instant::now();
                    let mut remaining = interval;

                    loop {
                        thread::park_timeout(remaining);
                        let elapsed = start.elapsed();
                        if elapsed >= remaining {
                            break;
                        }
                        remaining -= interval - elapsed;
                    }

                    send_hartbeat.store(true, Ordering::Relaxed);
                }
            })
            .map_err(Arc::new)
            .map_err(Error::IOError)?;
        self.hb_handle = Some(hb_handle);
        Ok(())
    }

    fn heartbeat(&mut self) -> Result<()> {
        if self.send_heartbeat.load(Ordering::Relaxed) {
            trace!("send heartbeat");
            self.connection.send_heartbeat()?;
            self.send_heartbeat.store(false, Ordering::Relaxed);
        }
        Ok(())
    }

    fn ensure_setup(&mut self) -> Result<()> {
        if self.status != Status::Setup && self.connection.status().connected() {
            let frame_max = self.connection.configuration().frame_max() as usize;
            self.frame_size = std::cmp::max(self.frame_size, frame_max);
            self.receive_buffer.grow(FRAMES_STORAGE * self.frame_size);
            self.send_buffer.grow(FRAMES_STORAGE * self.frame_size);
            let heartbeat = self.connection.configuration().heartbeat();
            if heartbeat != 0 {
                trace!("io_loop: start heartbeat");
                let heartbeat = Duration::from_secs(u64::from(heartbeat));
                self.start_heartbeat(heartbeat)?;
                self.poll_timeout = Some(heartbeat);
                trace!("io_loop: heartbeat started");
            }
            self.status = Status::Setup;
        }
        Ok(())
    }

    fn can_write(&self) -> bool {
        self.can_write && self.has_data && !self.connection.status().blocked()
    }

    fn can_read(&self) -> bool {
        self.can_read
    }

    fn can_parse(&self) -> bool {
        self.receive_buffer.available_data() > 0
    }

    fn should_continue(&self) -> bool {
        let connection_status = self.connection.status();
        (self.status == Status::Initial
            || connection_status.connected()
            || connection_status.closing())
            && self.status != Status::Stop
            && !connection_status.errored()
    }

    pub fn start(mut self) -> Result<()> {
        self.connection.clone().set_io_loop(
            ThreadBuilder::new()
                .name("io_loop".to_owned())
                .spawn(move || {
                    let mut events = Events::with_capacity(1024);
                    while self.should_continue() {
                        self.run(&mut events)?;
                    }
                    if let Some(hb_handle) = self.hb_handle.take() {
                        hb_handle.thread().unpark();
                        hb_handle.join().expect("heartbeat loop failed");
                    }
                    Ok(())
                })
                .map_err(Arc::new)
                .map_err(Error::IOError)?,
        );
        Ok(())
    }

    fn poll(&mut self, events: &mut Events) -> Result<()> {
        trace!("io_loop poll");
        self.poll
            .poll(events, self.poll_timeout)
            .map_err(Arc::new)
            .map_err(Error::IOError)?;
        trace!("io_loop poll done");
        for event in events.iter() {
            match event.token() {
                SOCKET => {
                    if event.readiness().is_readable() {
                        self.can_read = true;
                    }
                    if event.readiness().is_writable() {
                        self.can_write = true;
                    }
                }
                DATA => self.has_data = true,
                _ => {}
            }
        }
        Ok(())
    }

    fn run(&mut self, events: &mut Events) -> Result<()> {
        trace!("io_loop run");
        self.ensure_setup()?;
        self.poll(events)?;
        self.do_run()
    }

    fn do_run(&mut self) -> Result<()> {
        trace!(
            "io_loop do_run; can_read={}, can_write={}, has_data={}",
            self.can_read,
            self.can_write,
            self.has_data
        );
        loop {
            self.heartbeat()?;
            self.write()?;
            if self.connection.status().closed() {
                self.status = Status::Stop;
            }
            if self.should_continue() {
                self.read()?;
            }
            self.parse()?;
            if self.stop_looping() {
                self.maybe_continue()?;
                break;
            }
        }
        trace!(
            "io_loop do_run done; can_read={}, can_write={}, has_data={}, status={:?}",
            self.can_read,
            self.can_write,
            self.has_data,
            self.status
        );
        Ok(())
    }

    fn stop_looping(&self) -> bool {
        !self.can_read()
            || !self.can_write()
            || self.status == Status::Stop
            || self.connection.status().errored()
    }

    fn has_pending_operations(&self) -> bool {
        self.status != Status::Stop && (self.can_read() || self.can_parse() || self.can_write())
    }

    fn maybe_continue(&mut self) -> Result<()> {
        if self.has_pending_operations() {
            trace!(
                "io_loop send continue; can_read={}, can_write={}, has_data={}",
                self.can_read,
                self.can_write,
                self.has_data
            );
            self.send_continue()?;
        }
        Ok(())
    }

    fn critical_error(&mut self, error: Error) -> Result<()> {
        if let ConnectionState::SentProtocolHeader(pinky, ..) = self.connection.status().state() {
            pinky.swear(Err(error.clone()));
            self.status = Status::Stop;
        }
        self.connection.set_error(error.clone())?;
        Err(error)
    }

    fn write(&mut self) -> Result<()> {
        if self.can_write() {
            if let Err(e) = self.write_to_stream() {
                if e.wouldblock() {
                    self.can_write = false
                } else {
                    error!("error writing: {:?}", e);
                    self.critical_error(e)?;
                }
            }
            self.send_buffer.shift_unless_available(self.frame_size);
        }
        Ok(())
    }

    fn read(&mut self) -> Result<()> {
        if self.can_read() {
            if let Err(e) = self.read_from_stream() {
                if e.wouldblock() {
                    self.can_read = false
                } else {
                    error!("error reading: {:?}", e);
                    self.critical_error(e)?;
                }
            }
            self.receive_buffer.shift_unless_available(self.frame_size);
        }
        Ok(())
    }

    fn send_continue(&mut self) -> Result<()> {
        self.set_readiness
            .set_readiness(Ready::readable())
            .map_err(Arc::new)
            .map_err(Error::IOError)
    }

    fn write_to_stream(&mut self) -> Result<()> {
        self.serialize()?;

        let sz = self
            .socket
            .write(&self.send_buffer.data())
            .map_err(Arc::new)
            .map_err(Error::IOError)?;

        trace!("wrote {} bytes", sz);
        self.send_buffer.consume(sz);
        if sz > 0 && self.send_buffer.available_data() > 0 {
            // We didn't write all the data yet
            self.connection.set_readable()?;
        }
        Ok(())
    }

    fn read_from_stream(&mut self) -> Result<()> {
        match self.connection.status().state() {
            ConnectionState::Closed => Ok(()),
            ConnectionState::Error => Err(Error::InvalidConnectionState(ConnectionState::Error)),
            _ => self
                .socket
                .read(&mut self.receive_buffer.space())
                .map(|sz| {
                    trace!("read {} bytes", sz);
                    self.receive_buffer.fill(sz);
                })
                .map_err(Arc::new)
                .map_err(Error::IOError),
        }
    }

    fn serialize(&mut self) -> Result<()> {
        if let Some((send_id, next_msg)) = self.connection.next_frame() {
            trace!("will write to buffer: {:?}", next_msg);
            let checkpoint = self.send_buffer.checkpoint();
            let res = gen_frame(&next_msg)((&mut self.send_buffer).into());
            match res.map(|w| w.into_inner().1) {
                Ok(_) => {
                    self.connection.mark_sent(send_id);
                    Ok(())
                }
                Err(e) => {
                    self.send_buffer.rollback(checkpoint);
                    match e {
                        GenError::BufferTooSmall(_) => {
                            // Requeue msg
                            self.connection.requeue_frame(send_id, next_msg)?;
                            self.send_buffer.shift();
                            Ok(())
                        }
                        e => {
                            error!("error generating frame: {:?}", e);
                            let error = Error::SerialisationError(Arc::new(e));
                            self.connection.set_error(error.clone())?;
                            Err(error)
                        }
                    }
                }
            }
        } else {
            self.has_data = false;
            Ok(())
        }
    }

    fn parse(&mut self) -> Result<()> {
        if self.can_parse() {
            if let Some(frame) = self.do_parse()? {
                self.connection.handle_frame(frame)?;
            }
        }
        Ok(())
    }

    fn do_parse(&mut self) -> Result<Option<AMQPFrame>> {
        match parse_frame(self.receive_buffer.data()) {
            Ok((i, f)) => {
                let consumed = self.receive_buffer.data().offset(i);
                self.receive_buffer.consume(consumed);
                Ok(Some(f))
            }
            Err(e) => {
                if e.is_incomplete() {
                    self.receive_buffer.shift();
                    Ok(None)
                } else {
                    error!("parse error: {:?}", e);
                    let error = Error::ParsingError(format!("{:?}", e));
                    self.connection.set_error(error.clone())?;
                    Err(error)
                }
            }
        }
    }
}
<|endoftext|>


<commit_before>use std::collections::HashSet;
use std::default::Default;
use super::command::PrefixCheck;
use client::Context;
use http;
use model::{GuildId, Message, UserId};

/// The configuration to use for a [`Framework`] associated with a [`Client`]
/// instance.
///
/// This allows setting configurations like the depth to search for commands,
/// whether to treat mentions like a command prefix, etc.
///
/// # Examples
///
/// Responding to mentions and setting a command prefix of `"~"`:
///
/// ```rust,no_run
/// # use serenity::prelude::EventHandler;
/// struct Handler;
///
/// impl EventHandler for Handler {}
/// use serenity::Client;
/// use std::env;
/// use serenity::framework::StandardFramework;
///
/// let mut client = Client::new(&env::var("DISCORD_BOT_TOKEN").unwrap(), Handler);
///
/// client.with_framework(StandardFramework::new()
///     .configure(|c| c.on_mention(true).prefix("~")));
/// ```
///
/// [`Client`]: ../../client/struct.Client.html
/// [`Framework`]: struct.Framework.html
pub struct Configuration {
    #[doc(hidden)]
    pub allow_dm: bool,
    #[doc(hidden)]
    pub allow_whitespace: bool,
    #[doc(hidden)]
    pub blocked_guilds: HashSet<GuildId>,
    #[doc(hidden)]
    pub blocked_users: HashSet<UserId>,
    #[doc(hidden)]
    pub depth: usize,
    #[doc(hidden)]
    pub disabled_commands: HashSet<String>,
    #[doc(hidden)]
    pub dynamic_prefix: Option<Box<PrefixCheck>>,
    #[doc(hidden)]
    pub ignore_bots: bool,
    #[doc(hidden)]
    pub ignore_webhooks: bool,
    #[doc(hidden)]
    pub on_mention: Option<Vec<String>>,
    #[doc(hidden)]
    pub owners: HashSet<UserId>,
    #[doc(hidden)]
    pub prefixes: Vec<String>,
    #[doc(hidden)]
    pub delimiters: Vec<String>,
    #[doc(hidden)]
    pub case_insensitive: bool,
}

impl Configuration {
    /// If set to false, bot will ignore any private messages.
    pub fn allow_dm(mut self, allow_dm: bool) -> Self {
        self.allow_dm = allow_dm;

        self
    }

    /// Whether to allow whitespace being optional between a mention/prefix and
    /// a command.
    ///
    /// **Note**: Defaults to `false`.
    ///
    /// # Examples
    ///
    /// Setting this to `false` will _only_ allow this scenario to occur:
    ///
    /// ```ignore
    /// <@245571012924538880> about
    /// !about
    ///
    /// // bot processes and executes the "about" command if it exists
    /// ```
    ///
    /// while setting this to `true` will _also_ allow this scenario to occur:
    ///
    /// ```ignore
    /// <@245571012924538880>about
    /// ! about
    ///
    /// // bot processes and executes the "about" command if it exists
    /// ```
    pub fn allow_whitespace(mut self, allow_whitespace: bool) -> Self {
        self.allow_whitespace = allow_whitespace;

        self
    }

    /// HashSet of guild Ids where commands will be ignored.
    ///
    /// # Examples
    ///
    /// Create a HashSet in-place:
    ///
    /// ```rust
    /// # use serenity::prelude::*;
    /// # struct Handler;
    /// #
    /// # impl EventHandler for Handler {}
    /// # let mut client = Client::new("token", Handler);
    /// use serenity::model::GuildId;
    /// use serenity::framework::StandardFramework;
    ///
    /// client.with_framework(StandardFramework::new().configure(|c| c
    ///     .blocked_guilds(vec![GuildId(7), GuildId(77)].into_iter().collect())));
    /// ```
    pub fn blocked_guilds(mut self, guilds: HashSet<GuildId>) -> Self {
        self.blocked_guilds = guilds;

        self
    }

    /// HashSet of user Ids whose commands will be ignored.
    /// Guilds owned by user Ids will also be ignored.
    ///
    /// # Examples
    ///
    /// Create a HashSet in-place:
    ///
    /// ```rust
    /// # use serenity::prelude::*;
    /// # struct Handler;
    /// #
    /// # impl EventHandler for Handler {}
    /// # let mut client = Client::new("token", Handler);
    /// use serenity::model::UserId;
    /// use serenity::framework::StandardFramework;
    ///
    /// client.with_framework(StandardFramework::new().configure(|c| c
    ///     .blocked_users(vec![UserId(7), UserId(77)].into_iter().collect())));
    /// ```
    pub fn blocked_users(mut self, users: HashSet<UserId>) -> Self {
        self.blocked_users = users;

        self
    }

    /// The default depth of the message to check for commands. Defaults to 5.
    /// This determines how "far" into a message to check for a valid command.
    ///
    /// # Examples
    ///
    /// If you set a depth of `1`, and make a command of `"music play"`, but
    /// not a `"music"` command, then the former command will never be
    /// triggered, as its "depth" is `2`.
    pub fn depth(mut self, depth: u8) -> Self {
        self.depth = depth as usize;

        self
    }

    /// HashSet of command names that won't be run.
    ///
    /// # Examples
    ///
    /// Ignore a set of commands, assuming they exist:
    ///
    /// ```rust
    /// # use serenity::prelude::*;
    /// # struct Handler;
    /// #
    /// # impl EventHandler for Handler {}
    /// # let mut client = Client::new("token", Handler);
    /// use serenity::framework::StandardFramework;
    ///
    /// let disabled = vec!["ping"].into_iter().map(|x| x.to_owned()).collect();
    ///
    /// client.with_framework(StandardFramework::new()
    ///     .command("ping", |c| c.exec_str("pong!"))
    ///     .configure(|c| c.disabled_commands(disabled)));
    /// ```
    pub fn disabled_commands(mut self, commands: HashSet<String>) -> Self {
        self.disabled_commands = commands;

        self
    }

    /// Sets the prefix to respond to dynamically based on conditions.
    ///
    /// Return `None` to not have a special prefix for the dispatch, and to
    /// instead use the inherited prefix.
    ///
    /// # Examples
    ///
    /// If the Id of the channel is divisible by 5, return a prefix of `"!"`,
    /// otherwise return a prefix of `"~"`.
    ///
    /// ```rust,no_run
    /// # use serenity::prelude::*;
    /// # struct Handler;
    /// #
    /// # impl EventHandler for Handler {}
    /// # let mut client = Client::new("token", Handler);
    /// use serenity::framework::StandardFramework;
    ///
    /// client.with_framework(StandardFramework::new()
    ///     .command("ping", |c| c.exec_str("Pong!"))
    ///     .configure(|c| c.dynamic_prefix(|_, msg| {
    ///         Some(if msg.channel_id.0 % 5 == 0 {
    ///             "!"
    ///         } else {
    ///             "~"
    ///         }.to_owned())
    ///     })));
    /// ```
    pub fn dynamic_prefix<F>(mut self, dynamic_prefix: F) -> Self
        where F: Fn(&mut Context, &Message) -> Option<String> + Send + Sync + 'static {
        self.dynamic_prefix = Some(Box::new(dynamic_prefix));

        self
    }

    /// Whether the bot should respond to other bots.
    ///
    /// For example, if this is set to false, then the bot will respond to any
    /// other bots including itself.
    pub fn ignore_bots(mut self, ignore_bots: bool) -> Self {
        self.ignore_bots = ignore_bots;

        self
    }

    /// If set to true, bot will ignore all commands called by webhooks.
    /// True by default.
    pub fn ignore_webhooks(mut self, ignore_webhooks: bool) -> Self {
        self.ignore_webhooks = ignore_webhooks;

        self
    }

    /// Whether or not to respond to commands initiated with a mention. Note
    /// that this can be used in conjunction with [`prefix`].
    ///
    /// By default this is set to `false`.
    ///
    /// # Examples
    ///
    /// Setting this to `true` will allow the following types of mentions to be
    /// responded to:
    ///
    /// ```ignore
    /// <@245571012924538880> about
    /// <@!245571012924538880> about
    /// ```
    ///
    /// The former is a direct mention, while the latter is a nickname mention,
    /// which aids mobile devices in determining whether to display a user's
    /// nickname. It has no real meaning for your bot, and the library
    /// encourages you to ignore differentiating between the two.
    ///
    /// [`prefix`]: #method.prefix
    pub fn on_mention(mut self, on_mention: bool) -> Self {
        if !on_mention {
            return self;
        }

        if let Ok(current_user) = http::get_current_user() {
            self.on_mention = Some(vec![
                format!("<@{}>", current_user.id), // Regular mention
                format!("<@!{}>", current_user.id) /* Nickname mention */,
            ]);
        }

        self
    }

    /// A `HashSet` of user Ids checks won't apply to.
    ///
    /// # Examples
    ///
    /// Create a HashSet in-place:
    ///
    /// ```rust
    /// # use serenity::prelude::*;
    /// # struct Handler;
    /// #
    /// # impl EventHandler for Handler {}
    /// # let mut client = Client::new("token", Handler);
    /// use serenity::model::UserId;
    /// use serenity::framework::StandardFramework;
    ///
    /// client.with_framework(StandardFramework::new().configure(|c| c
    ///     .owners(vec![UserId(7), UserId(77)].into_iter().collect())));
    /// ```
    ///
    /// Create a HashSet beforehand:
    ///
    /// ```rust
    /// # use serenity::prelude::*;
    /// # struct Handler;
    /// #
    /// # impl EventHandler for Handler {}
    /// # let mut client = Client::new("token", Handler);
    /// use serenity::model::UserId;
    /// use std::collections::HashSet;
    /// use serenity::framework::StandardFramework;
    ///
    /// let mut set = HashSet::new();
    /// set.insert(UserId(7));
    /// set.insert(UserId(77));
    ///
    /// client.with_framework(StandardFramework::new().configure(|c| c.owners(set)));
    /// ```
    pub fn owners(mut self, user_ids: HashSet<UserId>) -> Self {
        self.owners = user_ids;

        self
    }

    /// Sets the prefix to respond to. A prefix can be a string slice of any
    /// non-zero length.
    ///
    /// # Examples
    ///
    /// Assign a basic prefix:
    ///
    /// ```rust
    /// # use serenity::prelude::*;
    /// # struct Handler;
    /// #
    /// # impl EventHandler for Handler {}
    /// # let mut client = Client::new("token", Handler);
    /// #
    /// use serenity::framework::StandardFramework;
    ///
    /// client.with_framework(StandardFramework::new().configure(|c| c
    ///     .prefix("!")));
    /// ```
    pub fn prefix(mut self, prefix: &str) -> Self {
        self.prefixes = vec![prefix.to_owned()];

        self
    }

    /// Sets the prefixes to respond to. Each can be a string slice of any
    /// non-zero length.
    ///
    /// # Examples
    ///
    /// Assign a set of prefixes the bot can respond to:
    ///
    /// ```rust
    /// # use serenity::prelude::*;
    /// # struct Handler;
    /// #
    /// # impl EventHandler for Handler {}
    /// # let mut client = Client::new("token", Handler);
    /// #
    /// use serenity::framework::StandardFramework;
    ///
    /// client.with_framework(StandardFramework::new().configure(|c| c
    ///     .prefixes(vec!["!", ">", "+"])));
    /// ```
    pub fn prefixes(mut self, prefixes: Vec<&str>) -> Self {
        self.prefixes = prefixes.iter().map(|x| x.to_string()).collect();

        self
    }

    /// Sets a delimiter to be used when splitting the content after a command.
    ///
    /// # Examples
    ///
    /// Have the args be seperated by a comma and a space:
    ///
    /// ```rust
    /// # use serenity::prelude::*;
    /// # struct Handler;
    /// #
    /// # impl EventHandler for Handler {}
    /// # let mut client = Client::new("token", Handler);
    /// #
    /// use serenity::framework::StandardFramework;
    ///
    /// client.with_framework(StandardFramework::new().configure(|c| c
    ///     .delimiter(", ")));
    /// ```
    pub fn delimiter(mut self, delimiter: &str) -> Self {
        self.delimiters.push(delimiter.to_string());

        self
    }

    /// Sets multiple delimiters to be used when splitting the content after a command.
    /// Additionally cleans the default delimiter from the vector.
    ///
    /// # Examples
    ///
    /// Have the args be seperated by a comma and a space; and a regular space:
    ///
    /// ```rust
    /// # use serenity::prelude::*;
    /// # struct Handler;
    /// #
    /// # impl EventHandler for Handler {}
    /// # let mut client = Client::new("token", Handler);
    /// #
    /// use serenity::framework::StandardFramework;
    ///
    /// client.with_framework(StandardFramework::new().configure(|c| c
    ///     .delimiters(vec![", ", " "])));
    /// ```
    pub fn delimiters(mut self, delimiters: Vec<&str>) -> Self {
        self.delimiters.clear();
        self.delimiters.extend(
            delimiters.into_iter().map(|s| s.to_string()),
        );

        self
    }

    /// Whether the framework shouldn't care about the user's input if it's: `~command`, `~Command`, `~COMMAND`.
    /// Setting this to `true` will result in *all* command names to be case insensitive.
    pub fn case_insensitivity(mut self, cs: bool) -> Self {
        self.case_insensitive = cs;

        self
    }
}

impl Default for Configuration {
    /// Builds a default framework configuration, setting the following:
    ///
    /// - **allow_whitespace** to `false`
    /// - **depth** to `5`
    /// - **on_mention** to `false` (basically)
    /// - **prefix** to `None`
    /// - **delimiters** to vec![" "]
    /// - **case_insensitive** to `false`
    fn default() -> Configuration {
        Configuration {
            depth: 5,
            on_mention: None,
            dynamic_prefix: None,
            allow_whitespace: false,
            prefixes: vec![],
            ignore_bots: true,
            owners: HashSet::default(),
            blocked_users: HashSet::default(),
            blocked_guilds: HashSet::default(),
            disabled_commands: HashSet::default(),
            allow_dm: true,
            ignore_webhooks: true,
            case_insensitive: false,
            delimiters: vec![" ".to_string()],
        }
    }
}
<commit_msg>Apply rustfmt fixes<commit_after>use std::collections::HashSet;
use std::default::Default;
use super::command::PrefixCheck;
use client::Context;
use http;
use model::{GuildId, Message, UserId};

/// The configuration to use for a [`Framework`] associated with a [`Client`]
/// instance.
///
/// This allows setting configurations like the depth to search for commands,
/// whether to treat mentions like a command prefix, etc.
///
/// # Examples
///
/// Responding to mentions and setting a command prefix of `"~"`:
///
/// ```rust,no_run
/// # use serenity::prelude::EventHandler;
/// struct Handler;
///
/// impl EventHandler for Handler {}
/// use serenity::Client;
/// use std::env;
/// use serenity::framework::StandardFramework;
///
/// let mut client = Client::new(&env::var("DISCORD_BOT_TOKEN").unwrap(), Handler);
///
/// client.with_framework(StandardFramework::new()
///     .configure(|c| c.on_mention(true).prefix("~")));
/// ```
///
/// [`Client`]: ../../client/struct.Client.html
/// [`Framework`]: struct.Framework.html
pub struct Configuration {
    #[doc(hidden)]
    pub allow_dm: bool,
    #[doc(hidden)]
    pub allow_whitespace: bool,
    #[doc(hidden)]
    pub blocked_guilds: HashSet<GuildId>,
    #[doc(hidden)]
    pub blocked_users: HashSet<UserId>,
    #[doc(hidden)]
    pub depth: usize,
    #[doc(hidden)]
    pub disabled_commands: HashSet<String>,
    #[doc(hidden)]
    pub dynamic_prefix: Option<Box<PrefixCheck>>,
    #[doc(hidden)]
    pub ignore_bots: bool,
    #[doc(hidden)]
    pub ignore_webhooks: bool,
    #[doc(hidden)]
    pub on_mention: Option<Vec<String>>,
    #[doc(hidden)]
    pub owners: HashSet<UserId>,
    #[doc(hidden)]
    pub prefixes: Vec<String>,
    #[doc(hidden)]
    pub delimiters: Vec<String>,
    #[doc(hidden)]
    pub case_insensitive: bool,
}

impl Configuration {
    /// If set to false, bot will ignore any private messages.
    pub fn allow_dm(mut self, allow_dm: bool) -> Self {
        self.allow_dm = allow_dm;

        self
    }

    /// Whether to allow whitespace being optional between a mention/prefix and
    /// a command.
    ///
    /// **Note**: Defaults to `false`.
    ///
    /// # Examples
    ///
    /// Setting this to `false` will _only_ allow this scenario to occur:
    ///
    /// ```ignore
    /// <@245571012924538880> about
    /// !about
    ///
    /// // bot processes and executes the "about" command if it exists
    /// ```
    ///
    /// while setting this to `true` will _also_ allow this scenario to occur:
    ///
    /// ```ignore
    /// <@245571012924538880>about
    /// ! about
    ///
    /// // bot processes and executes the "about" command if it exists
    /// ```
    pub fn allow_whitespace(mut self, allow_whitespace: bool) -> Self {
        self.allow_whitespace = allow_whitespace;

        self
    }

    /// HashSet of guild Ids where commands will be ignored.
    ///
    /// # Examples
    ///
    /// Create a HashSet in-place:
    ///
    /// ```rust
    /// # use serenity::prelude::*;
    /// # struct Handler;
    /// #
    /// # impl EventHandler for Handler {}
    /// # let mut client = Client::new("token", Handler);
    /// use serenity::model::GuildId;
    /// use serenity::framework::StandardFramework;
    ///
    /// client.with_framework(StandardFramework::new().configure(|c| c
    ///     .blocked_guilds(vec![GuildId(7), GuildId(77)].into_iter().collect())));
    /// ```
    pub fn blocked_guilds(mut self, guilds: HashSet<GuildId>) -> Self {
        self.blocked_guilds = guilds;

        self
    }

    /// HashSet of user Ids whose commands will be ignored.
    /// Guilds owned by user Ids will also be ignored.
    ///
    /// # Examples
    ///
    /// Create a HashSet in-place:
    ///
    /// ```rust
    /// # use serenity::prelude::*;
    /// # struct Handler;
    /// #
    /// # impl EventHandler for Handler {}
    /// # let mut client = Client::new("token", Handler);
    /// use serenity::model::UserId;
    /// use serenity::framework::StandardFramework;
    ///
    /// client.with_framework(StandardFramework::new().configure(|c| c
    ///     .blocked_users(vec![UserId(7), UserId(77)].into_iter().collect())));
    /// ```
    pub fn blocked_users(mut self, users: HashSet<UserId>) -> Self {
        self.blocked_users = users;

        self
    }

    /// The default depth of the message to check for commands. Defaults to 5.
    /// This determines how "far" into a message to check for a valid command.
    ///
    /// # Examples
    ///
    /// If you set a depth of `1`, and make a command of `"music play"`, but
    /// not a `"music"` command, then the former command will never be
    /// triggered, as its "depth" is `2`.
    pub fn depth(mut self, depth: u8) -> Self {
        self.depth = depth as usize;

        self
    }

    /// HashSet of command names that won't be run.
    ///
    /// # Examples
    ///
    /// Ignore a set of commands, assuming they exist:
    ///
    /// ```rust
    /// # use serenity::prelude::*;
    /// # struct Handler;
    /// #
    /// # impl EventHandler for Handler {}
    /// # let mut client = Client::new("token", Handler);
    /// use serenity::framework::StandardFramework;
    ///
    /// let disabled = vec!["ping"].into_iter().map(|x| x.to_owned()).collect();
    ///
    /// client.with_framework(StandardFramework::new()
    ///     .command("ping", |c| c.exec_str("pong!"))
    ///     .configure(|c| c.disabled_commands(disabled)));
    /// ```
    pub fn disabled_commands(mut self, commands: HashSet<String>) -> Self {
        self.disabled_commands = commands;

        self
    }

    /// Sets the prefix to respond to dynamically based on conditions.
    ///
    /// Return `None` to not have a special prefix for the dispatch, and to
    /// instead use the inherited prefix.
    ///
    /// # Examples
    ///
    /// If the Id of the channel is divisible by 5, return a prefix of `"!"`,
    /// otherwise return a prefix of `"~"`.
    ///
    /// ```rust,no_run
    /// # use serenity::prelude::*;
    /// # struct Handler;
    /// #
    /// # impl EventHandler for Handler {}
    /// # let mut client = Client::new("token", Handler);
    /// use serenity::framework::StandardFramework;
    ///
    /// client.with_framework(StandardFramework::new()
    ///     .command("ping", |c| c.exec_str("Pong!"))
    ///     .configure(|c| c.dynamic_prefix(|_, msg| {
    ///         Some(if msg.channel_id.0 % 5 == 0 {
    ///             "!"
    ///         } else {
    ///             "~"
    ///         }.to_owned())
    ///     })));
    /// ```
    pub fn dynamic_prefix<F>(mut self, dynamic_prefix: F) -> Self
        where F: Fn(&mut Context, &Message) -> Option<String> + Send + Sync + 'static {
        self.dynamic_prefix = Some(Box::new(dynamic_prefix));

        self
    }

    /// Whether the bot should respond to other bots.
    ///
    /// For example, if this is set to false, then the bot will respond to any
    /// other bots including itself.
    pub fn ignore_bots(mut self, ignore_bots: bool) -> Self {
        self.ignore_bots = ignore_bots;

        self
    }

    /// If set to true, bot will ignore all commands called by webhooks.
    /// True by default.
    pub fn ignore_webhooks(mut self, ignore_webhooks: bool) -> Self {
        self.ignore_webhooks = ignore_webhooks;

        self
    }

    /// Whether or not to respond to commands initiated with a mention. Note
    /// that this can be used in conjunction with [`prefix`].
    ///
    /// By default this is set to `false`.
    ///
    /// # Examples
    ///
    /// Setting this to `true` will allow the following types of mentions to be
    /// responded to:
    ///
    /// ```ignore
    /// <@245571012924538880> about
    /// <@!245571012924538880> about
    /// ```
    ///
    /// The former is a direct mention, while the latter is a nickname mention,
    /// which aids mobile devices in determining whether to display a user's
    /// nickname. It has no real meaning for your bot, and the library
    /// encourages you to ignore differentiating between the two.
    ///
    /// [`prefix`]: #method.prefix
    pub fn on_mention(mut self, on_mention: bool) -> Self {
        if !on_mention {
            return self;
        }

        if let Ok(current_user) = http::get_current_user() {
            self.on_mention = Some(vec![
                format!("<@{}>", current_user.id), // Regular mention
                format!("<@!{}>", current_user.id) /* Nickname mention */,
            ]);
        }

        self
    }

    /// A `HashSet` of user Ids checks won't apply to.
    ///
    /// # Examples
    ///
    /// Create a HashSet in-place:
    ///
    /// ```rust
    /// # use serenity::prelude::*;
    /// # struct Handler;
    /// #
    /// # impl EventHandler for Handler {}
    /// # let mut client = Client::new("token", Handler);
    /// use serenity::model::UserId;
    /// use serenity::framework::StandardFramework;
    ///
    /// client.with_framework(StandardFramework::new().configure(|c| c
    ///     .owners(vec![UserId(7), UserId(77)].into_iter().collect())));
    /// ```
    ///
    /// Create a HashSet beforehand:
    ///
    /// ```rust
    /// # use serenity::prelude::*;
    /// # struct Handler;
    /// #
    /// # impl EventHandler for Handler {}
    /// # let mut client = Client::new("token", Handler);
    /// use serenity::model::UserId;
    /// use std::collections::HashSet;
    /// use serenity::framework::StandardFramework;
    ///
    /// let mut set = HashSet::new();
    /// set.insert(UserId(7));
    /// set.insert(UserId(77));
    ///
    /// client.with_framework(StandardFramework::new().configure(|c| c.owners(set)));
    /// ```
    pub fn owners(mut self, user_ids: HashSet<UserId>) -> Self {
        self.owners = user_ids;

        self
    }

    /// Sets the prefix to respond to. A prefix can be a string slice of any
    /// non-zero length.
    ///
    /// # Examples
    ///
    /// Assign a basic prefix:
    ///
    /// ```rust
    /// # use serenity::prelude::*;
    /// # struct Handler;
    /// #
    /// # impl EventHandler for Handler {}
    /// # let mut client = Client::new("token", Handler);
    /// #
    /// use serenity::framework::StandardFramework;
    ///
    /// client.with_framework(StandardFramework::new().configure(|c| c
    ///     .prefix("!")));
    /// ```
    pub fn prefix(mut self, prefix: &str) -> Self {
        self.prefixes = vec![prefix.to_owned()];

        self
    }

    /// Sets the prefixes to respond to. Each can be a string slice of any
    /// non-zero length.
    ///
    /// # Examples
    ///
    /// Assign a set of prefixes the bot can respond to:
    ///
    /// ```rust
    /// # use serenity::prelude::*;
    /// # struct Handler;
    /// #
    /// # impl EventHandler for Handler {}
    /// # let mut client = Client::new("token", Handler);
    /// #
    /// use serenity::framework::StandardFramework;
    ///
    /// client.with_framework(StandardFramework::new().configure(|c| c
    ///     .prefixes(vec!["!", ">", "+"])));
    /// ```
    pub fn prefixes(mut self, prefixes: Vec<&str>) -> Self {
        self.prefixes = prefixes.iter().map(|x| x.to_string()).collect();

        self
    }

    /// Sets a delimiter to be used when splitting the content after a command.
    ///
    /// # Examples
    ///
    /// Have the args be seperated by a comma and a space:
    ///
    /// ```rust
    /// # use serenity::prelude::*;
    /// # struct Handler;
    /// #
    /// # impl EventHandler for Handler {}
    /// # let mut client = Client::new("token", Handler);
    /// #
    /// use serenity::framework::StandardFramework;
    ///
    /// client.with_framework(StandardFramework::new().configure(|c| c
    ///     .delimiter(", ")));
    /// ```
    pub fn delimiter(mut self, delimiter: &str) -> Self {
        self.delimiters.push(delimiter.to_string());

        self
    }

    /// Sets multiple delimiters to be used when splitting the content after a command.
    /// Additionally cleans the default delimiter from the vector.
    ///
    /// # Examples
    ///
    /// Have the args be seperated by a comma and a space; and a regular space:
    ///
    /// ```rust
    /// # use serenity::prelude::*;
    /// # struct Handler;
    /// #
    /// # impl EventHandler for Handler {}
    /// # let mut client = Client::new("token", Handler);
    /// #
    /// use serenity::framework::StandardFramework;
    ///
    /// client.with_framework(StandardFramework::new().configure(|c| c
    ///     .delimiters(vec![", ", " "])));
    /// ```
    pub fn delimiters(mut self, delimiters: Vec<&str>) -> Self {
        self.delimiters.clear();
        self.delimiters.extend(
            delimiters.into_iter().map(|s| s.to_string()),
        );

        self
    }

    /// Whether the framework shouldn't care about the user's input if it's: `~command`,
    /// `~Command`, `~COMMAND`.
    /// Setting this to `true` will result in *all* command names to be case insensitive.
    pub fn case_insensitivity(mut self, cs: bool) -> Self {
        self.case_insensitive = cs;

        self
    }
}

impl Default for Configuration {
    /// Builds a default framework configuration, setting the following:
    ///
    /// - **allow_whitespace** to `false`
    /// - **depth** to `5`
    /// - **on_mention** to `false` (basically)
    /// - **prefix** to `None`
    /// - **delimiters** to vec![" "]
    /// - **case_insensitive** to `false`
    fn default() -> Configuration {
        Configuration {
            depth: 5,
            on_mention: None,
            dynamic_prefix: None,
            allow_whitespace: false,
            prefixes: vec![],
            ignore_bots: true,
            owners: HashSet::default(),
            blocked_users: HashSet::default(),
            blocked_guilds: HashSet::default(),
            disabled_commands: HashSet::default(),
            allow_dm: true,
            ignore_webhooks: true,
            case_insensitive: false,
            delimiters: vec![" ".to_string()],
        }
    }
}
<|endoftext|>


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

extern crate piston;
// extern crate glfw_game_window;
extern crate sdl2_game_window;
extern crate gfx;
#[phase(plugin)]
extern crate gfx_macros;
extern crate native;
extern crate time;

// use Window = glfw_game_window::GameWindowGLFW;
use Window = sdl2_game_window::GameWindowSDL2;
use gfx::{Device, DeviceHelper};
use piston::GameWindow;

//----------------------------------------
// Cube associated data

#[vertex_format]
struct Vertex {
    #[as_float]
    a_Pos: [i8, ..3],
    #[as_float]
    a_TexCoord: [u8, ..2],
}

impl Vertex {
    fn new(pos: [i8, ..3], tc: [u8, ..2]) -> Vertex {
        Vertex {
            a_Pos: pos,
            a_TexCoord: tc,
        }
    }
}

#[shader_param(Program)]
struct Params {
    u_ModelViewProj: [[f32, ..4], ..4],
    t_Color: gfx::shade::TextureParam,
}

static VERTEX_SRC: gfx::ShaderSource = shaders! {
GLSL_120: b"
    #version 120
    attribute vec3 a_Pos;
    attribute vec2 a_TexCoord;
    varying vec2 v_TexCoord;
    uniform mat4 u_ModelViewProj;
    void main() {
        v_TexCoord = a_TexCoord;
        gl_Position = u_ModelViewProj * vec4(a_Pos, 1.0);
    }
"
GLSL_150: b"
    #version 150 core
    in vec3 a_Pos;
    in vec2 a_TexCoord;
    out vec2 v_TexCoord;
    uniform mat4 u_ModelViewProj;
    void main() {
        v_TexCoord = a_TexCoord;
        gl_Position = u_ModelViewProj * vec4(a_Pos, 1.0);
    }
"
};

static FRAGMENT_SRC: gfx::ShaderSource = shaders! {
GLSL_120: b"
    #version 120
    varying vec2 v_TexCoord;
    uniform sampler2D t_Color;
    void main() {
        vec4 tex = texture2D(t_Color, v_TexCoord);
        float blend = dot(v_TexCoord-vec2(0.5,0.5), v_TexCoord-vec2(0.5,0.5));
        gl_FragColor = mix(tex, vec4(0.0,0.0,0.0,0.0), blend*1.0);
    }
"
GLSL_150: b"
    #version 150 core
    in vec2 v_TexCoord;
    out vec4 o_Color;
    uniform sampler2D t_Color;
    void main() {
        vec4 tex = texture(t_Color, v_TexCoord);
        float blend = dot(v_TexCoord-vec2(0.5,0.5), v_TexCoord-vec2(0.5,0.5));
        o_Color = mix(tex, vec4(0.0,0.0,0.0,0.0), blend*1.0);
    }
"
};

//----------------------------------------

// We need to run on the main thread, so ensure we are using the `native` runtime. This is
// technically not needed, since this is the default, but it's not guaranteed.
#[start]
fn start(argc: int, argv: *const *const u8) -> int {
     native::start(argc, argv, main)
}

fn main() {
    let mut window = Window::new(
        piston::shader_version::opengl::OpenGL_3_2,
        piston::GameWindowSettings {
            title: "cube".to_string(),
            size: [640, 480],
            fullscreen: false,
            exit_on_esc: true
        }
    );

    window.capture_cursor(true);

    let (mut device, frame) = window.gfx();
    let mut list = device.create_draw_list();
    let state = gfx::DrawState::new().depth(gfx::state::LessEqual, true);

    let vertex_data = vec![
        //top (0, 0, 1)
        Vertex::new([-1, -1,  1], [0, 0]),
        Vertex::new([ 1, -1,  1], [1, 0]),
        Vertex::new([ 1,  1,  1], [1, 1]),
        Vertex::new([-1,  1,  1], [0, 1]),
        //bottom (0, 0, -1)
        Vertex::new([ 1,  1, -1], [0, 0]),
        Vertex::new([-1,  1, -1], [1, 0]),
        Vertex::new([-1, -1, -1], [1, 1]),
        Vertex::new([ 1, -1, -1], [0, 1]),
        //right (1, 0, 0)
        Vertex::new([ 1, -1, -1], [0, 0]),
        Vertex::new([ 1,  1, -1], [1, 0]),
        Vertex::new([ 1,  1,  1], [1, 1]),
        Vertex::new([ 1, -1,  1], [0, 1]),
        //left (-1, 0, 0)
        Vertex::new([-1,  1,  1], [0, 0]),
        Vertex::new([-1, -1,  1], [1, 0]),
        Vertex::new([-1, -1, -1], [1, 1]),
        Vertex::new([-1,  1, -1], [0, 1]),
        //front (0, 1, 0)
        Vertex::new([-1,  1, -1], [0, 0]),
        Vertex::new([ 1,  1, -1], [1, 0]),
        Vertex::new([ 1,  1,  1], [1, 1]),
        Vertex::new([-1,  1,  1], [0, 1]),
        //back (0, -1, 0)
        Vertex::new([ 1, -1,  1], [0, 0]),
        Vertex::new([-1, -1,  1], [1, 0]),
        Vertex::new([-1, -1, -1], [1, 1]),
        Vertex::new([ 1, -1, -1], [0, 1]),
    ];

    let mesh = device.create_mesh(vertex_data);

    let slice = {
        let index_data = vec![
            0u8, 1, 2, 2, 3, 0,    //top
            4, 5, 6, 6, 7, 4,       //bottom
            8, 9, 10, 10, 11, 8,    //right
            12, 13, 14, 14, 16, 12, //left
            16, 17, 18, 18, 19, 16, //front
            20, 21, 22, 22, 23, 20, //back
        ];

        let buf = device.create_buffer_static(&index_data);
        gfx::IndexSlice8(buf, 0, 36)
    };

    let tinfo = gfx::tex::TextureInfo {
        width: 1,
        height: 1,
        depth: 1,
        mipmap_range: (0, 1),
        kind: gfx::tex::Texture2D,
        format: gfx::tex::RGBA8,
    };
    let img_info = tinfo.to_image_info();
    let texture = device.create_texture(tinfo).unwrap();
    device.update_texture(
            &texture,
            &img_info,
            &vec![0x20u8, 0xA0u8, 0xC0u8, 0x00u8]
        ).unwrap();

    let sampler = device.create_sampler(
        gfx::tex::SamplerInfo::new(
            gfx::tex::Bilinear,
            gfx::tex::Clamp
        )
    );

    let mut data = Params {
        u_ModelViewProj: piston::vecmath::mat4_id(),
        t_Color: (texture, Some(sampler)),
    };
    let prog: Program = device.link_program(
            VERTEX_SRC.clone(),
            FRAGMENT_SRC.clone()
        ).unwrap();

    let model = piston::vecmath::mat4_id();
    let projection = piston::CameraPerspective {
            fov: 90.0f32,
            near_clip: 0.1,
            far_clip: 1000.0,
            aspect_ratio: 1.0
        }.projection();
    let mut camera = piston::Camera::new(0.5f32, 0.5, 4.0);
    let mut fps_controller = piston::FPSController::new(
        piston::FPSControllerSettings::default()
    );
    camera.set_yaw_pitch(fps_controller.yaw, fps_controller.pitch);

    let mut game_iter = piston::GameIterator::new(
        &mut window,
        &piston::GameIteratorSettings {
            updates_per_second: 120,
            max_frames_per_second: 60
        }
    );

    for e in game_iter {
        match e {
            piston::Render(_args) => {
                list.reset();
                list.clear(
                    gfx::ClearData {
                        color: Some(gfx::Color([0.3, 0.3, 0.3, 1.0])),
                        depth: Some(1.0),
                        stencil: None,
                    },
                    &frame
                );
                data.u_ModelViewProj = piston::model_view_projection(
                        model,
                        camera.orthogonal(),
                        projection
                    );
                list.draw(&mesh, slice, &frame, (&prog, &data), &state).unwrap();
                device.submit(list.as_slice());
            },
            e => fps_controller.event(&e, &mut camera),
        }
    }
}

<commit_msg>Upgraded to latest Piston<commit_after>#![feature(phase)]
#![feature(globs)]
#![crate_name = "cube"]

extern crate piston;
// extern crate glfw_game_window;
extern crate sdl2_game_window;
extern crate gfx;
#[phase(plugin)]
extern crate gfx_macros;
extern crate native;
extern crate time;

// use Window = glfw_game_window::GameWindowGLFW;
use Window = sdl2_game_window::GameWindowSDL2;
use gfx::{Device, DeviceHelper};
use piston::GameWindow;

//----------------------------------------
// Cube associated data

#[vertex_format]
struct Vertex {
    #[as_float]
    a_Pos: [i8, ..3],
    #[as_float]
    a_TexCoord: [u8, ..2],
}

impl Vertex {
    fn new(pos: [i8, ..3], tc: [u8, ..2]) -> Vertex {
        Vertex {
            a_Pos: pos,
            a_TexCoord: tc,
        }
    }
}

#[shader_param(Program)]
struct Params {
    u_ModelViewProj: [[f32, ..4], ..4],
    t_Color: gfx::shade::TextureParam,
}

static VERTEX_SRC: gfx::ShaderSource = shaders! {
GLSL_120: b"
    #version 120
    attribute vec3 a_Pos;
    attribute vec2 a_TexCoord;
    varying vec2 v_TexCoord;
    uniform mat4 u_ModelViewProj;
    void main() {
        v_TexCoord = a_TexCoord;
        gl_Position = u_ModelViewProj * vec4(a_Pos, 1.0);
    }
"
GLSL_150: b"
    #version 150 core
    in vec3 a_Pos;
    in vec2 a_TexCoord;
    out vec2 v_TexCoord;
    uniform mat4 u_ModelViewProj;
    void main() {
        v_TexCoord = a_TexCoord;
        gl_Position = u_ModelViewProj * vec4(a_Pos, 1.0);
    }
"
};

static FRAGMENT_SRC: gfx::ShaderSource = shaders! {
GLSL_120: b"
    #version 120
    varying vec2 v_TexCoord;
    uniform sampler2D t_Color;
    void main() {
        vec4 tex = texture2D(t_Color, v_TexCoord);
        float blend = dot(v_TexCoord-vec2(0.5,0.5), v_TexCoord-vec2(0.5,0.5));
        gl_FragColor = mix(tex, vec4(0.0,0.0,0.0,0.0), blend*1.0);
    }
"
GLSL_150: b"
    #version 150 core
    in vec2 v_TexCoord;
    out vec4 o_Color;
    uniform sampler2D t_Color;
    void main() {
        vec4 tex = texture(t_Color, v_TexCoord);
        float blend = dot(v_TexCoord-vec2(0.5,0.5), v_TexCoord-vec2(0.5,0.5));
        o_Color = mix(tex, vec4(0.0,0.0,0.0,0.0), blend*1.0);
    }
"
};

//----------------------------------------

// We need to run on the main thread, so ensure we are using the `native` runtime. This is
// technically not needed, since this is the default, but it's not guaranteed.
#[start]
fn start(argc: int, argv: *const *const u8) -> int {
     native::start(argc, argv, main)
}

fn main() {
    let mut window = Window::new(
        piston::shader_version::opengl::OpenGL_3_2,
        piston::GameWindowSettings {
            title: "cube".to_string(),
            size: [640, 480],
            fullscreen: false,
            exit_on_esc: true
        }
    );

    window.capture_cursor(true);

    let (mut device, frame) = window.gfx();
    let mut list = device.create_draw_list();
    let state = gfx::DrawState::new().depth(gfx::state::LessEqual, true);

    let vertex_data = vec![
        //top (0, 0, 1)
        Vertex::new([-1, -1,  1], [0, 0]),
        Vertex::new([ 1, -1,  1], [1, 0]),
        Vertex::new([ 1,  1,  1], [1, 1]),
        Vertex::new([-1,  1,  1], [0, 1]),
        //bottom (0, 0, -1)
        Vertex::new([ 1,  1, -1], [0, 0]),
        Vertex::new([-1,  1, -1], [1, 0]),
        Vertex::new([-1, -1, -1], [1, 1]),
        Vertex::new([ 1, -1, -1], [0, 1]),
        //right (1, 0, 0)
        Vertex::new([ 1, -1, -1], [0, 0]),
        Vertex::new([ 1,  1, -1], [1, 0]),
        Vertex::new([ 1,  1,  1], [1, 1]),
        Vertex::new([ 1, -1,  1], [0, 1]),
        //left (-1, 0, 0)
        Vertex::new([-1,  1,  1], [0, 0]),
        Vertex::new([-1, -1,  1], [1, 0]),
        Vertex::new([-1, -1, -1], [1, 1]),
        Vertex::new([-1,  1, -1], [0, 1]),
        //front (0, 1, 0)
        Vertex::new([-1,  1, -1], [0, 0]),
        Vertex::new([ 1,  1, -1], [1, 0]),
        Vertex::new([ 1,  1,  1], [1, 1]),
        Vertex::new([-1,  1,  1], [0, 1]),
        //back (0, -1, 0)
        Vertex::new([ 1, -1,  1], [0, 0]),
        Vertex::new([-1, -1,  1], [1, 0]),
        Vertex::new([-1, -1, -1], [1, 1]),
        Vertex::new([ 1, -1, -1], [0, 1]),
    ];

    let mesh = device.create_mesh(vertex_data);

    let slice = {
        let index_data = vec![
            0u8, 1, 2, 2, 3, 0,    //top
            4, 5, 6, 6, 7, 4,       //bottom
            8, 9, 10, 10, 11, 8,    //right
            12, 13, 14, 14, 16, 12, //left
            16, 17, 18, 18, 19, 16, //front
            20, 21, 22, 22, 23, 20, //back
        ];

        let buf = device.create_buffer_static(&index_data);
        gfx::IndexSlice8(buf, 0, 36)
    };

    let tinfo = gfx::tex::TextureInfo {
        width: 1,
        height: 1,
        depth: 1,
        mipmap_range: (0, 1),
        kind: gfx::tex::Texture2D,
        format: gfx::tex::RGBA8,
    };
    let img_info = tinfo.to_image_info();
    let texture = device.create_texture(tinfo).unwrap();
    device.update_texture(
            &texture,
            &img_info,
            &vec![0x20u8, 0xA0u8, 0xC0u8, 0x00u8]
        ).unwrap();

    let sampler = device.create_sampler(
        gfx::tex::SamplerInfo::new(
            gfx::tex::Bilinear,
            gfx::tex::Clamp
        )
    );

    let mut data = Params {
        u_ModelViewProj: piston::vecmath::mat4_id(),
        t_Color: (texture, Some(sampler)),
    };
    let prog: Program = device.link_program(
            VERTEX_SRC.clone(),
            FRAGMENT_SRC.clone()
        ).unwrap();

    let model = piston::vecmath::mat4_id();
    let projection = piston::CameraPerspective {
            fov: 90.0f32,
            near_clip: 0.1,
            far_clip: 1000.0,
            aspect_ratio: 1.0
        }.projection();
    let mut camera = piston::Camera::new(0.5f32, 0.5, 4.0);
    let mut fps_controller = piston::FPSController::new(
        piston::FPSControllerSettings::default()
    );
    camera.set_yaw_pitch(fps_controller.yaw, fps_controller.pitch);

    let mut game_iter = piston::GameIterator::new(
        &mut window,
        &piston::GameIteratorSettings {
            updates_per_second: 120,
            max_frames_per_second: 60
        }
    );

    for e in game_iter {
        match e {
            piston::Render(_args) => {
                list.reset();
                list.clear(
                    gfx::ClearData {
                        color: Some(gfx::Color([0.3, 0.3, 0.3, 1.0])),
                        depth: Some(1.0),
                        stencil: None,
                    },
                    &frame
                );
                data.u_ModelViewProj = piston::model_view_projection(
                        model,
                        camera.orthogonal(),
                        projection
                    );
                list.draw(&mesh, slice, &frame, (&prog, &data), &state).unwrap();
                device.submit(list.as_slice());
            },
            piston::Update(args) => fps_controller.update(args.dt, &mut camera),
            piston::Input(e) => fps_controller.input(&e, &mut camera),
        }
    }
}

<|endoftext|>


<commit_before>use base::{FromAscii};

trait FromStrHelper: {
    fn signed() -> bool;
}

pub enum ParseIntError {
    Empty,
    InvalidDigit,
    Overflow,
    Underflow,
}

#[inline]
pub fn dec_to_digit(c: u8) -> Option<u8> {
    let val = match c {
        b'0' ... b'9' => c - b'0',
        _ => return None,
    };
    Some(val)
}

macro_rules! from_bytes {
    ($src: expr, $t: ty) => ({
        if $src.is_empty() {
            return Err(ParseIntError::Empty);
        }

        let (is_positive, digits) = match $src[0] {
            b'+' => (true, &$src[1..]),
            b'-' if <$t>::signed() => (false, &$src[1..]),
            _ => (true, $src)
        };

        if digits.is_empty() {
            return Err(ParseIntError::Empty);
        }

        let mut result: $t = 0;

        if is_positive {
            for &c in digits {
                let x = match dec_to_digit(c) {
                    Some(x) => x as $t,
                    None => return Err(ParseIntError::InvalidDigit),
                };
                result = match result.checked_mul(10) {
                    Some(result) => result,
                    None => return Err(ParseIntError::Overflow),
                };
                result = match result.checked_add(x) {
                    Some(result) => result,
                    None => return Err(ParseIntError::Overflow),
                };
            }
        } else {
            for &c in digits {
                let x = match dec_to_digit(c) {
                    Some(x) => x as $t,
                    None => return Err(ParseIntError::InvalidDigit),
                };
                result = match result.checked_mul(10) {
                    Some(result) => result,
                    None => return Err(ParseIntError::Underflow),
                };
                result = match result.checked_sub(x) {
                    Some(result) => result,
                    None => return Err(ParseIntError::Underflow),
                };
            }
        }
        Ok(result)
    })
}

macro_rules! impl_helpers {
    ($t:ty, $signed: expr) => {
        impl FromStrHelper for $t {
            #[inline]
            fn signed() -> bool { $signed }
        }

        impl FromAscii for $t {
            type Err = ParseIntError;

            #[inline]
            fn from_ascii(src: &[u8]) -> Result<Self, Self::Err> {
                from_bytes!(src, $t)
            }
        }
    }
}

impl_helpers!(i8, true);
impl_helpers!(i16, true);
impl_helpers!(i32, true);
impl_helpers!(i64, true);
impl_helpers!(isize, true);
impl_helpers!(u8, false);
impl_helpers!(u16, false);
impl_helpers!(u32, false);
impl_helpers!(u64, false);
impl_helpers!(usize, false);
<commit_msg>Add simple test<commit_after>use base::{FromAscii};

trait FromStrHelper: {
    fn signed() -> bool;
}

#[derive(Debug, PartialEq)]
pub enum ParseIntError {
    Empty,
    InvalidDigit,
    Overflow,
    Underflow,
}

#[inline]
pub fn dec_to_digit(c: u8) -> Option<u8> {
    let val = match c {
        b'0' ... b'9' => c - b'0',
        _ => return None,
    };
    Some(val)
}

macro_rules! from_bytes {
    ($src: expr, $t: ty) => ({
        if $src.is_empty() {
            return Err(ParseIntError::Empty);
        }

        let (is_positive, digits) = match $src[0] {
            b'+' => (true, &$src[1..]),
            b'-' if <$t>::signed() => (false, &$src[1..]),
            _ => (true, $src)
        };

        if digits.is_empty() {
            return Err(ParseIntError::Empty);
        }

        let mut result: $t = 0;

        if is_positive {
            for &c in digits {
                let x = match dec_to_digit(c) {
                    Some(x) => x as $t,
                    None => return Err(ParseIntError::InvalidDigit),
                };
                result = match result.checked_mul(10) {
                    Some(result) => result,
                    None => return Err(ParseIntError::Overflow),
                };
                result = match result.checked_add(x) {
                    Some(result) => result,
                    None => return Err(ParseIntError::Overflow),
                };
            }
        } else {
            for &c in digits {
                let x = match dec_to_digit(c) {
                    Some(x) => x as $t,
                    None => return Err(ParseIntError::InvalidDigit),
                };
                result = match result.checked_mul(10) {
                    Some(result) => result,
                    None => return Err(ParseIntError::Underflow),
                };
                result = match result.checked_sub(x) {
                    Some(result) => result,
                    None => return Err(ParseIntError::Underflow),
                };
            }
        }
        Ok(result)
    })
}

macro_rules! impl_helpers {
    ($t:ty, $signed: expr) => {
        impl FromStrHelper for $t {
            #[inline]
            fn signed() -> bool { $signed }
        }

        impl FromAscii for $t {
            type Err = ParseIntError;

            #[inline]
            fn from_ascii(src: &[u8]) -> Result<Self, Self::Err> {
                from_bytes!(src, $t)
            }
        }
    }
}

impl_helpers!(i8, true);
impl_helpers!(i16, true);
impl_helpers!(i32, true);
impl_helpers!(i64, true);
impl_helpers!(isize, true);
impl_helpers!(u8, false);
impl_helpers!(u16, false);
impl_helpers!(u32, false);
impl_helpers!(u64, false);
impl_helpers!(usize, false);

#[cfg(test)]
mod tests {
    use super::super::base::FromAscii;
    use std::str::{from_utf8};

    #[test]
    fn test() {
        assert_eq!(u64::from_ascii(b"1234567890").ok(), Some(1234567890));
        assert_eq!(from_utf8(b"1234567890").ok().and_then(|x| x.parse::<u64>().ok()), Some(1234567890));
    }

}<|endoftext|>