<commit_before>use crate::utils::{span_lint, span_lint_and_then};
use rustc_ast::ast::{
    Arm, AssocItem, AssocItemKind, Attribute, Block, FnDecl, Ident, Item, ItemKind, Local, MacCall, Pat, PatKind,
};
use rustc_ast::attr;
use rustc_ast::visit::{walk_block, walk_expr, walk_pat, Visitor};
use rustc_lint::{EarlyContext, EarlyLintPass};
use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::source_map::Span;
use rustc_span::symbol::SymbolStr;
use std::cmp::Ordering;

declare_clippy_lint! {
    /// **What it does:** Checks for names that are very similar and thus confusing.
    ///
    /// **Why is this bad?** It's hard to distinguish between names that differ only
    /// by a single character.
    ///
    /// **Known problems:** None?
    ///
    /// **Example:**
    /// ```ignore
    /// let checked_exp = something;
    /// let checked_expr = something_else;
    /// ```
    pub SIMILAR_NAMES,
    pedantic,
    "similarly named items and bindings"
}

declare_clippy_lint! {
    /// **What it does:** Checks for too many variables whose name consists of a
    /// single character.
    ///
    /// **Why is this bad?** It's hard to memorize what a variable means without a
    /// descriptive name.
    ///
    /// **Known problems:** None?
    ///
    /// **Example:**
    /// ```ignore
    /// let (a, b, c, d, e, f, g) = (...);
    /// ```
    pub MANY_SINGLE_CHAR_NAMES,
    style,
    "too many single character bindings"
}

declare_clippy_lint! {
    /// **What it does:** Checks if you have variables whose name consists of just
    /// underscores and digits.
    ///
    /// **Why is this bad?** It's hard to memorize what a variable means without a
    /// descriptive name.
    ///
    /// **Known problems:** None?
    ///
    /// **Example:**
    /// ```rust
    /// let _1 = 1;
    /// let ___1 = 1;
    /// let __1___2 = 11;
    /// ```
    pub JUST_UNDERSCORES_AND_DIGITS,
    style,
    "unclear name"
}

#[derive(Copy, Clone)]
pub struct NonExpressiveNames {
    pub single_char_binding_names_threshold: u64,
}

impl_lint_pass!(NonExpressiveNames => [SIMILAR_NAMES, MANY_SINGLE_CHAR_NAMES, JUST_UNDERSCORES_AND_DIGITS]);

struct ExistingName {
    interned: SymbolStr,
    span: Span,
    len: usize,
    whitelist: &'static [&'static str],
}

struct SimilarNamesLocalVisitor<'a, 'tcx> {
    names: Vec<ExistingName>,
    cx: &'a EarlyContext<'tcx>,
    lint: &'a NonExpressiveNames,

    /// A stack of scopes containing the single-character bindings in each scope.
    single_char_names: Vec<Vec<Ident>>,
}

impl<'a, 'tcx> SimilarNamesLocalVisitor<'a, 'tcx> {
    fn check_single_char_names(&self) {
        let num_single_char_names = self.single_char_names.iter().flatten().count();
        let threshold = self.lint.single_char_binding_names_threshold;
        if num_single_char_names as u64 >= threshold {
            let span = self
                .single_char_names
                .iter()
                .flatten()
                .map(|ident| ident.span)
                .collect::<Vec<_>>();
            span_lint(
                self.cx,
                MANY_SINGLE_CHAR_NAMES,
                span,
                &format!(
                    "{} bindings with single-character names in scope",
                    num_single_char_names
                ),
            );
        }
    }
}

// this list contains lists of names that are allowed to be similar
// the assumption is that no name is ever contained in multiple lists.
#[rustfmt::skip]
const WHITELIST: &[&[&str]] = &[
    &["parsed", "parser"],
    &["lhs", "rhs"],
    &["tx", "rx"],
    &["set", "get"],
    &["args", "arms"],
    &["qpath", "path"],
    &["lit", "lint"],
];

struct SimilarNamesNameVisitor<'a, 'tcx, 'b>(&'b mut SimilarNamesLocalVisitor<'a, 'tcx>);

impl<'a, 'tcx, 'b> Visitor<'tcx> for SimilarNamesNameVisitor<'a, 'tcx, 'b> {
    fn visit_pat(&mut self, pat: &'tcx Pat) {
        match pat.kind {
            PatKind::Ident(_, ident, _) => self.check_ident(ident),
            PatKind::Struct(_, ref fields, _) => {
                for field in fields {
                    if !field.is_shorthand {
                        self.visit_pat(&field.pat);
                    }
                }
            },
            // just go through the first pattern, as either all patterns
            // bind the same bindings or rustc would have errored much earlier
            PatKind::Or(ref pats) => self.visit_pat(&pats[0]),
            _ => walk_pat(self, pat),
        }
    }
    fn visit_mac(&mut self, _mac: &MacCall) {
        // do not check macs
    }
}

#[must_use]
fn get_whitelist(interned_name: &str) -> Option<&'static [&'static str]> {
    for &allow in WHITELIST {
        if whitelisted(interned_name, allow) {
            return Some(allow);
        }
    }
    None
}

#[must_use]
fn whitelisted(interned_name: &str, list: &[&str]) -> bool {
    list.iter()
        .any(|&name| interned_name.starts_with(name) || interned_name.ends_with(name))
}

impl<'a, 'tcx, 'b> SimilarNamesNameVisitor<'a, 'tcx, 'b> {
    fn check_short_ident(&mut self, ident: Ident) {
        // Ignore shadowing
        if self
            .0
            .single_char_names
            .iter()
            .flatten()
            .any(|id| id.name == ident.name)
        {
            return;
        }

        if let Some(scope) = &mut self.0.single_char_names.last_mut() {
            scope.push(ident);
        }
    }

    #[allow(clippy::too_many_lines)]
    fn check_ident(&mut self, ident: Ident) {
        let interned_name = ident.name.as_str();
        if interned_name.chars().any(char::is_uppercase) {
            return;
        }
        if interned_name.chars().all(|c| c.is_digit(10) || c == '_') {
            span_lint(
                self.0.cx,
                JUST_UNDERSCORES_AND_DIGITS,
                ident.span,
                "consider choosing a more descriptive name",
            );
            return;
        }
        let count = interned_name.chars().count();
        if count < 3 {
            if count == 1 {
                self.check_short_ident(ident);
            }
            return;
        }
        for existing_name in &self.0.names {
            if whitelisted(&interned_name, existing_name.whitelist) {
                continue;
            }
            let mut split_at = None;
            match existing_name.len.cmp(&count) {
                Ordering::Greater => {
                    if existing_name.len - count != 1 || levenstein_not_1(&interned_name, &existing_name.interned) {
                        continue;
                    }
                },
                Ordering::Less => {
                    if count - existing_name.len != 1 || levenstein_not_1(&existing_name.interned, &interned_name) {
                        continue;
                    }
                },
                Ordering::Equal => {
                    let mut interned_chars = interned_name.chars();
                    let mut existing_chars = existing_name.interned.chars();
                    let first_i = interned_chars.next().expect("we know we have at least one char");
                    let first_e = existing_chars.next().expect("we know we have at least one char");
                    let eq_or_numeric = |(a, b): (char, char)| a == b || a.is_numeric() && b.is_numeric();

                    if eq_or_numeric((first_i, first_e)) {
                        let last_i = interned_chars.next_back().expect("we know we have at least two chars");
                        let last_e = existing_chars.next_back().expect("we know we have at least two chars");
                        if eq_or_numeric((last_i, last_e)) {
                            if interned_chars
                                .zip(existing_chars)
                                .filter(|&ie| !eq_or_numeric(ie))
                                .count()
                                != 1
                            {
                                continue;
                            }
                        } else {
                            let second_last_i = interned_chars
                                .next_back()
                                .expect("we know we have at least three chars");
                            let second_last_e = existing_chars
                                .next_back()
                                .expect("we know we have at least three chars");
                            if !eq_or_numeric((second_last_i, second_last_e))
                                || second_last_i == '_'
                                || !interned_chars.zip(existing_chars).all(eq_or_numeric)
                            {
                                // allowed similarity foo_x, foo_y
                                // or too many chars differ (foo_x, boo_y) or (foox, booy)
                                continue;
                            }
                            split_at = interned_name.char_indices().rev().next().map(|(i, _)| i);
                        }
                    } else {
                        let second_i = interned_chars.next().expect("we know we have at least two chars");
                        let second_e = existing_chars.next().expect("we know we have at least two chars");
                        if !eq_or_numeric((second_i, second_e))
                            || second_i == '_'
                            || !interned_chars.zip(existing_chars).all(eq_or_numeric)
                        {
                            // allowed similarity x_foo, y_foo
                            // or too many chars differ (x_foo, y_boo) or (xfoo, yboo)
                            continue;
                        }
                        split_at = interned_name.chars().next().map(char::len_utf8);
                    }
                },
            }
            span_lint_and_then(
                self.0.cx,
                SIMILAR_NAMES,
                ident.span,
                "binding's name is too similar to existing binding",
                |diag| {
                    diag.span_note(existing_name.span, "existing binding defined here");
                    if let Some(split) = split_at {
                        diag.span_help(
                            ident.span,
                            &format!(
                                "separate the discriminating character by an \
                                 underscore like: `{}_{}`",
                                &interned_name[..split],
                                &interned_name[split..]
                            ),
                        );
                    }
                },
            );
            return;
        }
        self.0.names.push(ExistingName {
            whitelist: get_whitelist(&interned_name).unwrap_or(&[]),
            interned: interned_name,
            span: ident.span,
            len: count,
        });
    }
}

impl<'a, 'b> SimilarNamesLocalVisitor<'a, 'b> {
    /// ensure scoping rules work
    fn apply<F: for<'c> Fn(&'c mut Self)>(&mut self, f: F) {
        let n = self.names.len();
        let single_char_count = self.single_char_names.len();
        f(self);
        self.names.truncate(n);
        self.single_char_names.truncate(single_char_count);
    }
}

impl<'a, 'tcx> Visitor<'tcx> for SimilarNamesLocalVisitor<'a, 'tcx> {
    fn visit_local(&mut self, local: &'tcx Local) {
        if let Some(ref init) = local.init {
            self.apply(|this| walk_expr(this, &**init));
        }
        // add the pattern after the expression because the bindings aren't available
        // yet in the init
        // expression
        SimilarNamesNameVisitor(self).visit_pat(&*local.pat);
    }
    fn visit_block(&mut self, blk: &'tcx Block) {
        self.single_char_names.push(vec![]);

        self.apply(|this| walk_block(this, blk));

        self.check_single_char_names();
        self.single_char_names.pop();
    }
    fn visit_arm(&mut self, arm: &'tcx Arm) {
        self.single_char_names.push(vec![]);

        self.apply(|this| {
            SimilarNamesNameVisitor(this).visit_pat(&arm.pat);
            this.apply(|this| walk_expr(this, &arm.body));
        });

        self.check_single_char_names();
        self.single_char_names.pop();
    }
    fn visit_item(&mut self, _: &Item) {
        // do not recurse into inner items
    }
    fn visit_mac(&mut self, _mac: &MacCall) {
        // do not check macs
    }
}

impl EarlyLintPass for NonExpressiveNames {
    fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) {
        if let ItemKind::Fn(_, ref sig, _, Some(ref blk)) = item.kind {
            do_check(self, cx, &item.attrs, &sig.decl, blk);
        }
    }

    fn check_impl_item(&mut self, cx: &EarlyContext<'_>, item: &AssocItem) {
        if let AssocItemKind::Fn(_, ref sig, _, Some(ref blk)) = item.kind {
            do_check(self, cx, &item.attrs, &sig.decl, blk);
        }
    }
}

fn do_check(lint: &mut NonExpressiveNames, cx: &EarlyContext<'_>, attrs: &[Attribute], decl: &FnDecl, blk: &Block) {
    if !attr::contains_name(attrs, sym!(test)) {
        let mut visitor = SimilarNamesLocalVisitor {
            names: Vec::new(),
            cx,
            lint,
            single_char_names: vec![vec![]],
        };

        // initialize with function arguments
        for arg in &decl.inputs {
            SimilarNamesNameVisitor(&mut visitor).visit_pat(&arg.pat);
        }
        // walk all other bindings
        walk_block(&mut visitor, blk);

        visitor.check_single_char_names();
    }
}

/// Precondition: `a_name.chars().count() < b_name.chars().count()`.
#[must_use]
fn levenstein_not_1(a_name: &str, b_name: &str) -> bool {
    debug_assert!(a_name.chars().count() < b_name.chars().count());
    let mut a_chars = a_name.chars();
    let mut b_chars = b_name.chars();
    while let (Some(a), Some(b)) = (a_chars.next(), b_chars.next()) {
        if a == b {
            continue;
        }
        if let Some(b2) = b_chars.next() {
            // check if there's just one character inserted
            return a != b2 || a_chars.ne(b_chars);
        } else {
            // tuple
            // ntuple
            return true;
        }
    }
    // for item in items
    true
}
<commit_msg>Make the single char threshold strict inequality<commit_after>use crate::utils::{span_lint, span_lint_and_then};
use rustc_ast::ast::{
    Arm, AssocItem, AssocItemKind, Attribute, Block, FnDecl, Ident, Item, ItemKind, Local, MacCall, Pat, PatKind,
};
use rustc_ast::attr;
use rustc_ast::visit::{walk_block, walk_expr, walk_pat, Visitor};
use rustc_lint::{EarlyContext, EarlyLintPass};
use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::source_map::Span;
use rustc_span::symbol::SymbolStr;
use std::cmp::Ordering;

declare_clippy_lint! {
    /// **What it does:** Checks for names that are very similar and thus confusing.
    ///
    /// **Why is this bad?** It's hard to distinguish between names that differ only
    /// by a single character.
    ///
    /// **Known problems:** None?
    ///
    /// **Example:**
    /// ```ignore
    /// let checked_exp = something;
    /// let checked_expr = something_else;
    /// ```
    pub SIMILAR_NAMES,
    pedantic,
    "similarly named items and bindings"
}

declare_clippy_lint! {
    /// **What it does:** Checks for too many variables whose name consists of a
    /// single character.
    ///
    /// **Why is this bad?** It's hard to memorize what a variable means without a
    /// descriptive name.
    ///
    /// **Known problems:** None?
    ///
    /// **Example:**
    /// ```ignore
    /// let (a, b, c, d, e, f, g) = (...);
    /// ```
    pub MANY_SINGLE_CHAR_NAMES,
    style,
    "too many single character bindings"
}

declare_clippy_lint! {
    /// **What it does:** Checks if you have variables whose name consists of just
    /// underscores and digits.
    ///
    /// **Why is this bad?** It's hard to memorize what a variable means without a
    /// descriptive name.
    ///
    /// **Known problems:** None?
    ///
    /// **Example:**
    /// ```rust
    /// let _1 = 1;
    /// let ___1 = 1;
    /// let __1___2 = 11;
    /// ```
    pub JUST_UNDERSCORES_AND_DIGITS,
    style,
    "unclear name"
}

#[derive(Copy, Clone)]
pub struct NonExpressiveNames {
    pub single_char_binding_names_threshold: u64,
}

impl_lint_pass!(NonExpressiveNames => [SIMILAR_NAMES, MANY_SINGLE_CHAR_NAMES, JUST_UNDERSCORES_AND_DIGITS]);

struct ExistingName {
    interned: SymbolStr,
    span: Span,
    len: usize,
    whitelist: &'static [&'static str],
}

struct SimilarNamesLocalVisitor<'a, 'tcx> {
    names: Vec<ExistingName>,
    cx: &'a EarlyContext<'tcx>,
    lint: &'a NonExpressiveNames,

    /// A stack of scopes containing the single-character bindings in each scope.
    single_char_names: Vec<Vec<Ident>>,
}

impl<'a, 'tcx> SimilarNamesLocalVisitor<'a, 'tcx> {
    fn check_single_char_names(&self) {
        let num_single_char_names = self.single_char_names.iter().flatten().count();
        let threshold = self.lint.single_char_binding_names_threshold;
        if num_single_char_names as u64 > threshold {
            let span = self
                .single_char_names
                .iter()
                .flatten()
                .map(|ident| ident.span)
                .collect::<Vec<_>>();
            span_lint(
                self.cx,
                MANY_SINGLE_CHAR_NAMES,
                span,
                &format!(
                    "{} bindings with single-character names in scope",
                    num_single_char_names
                ),
            );
        }
    }
}

// this list contains lists of names that are allowed to be similar
// the assumption is that no name is ever contained in multiple lists.
#[rustfmt::skip]
const WHITELIST: &[&[&str]] = &[
    &["parsed", "parser"],
    &["lhs", "rhs"],
    &["tx", "rx"],
    &["set", "get"],
    &["args", "arms"],
    &["qpath", "path"],
    &["lit", "lint"],
];

struct SimilarNamesNameVisitor<'a, 'tcx, 'b>(&'b mut SimilarNamesLocalVisitor<'a, 'tcx>);

impl<'a, 'tcx, 'b> Visitor<'tcx> for SimilarNamesNameVisitor<'a, 'tcx, 'b> {
    fn visit_pat(&mut self, pat: &'tcx Pat) {
        match pat.kind {
            PatKind::Ident(_, ident, _) => self.check_ident(ident),
            PatKind::Struct(_, ref fields, _) => {
                for field in fields {
                    if !field.is_shorthand {
                        self.visit_pat(&field.pat);
                    }
                }
            },
            // just go through the first pattern, as either all patterns
            // bind the same bindings or rustc would have errored much earlier
            PatKind::Or(ref pats) => self.visit_pat(&pats[0]),
            _ => walk_pat(self, pat),
        }
    }
    fn visit_mac(&mut self, _mac: &MacCall) {
        // do not check macs
    }
}

#[must_use]
fn get_whitelist(interned_name: &str) -> Option<&'static [&'static str]> {
    for &allow in WHITELIST {
        if whitelisted(interned_name, allow) {
            return Some(allow);
        }
    }
    None
}

#[must_use]
fn whitelisted(interned_name: &str, list: &[&str]) -> bool {
    list.iter()
        .any(|&name| interned_name.starts_with(name) || interned_name.ends_with(name))
}

impl<'a, 'tcx, 'b> SimilarNamesNameVisitor<'a, 'tcx, 'b> {
    fn check_short_ident(&mut self, ident: Ident) {
        // Ignore shadowing
        if self
            .0
            .single_char_names
            .iter()
            .flatten()
            .any(|id| id.name == ident.name)
        {
            return;
        }

        if let Some(scope) = &mut self.0.single_char_names.last_mut() {
            scope.push(ident);
        }
    }

    #[allow(clippy::too_many_lines)]
    fn check_ident(&mut self, ident: Ident) {
        let interned_name = ident.name.as_str();
        if interned_name.chars().any(char::is_uppercase) {
            return;
        }
        if interned_name.chars().all(|c| c.is_digit(10) || c == '_') {
            span_lint(
                self.0.cx,
                JUST_UNDERSCORES_AND_DIGITS,
                ident.span,
                "consider choosing a more descriptive name",
            );
            return;
        }
        let count = interned_name.chars().count();
        if count < 3 {
            if count == 1 {
                self.check_short_ident(ident);
            }
            return;
        }
        for existing_name in &self.0.names {
            if whitelisted(&interned_name, existing_name.whitelist) {
                continue;
            }
            let mut split_at = None;
            match existing_name.len.cmp(&count) {
                Ordering::Greater => {
                    if existing_name.len - count != 1 || levenstein_not_1(&interned_name, &existing_name.interned) {
                        continue;
                    }
                },
                Ordering::Less => {
                    if count - existing_name.len != 1 || levenstein_not_1(&existing_name.interned, &interned_name) {
                        continue;
                    }
                },
                Ordering::Equal => {
                    let mut interned_chars = interned_name.chars();
                    let mut existing_chars = existing_name.interned.chars();
                    let first_i = interned_chars.next().expect("we know we have at least one char");
                    let first_e = existing_chars.next().expect("we know we have at least one char");
                    let eq_or_numeric = |(a, b): (char, char)| a == b || a.is_numeric() && b.is_numeric();

                    if eq_or_numeric((first_i, first_e)) {
                        let last_i = interned_chars.next_back().expect("we know we have at least two chars");
                        let last_e = existing_chars.next_back().expect("we know we have at least two chars");
                        if eq_or_numeric((last_i, last_e)) {
                            if interned_chars
                                .zip(existing_chars)
                                .filter(|&ie| !eq_or_numeric(ie))
                                .count()
                                != 1
                            {
                                continue;
                            }
                        } else {
                            let second_last_i = interned_chars
                                .next_back()
                                .expect("we know we have at least three chars");
                            let second_last_e = existing_chars
                                .next_back()
                                .expect("we know we have at least three chars");
                            if !eq_or_numeric((second_last_i, second_last_e))
                                || second_last_i == '_'
                                || !interned_chars.zip(existing_chars).all(eq_or_numeric)
                            {
                                // allowed similarity foo_x, foo_y
                                // or too many chars differ (foo_x, boo_y) or (foox, booy)
                                continue;
                            }
                            split_at = interned_name.char_indices().rev().next().map(|(i, _)| i);
                        }
                    } else {
                        let second_i = interned_chars.next().expect("we know we have at least two chars");
                        let second_e = existing_chars.next().expect("we know we have at least two chars");
                        if !eq_or_numeric((second_i, second_e))
                            || second_i == '_'
                            || !interned_chars.zip(existing_chars).all(eq_or_numeric)
                        {
                            // allowed similarity x_foo, y_foo
                            // or too many chars differ (x_foo, y_boo) or (xfoo, yboo)
                            continue;
                        }
                        split_at = interned_name.chars().next().map(char::len_utf8);
                    }
                },
            }
            span_lint_and_then(
                self.0.cx,
                SIMILAR_NAMES,
                ident.span,
                "binding's name is too similar to existing binding",
                |diag| {
                    diag.span_note(existing_name.span, "existing binding defined here");
                    if let Some(split) = split_at {
                        diag.span_help(
                            ident.span,
                            &format!(
                                "separate the discriminating character by an \
                                 underscore like: `{}_{}`",
                                &interned_name[..split],
                                &interned_name[split..]
                            ),
                        );
                    }
                },
            );
            return;
        }
        self.0.names.push(ExistingName {
            whitelist: get_whitelist(&interned_name).unwrap_or(&[]),
            interned: interned_name,
            span: ident.span,
            len: count,
        });
    }
}

impl<'a, 'b> SimilarNamesLocalVisitor<'a, 'b> {
    /// ensure scoping rules work
    fn apply<F: for<'c> Fn(&'c mut Self)>(&mut self, f: F) {
        let n = self.names.len();
        let single_char_count = self.single_char_names.len();
        f(self);
        self.names.truncate(n);
        self.single_char_names.truncate(single_char_count);
    }
}

impl<'a, 'tcx> Visitor<'tcx> for SimilarNamesLocalVisitor<'a, 'tcx> {
    fn visit_local(&mut self, local: &'tcx Local) {
        if let Some(ref init) = local.init {
            self.apply(|this| walk_expr(this, &**init));
        }
        // add the pattern after the expression because the bindings aren't available
        // yet in the init
        // expression
        SimilarNamesNameVisitor(self).visit_pat(&*local.pat);
    }
    fn visit_block(&mut self, blk: &'tcx Block) {
        self.single_char_names.push(vec![]);

        self.apply(|this| walk_block(this, blk));

        self.check_single_char_names();
        self.single_char_names.pop();
    }
    fn visit_arm(&mut self, arm: &'tcx Arm) {
        self.single_char_names.push(vec![]);

        self.apply(|this| {
            SimilarNamesNameVisitor(this).visit_pat(&arm.pat);
            this.apply(|this| walk_expr(this, &arm.body));
        });

        self.check_single_char_names();
        self.single_char_names.pop();
    }
    fn visit_item(&mut self, _: &Item) {
        // do not recurse into inner items
    }
    fn visit_mac(&mut self, _mac: &MacCall) {
        // do not check macs
    }
}

impl EarlyLintPass for NonExpressiveNames {
    fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) {
        if let ItemKind::Fn(_, ref sig, _, Some(ref blk)) = item.kind {
            do_check(self, cx, &item.attrs, &sig.decl, blk);
        }
    }

    fn check_impl_item(&mut self, cx: &EarlyContext<'_>, item: &AssocItem) {
        if let AssocItemKind::Fn(_, ref sig, _, Some(ref blk)) = item.kind {
            do_check(self, cx, &item.attrs, &sig.decl, blk);
        }
    }
}

fn do_check(lint: &mut NonExpressiveNames, cx: &EarlyContext<'_>, attrs: &[Attribute], decl: &FnDecl, blk: &Block) {
    if !attr::contains_name(attrs, sym!(test)) {
        let mut visitor = SimilarNamesLocalVisitor {
            names: Vec::new(),
            cx,
            lint,
            single_char_names: vec![vec![]],
        };

        // initialize with function arguments
        for arg in &decl.inputs {
            SimilarNamesNameVisitor(&mut visitor).visit_pat(&arg.pat);
        }
        // walk all other bindings
        walk_block(&mut visitor, blk);

        visitor.check_single_char_names();
    }
}

/// Precondition: `a_name.chars().count() < b_name.chars().count()`.
#[must_use]
fn levenstein_not_1(a_name: &str, b_name: &str) -> bool {
    debug_assert!(a_name.chars().count() < b_name.chars().count());
    let mut a_chars = a_name.chars();
    let mut b_chars = b_name.chars();
    while let (Some(a), Some(b)) = (a_chars.next(), b_chars.next()) {
        if a == b {
            continue;
        }
        if let Some(b2) = b_chars.next() {
            // check if there's just one character inserted
            return a != b2 || a_chars.ne(b_chars);
        } else {
            // tuple
            // ntuple
            return true;
        }
    }
    // for item in items
    true
}
<|endoftext|>


<commit_before>use std::str::FromStr;

pub enum Types {
    NONE: 0;
    A: 1;
    NS: 2;
    MD: 3;
    MF: 4;
    CNAME: 5;
    SOA: 6;
    MB: 7;
    MG: 8;
    MR: 9;
    NULL: 10;
    WKS: 11;
    PTR: 12;
    HINFO: 13;
    MINFO: 14;
    MX: 15;
    TXT: 16;
    RP: 17;
    AFSDB: 18;
    X25: 19;
    ISDN: 20;
    RT: 21;
    NSAP: 22;
    NSAPPTR: 23;
    SIG: 24;
    KEY: 25;
    PX: 26;
    GPOS: 27;
    AAAA: 28;
    LOC: 29;
    NXT: 30;
    EID: 31;
    NIMLOC: 32;
    SRV: 33;
    ATMA: 34;
    NAPTR: 35;
    KX: 36;
    CERT: 37;
    DNAME: 39;
    OPT: 41;
    DS: 43;
    SSHFP: 44;
    IPSECKEY: 45;
    RRSIG: 46;
    NSEC: 47;
    DNSKEY: 48;
    DHCID: 49;
    NSEC3: 50;
    NSEC3PARAM: 51;
    TLSA: 52;
    HIP: 55;
    NINFO: 56;
    RKEY: 57;
    TALINK: 58;
    CDS: 59;
    OPENPGPKEY: 61;
    SPF: 99;
    UINFO: 100;
    UID: 101;
    GID: 102;
    UNSPEC: 103;
    NID: 104;
    L32: 105;
    L64: 106;
    LP: 107;
    EUI48: 108;
    EUI64: 109;
    TKEY: 249;
    TSIG: 250;
    IXFR: 251;
    AXFR: 252;
    MAILB: 253;
    MAILA: 254;
    ANY: 255;
    URI: 256;
    CAA: 257;
    TA: 32768;
    DLV: 32769;
    Reserved: 65535;
}

impl FromStr for Types;

pub enum Class {
    INET: 1,
    CSNET: 2,
    CHAOS: 3,
    HESIOD: 4,
    NONE: 254,
    ANY: 255
}

pub enum Rcode {
    Success: 0,
    FormatError: 1,
    ServerFailure: 2,
    NameError: 3,
    NotImplemented: 4,
    Refused: 5,
    YXDomain: 6,
    YXRrset: 7,
    NXRrset: 8,
    NotAuth: 9,
    NotZone: 10,
    BadSig: 16,
    BadVers: 16,
    BadKey: 17,
    BadTime: 18,
    BadMode: 19,
    BadName: 20,
    BadAlg: 21,
    BadTrunc: 22,
}

pub enum Opcode {
    Query: 0,
    IQuery: 1,
    Status: 2,
    Notify: 4,
    Update: 5,
}

pub enum Certs {
    PKIX: 1,
    SPKI: 2,
    PGP: 3,
    IPKIX: 4,
    ISPKI: 5,
    IPGP: 6,
    ACPKIX: 7,
    IACPKIX: 8,
    URI: 253,
    OID: 254
}

#[cfg(test)]
mod test {
    #[test]
    fn test_test () {
        assert_eq!(Types::from_str("A"), 1);
    }
}<commit_msg>fix errors<commit_after>use std::str::FromStr;

pub enum Types {
    NONE = 0,
    A = 1,
    NS = 2,
    MD = 3,
    MF = 4,
    CNAME = 5,
    SOA = 6,
    MB = 7,
    MG = 8,
    MR = 9,
    NULL = 10,
    WKS = 11,
    PTR = 12,
    HINFO = 13,
    MINFO = 14,
    MX = 15,
    TXT = 16,
    RP = 17,
    AFSDB = 18,
    X25 = 19,
    ISDN = 20,
    RT = 21,
    NSAP = 22,
    NSAPPTR = 23,
    SIG = 24,
    KEY = 25,
    PX = 26,
    GPOS = 27,
    AAAA = 28,
    LOC = 29,
    NXT = 30,
    EID = 31,
    NIMLOC = 32,
    SRV = 33,
    ATMA = 34,
    NAPTR = 35,
    KX = 36,
    CERT = 37,
    DNAME = 39,
    OPT = 41,
    DS = 43,
    SSHFP = 44,
    IPSECKEY = 45,
    RRSIG = 46,
    NSEC = 47,
    DNSKEY = 48,
    DHCID = 49,
    NSEC3 = 50,
    NSEC3PARAM = 51,
    TLSA = 52,
    HIP = 55,
    NINFO = 56,
    RKEY = 57,
    TALINK = 58,
    CDS = 59,
    OPENPGPKEY = 61,
    SPF = 99,
    UINFO = 100,
    UID = 101,
    GID = 102,
    UNSPEC = 103,
    NID = 104,
    L32 = 105,
    L64 = 106,
    LP = 107,
    EUI48 = 108,
    EUI64 = 109,
    TKEY = 249,
    TSIG = 250,
    IXFR = 251,
    AXFR = 252,
    MAILB = 253,
    MAILA = 254,
    ANY = 255,
    URI = 256,
    CAA = 257,
    TA = 32768,
    DLV = 32769,
    Reserved = 65535,
}

// impl FromStr for Types {};

pub enum Class {
    INET = 1,
    CSNET = 2,
    CHAOS = 3,
    HESIOD = 4,
    NONE = 254,
    ANY = 255
}

pub enum Rcode {
    Success = 0,
    FormatError = 1,
    ServerFailure = 2,
    NameError = 3,
    NotImplemented = 4,
    Refused = 5,
    YXDomain = 6,
    YXRrset = 7,
    NXRrset = 8,
    NotAuth = 9,
    NotZone = 10,
    BadSig = 16,  // this
    // BadVers = 16, // ... and this  have the same discriminant
    BadKey = 17,
    BadTime = 18,
    BadMode = 19,
    BadName = 20,
    BadAlg = 21,
    BadTrunc = 22,
}

pub enum Opcode {
    Query = 0,
    IQuery = 1,
    Status = 2,
    Notify = 4,
    Update = 5,
}

pub enum Certs {
    PKIX = 1,
    SPKI = 2,
    PGP = 3,
    IPKIX = 4,
    ISPKI = 5,
    IPGP = 6,
    ACPKIX = 7,
    IACPKIX = 8,
    URI = 253,
    OID = 254
}

#[cfg(test)]
mod test {
    #[test]
    fn test_test () {
    //    assert_eq!(tupes::from_str("A"), 1);
    }
}
<|endoftext|>


<commit_before>use std::iter::Peekable;
use std::str::FromStr;
use std::time::Duration;

use game::{Position, FromTo, White, Black, fen_to_position, start_pos};
use super::types::{Cmd, RegisterParam, GoParam};
use super::types::options;
use types::{NumNodes, NumPlies, NumMoves};

pub fn parse(s: &str, is_debug: &mut bool) -> Option<Cmd> {
    let mut words = s.words().peekable();

    let mut cmd_val: Option<Cmd> = None;

    while cmd_val.is_none() {
        let next_word_opt = words.next();
        let next_word = match next_word_opt {
            Some(val) => val,
            None => break,
        };

        cmd_val = match next_word {
            "uci" => Some(Cmd::Uci),
            "debug" => parse_on_off(&mut words, *is_debug).map(|val| Cmd::Debug(val)),
            "isready" => Some(Cmd::IsReady),
            "setoption" => parse_option_val(&mut words, *is_debug)
                           .map(|(name, val)| Cmd::SetOption(name, Some(val))),
            "register" => parse_register_vec(&mut words, *is_debug).map(|val| Cmd::Register(val)),
            "ucinewgame" => Some(Cmd::UciNewGame),
            "position" => {
                let temp = parse_position(&mut words, *is_debug);
                temp.and_then(|pos_opt| {
                    match parse_move_vec(&mut words, *is_debug) {
                        Some(moves) => Some(Cmd::SetupPosition(pos_opt, moves)),
                        None => None,
                    }
                })
            },
            "go" => parse_go_param_vec(&mut words, *is_debug).map(|val| Cmd::Go(val)),
            "stop" => Some(Cmd::Stop),
            "ponderhit" => Some(Cmd::PonderHit),
            "quit" => Some(Cmd::Quit),
            _ => None,
        }
    }

    if let Some(Cmd::Debug(x)) = cmd_val {
        *is_debug = x;
        debug!("debug is now {:?}", is_debug);
    }

    if *is_debug { debug!("parse() returning {:?}", cmd_val); }

    cmd_val
}

fn parse_on_off(words: &mut Iterator<Item = &str>, is_debug: bool) -> Option<bool> {
    let mut ans = None;
    while ans.is_none() {
        let next_word_opt = words.next();
        let next_word = match next_word_opt {
            Some(val) => val,
            None => break,
        };
        ans = match next_word {
            "on" => Some(true),
            "off" => Some(false),
            _ => None,
        }
    }

    if is_debug { debug!("parse_on_off() returning {:?}", ans); }

    ans
}

fn parse_option_val<'a,T>(words: &mut T, is_debug: bool) -> Option<options::NameAndVal>
where T: Iterator<Item = &'a str> {
    if is_debug { debug!("dummy option parsing"); }
    // TODO parse options.
    Some((options::Name::Dummy, options::Val::Spin(1)))
}

fn parse_register_vec<'a,T>(words: &mut Peekable<&'a str,T>, is_debug: bool) -> Option<Vec<RegisterParam>>
where T: Iterator<Item = &'a str> {
    let mut res = Vec::<RegisterParam>::new();
    while let Some(next_word) = words.next() {
        let register_val = match next_word {
            "later" => Some(RegisterParam::Later),
            "name" => {
                let mut name_vec = Vec::<&str>::new();
                loop {
                    let curr_name = words.peek().and_then( |s| {
                        if ["late","name","code"].contains(s) { None }
                        else { Some(*s) }
                    });
                    match curr_name {
                        Some(val) => {
                            name_vec.push(val);
                            words.next();
                        },
                        None => break,
                    }
                }
                Some(RegisterParam::Name(name_vec.connect(" ")))
            },
            "code" => words.next().map( |x| RegisterParam::Code(x.to_string())),
            _ => None,
        };
        if let Some(val) = register_val {
            res.push(val);
        }
    }
    let ans = if res.is_empty() { None } else { Some(res) };

    if is_debug { debug!("parse_register_vec() returning {:?}", ans); }

    ans
}

fn parse_position<'a,T>(words: &mut Peekable<&'a str,T>, is_debug: bool) -> Option<Position>
where T: Iterator<Item = &'a str> {
    let ans = if words.peek() == Some(&"startpos") {
        words.next();
        Some(start_pos())
    } else {
        let six_words: Vec<_> = words.by_ref().take(6).collect();
        fen_to_position(&*six_words.connect(" ")).ok()
    };

    if is_debug { debug!("parse_position() returning {:?}", ans); }

    ans
}

fn parse_move_vec<'a,T>(words: &mut Peekable<&'a str,T>, is_debug: bool) -> Option<Vec<FromTo>>
where T: Iterator<Item = &'a str> {
    let mut res = Vec::<FromTo>::new();
    while let Some(val) = words.peek().and_then(|val| FromStr::from_str(*val)) {
        res.push(val);
        words.next();
    }
    let ans = if res.is_empty() { None } else { Some(res) };

    if is_debug { debug!("parse_move_vec() returning {:?}", ans); }

    ans
}

fn parse_go_param_vec<'a,T>(words: &mut Peekable<&'a str,T>, is_debug: bool) -> Option<Vec<GoParam>>
where T: Iterator<Item = &'a str> {
    let mut res = Vec::<GoParam>::new();
    while let Some(next_word) = words.next() {
        match next_word {
            "search moves" => parse_move_vec(words, is_debug).map(|x| GoParam::SearchMoves(x)),
            "ponder" => Some(GoParam::Ponder),
            "wtime"     => words.next().and_then(|s| s.parse::<i64>())
                                       .map(|x| GoParam::Time(White,Duration::milliseconds(x))),
            "btime"     => words.next().and_then(|s| s.parse::<i64>())
                                       .map(|x| GoParam::Time(Black,Duration::milliseconds(x))),
            "winc"      => words.next().and_then(|s| s.parse::<i64>())
                                       .map(|x| GoParam::IncTime(White,Duration::milliseconds(x))),
            "binc"      => words.next().and_then(|s| s.parse::<i64>())
                                       .map(|x| GoParam::IncTime(Black,Duration::milliseconds(x))),
            "movestogo" => words.next().and_then(|s| s.parse::<u16>())
                                       .map(|x| GoParam::MovesToGo(NumMoves(x))),
            "depth"     => words.next().and_then(|s| s.parse::<u16>())
                                       .map(|x| GoParam::Depth(NumPlies(x))),
            "nodes"     => words.next().and_then(|s| s.parse::<u64>())
                                       .map(|x| GoParam::Nodes(NumNodes(x))),
            "mate"      => words.next().and_then(|s| s.parse::<u16>())
                                       .map(|x| GoParam::Mate(NumMoves(x))),
            "movetime"  => words.next().and_then(|s| s.parse::<i64>())
                                       .map(|x| GoParam::MoveTime(Duration::milliseconds(x))),
            "infinite" => Some(GoParam::Infinite),
            _ => None,
        }.map(|val| res.push(val));
    }
    let ans = if res.is_empty() { None } else { Some(res) };

    if is_debug { debug!("parse_go_param_vec() returning {:?}", ans); }

    ans
}
<commit_msg>Add detailed debugging for parse_position() and parse_move_vec()<commit_after>use std::iter::Peekable;
use std::str::FromStr;
use std::time::Duration;

use game::{Position, FromTo, White, Black, fen_to_position, start_pos};
use super::types::{Cmd, RegisterParam, GoParam};
use super::types::options;
use types::{NumNodes, NumPlies, NumMoves};

pub fn parse(s: &str, is_debug: &mut bool) -> Option<Cmd> {
    let mut words = s.words().peekable();

    let mut cmd_val: Option<Cmd> = None;

    while cmd_val.is_none() {
        let next_word_opt = words.next();
        let next_word = match next_word_opt {
            Some(val) => val,
            None => break,
        };

        cmd_val = match next_word {
            "uci" => Some(Cmd::Uci),
            "debug" => parse_on_off(&mut words, *is_debug).map(|val| Cmd::Debug(val)),
            "isready" => Some(Cmd::IsReady),
            "setoption" => parse_option_val(&mut words, *is_debug)
                           .map(|(name, val)| Cmd::SetOption(name, Some(val))),
            "register" => parse_register_vec(&mut words, *is_debug).map(|val| Cmd::Register(val)),
            "ucinewgame" => Some(Cmd::UciNewGame),
            "position" => {
                let temp = parse_position(&mut words, *is_debug);
                temp.and_then(|pos_opt| {
                    match parse_move_vec(&mut words, *is_debug) {
                        Some(moves) => Some(Cmd::SetupPosition(pos_opt, moves)),
                        None => None,
                    }
                })
            },
            "go" => parse_go_param_vec(&mut words, *is_debug).map(|val| Cmd::Go(val)),
            "stop" => Some(Cmd::Stop),
            "ponderhit" => Some(Cmd::PonderHit),
            "quit" => Some(Cmd::Quit),
            _ => None,
        }
    }

    if let Some(Cmd::Debug(x)) = cmd_val {
        *is_debug = x;
        debug!("debug is now {:?}", is_debug);
    }

    if *is_debug { debug!("parse() returning {:?}", cmd_val); }

    cmd_val
}

fn parse_on_off(words: &mut Iterator<Item = &str>, is_debug: bool) -> Option<bool> {
    let mut ans = None;
    while ans.is_none() {
        let next_word_opt = words.next();
        let next_word = match next_word_opt {
            Some(val) => val,
            None => break,
        };
        ans = match next_word {
            "on" => Some(true),
            "off" => Some(false),
            _ => None,
        }
    }

    if is_debug { debug!("parse_on_off() returning {:?}", ans); }

    ans
}

fn parse_option_val<'a,T>(words: &mut T, is_debug: bool) -> Option<options::NameAndVal>
where T: Iterator<Item = &'a str> {
    if is_debug { debug!("dummy option parsing"); }
    // TODO parse options.
    Some((options::Name::Dummy, options::Val::Spin(1)))
}

fn parse_register_vec<'a,T>(words: &mut Peekable<&'a str,T>, is_debug: bool) -> Option<Vec<RegisterParam>>
where T: Iterator<Item = &'a str> {
    let mut res = Vec::<RegisterParam>::new();
    while let Some(next_word) = words.next() {
        let register_val = match next_word {
            "later" => Some(RegisterParam::Later),
            "name" => {
                let mut name_vec = Vec::<&str>::new();
                loop {
                    let curr_name = words.peek().and_then( |s| {
                        if ["late","name","code"].contains(s) { None }
                        else { Some(*s) }
                    });
                    match curr_name {
                        Some(val) => {
                            name_vec.push(val);
                            words.next();
                        },
                        None => break,
                    }
                }
                Some(RegisterParam::Name(name_vec.connect(" ")))
            },
            "code" => words.next().map( |x| RegisterParam::Code(x.to_string())),
            _ => None,
        };
        if let Some(val) = register_val {
            res.push(val);
        }
    }
    let ans = if res.is_empty() { None } else { Some(res) };

    if is_debug { debug!("parse_register_vec() returning {:?}", ans); }

    ans
}

fn parse_position<'a,T>(words: &mut Peekable<&'a str,T>, is_debug: bool) -> Option<Position>
where T: Iterator<Item = &'a str> {
    let ans = if words.peek() == Some(&"startpos") {
        if is_debug { debug!("parse_position() consumed \"startpos\""); }
        words.next();
        Some(start_pos())
    } else {
        let six_words: Vec<_> = words.by_ref().take(6).collect();
        if is_debug { debug!("parse_position(): six_words = {:?}", six_words); }
        fen_to_position(&*six_words.connect(" ")).ok()
    };

    if is_debug { debug!("parse_position() returning {:?}", ans); }

    ans
}

fn parse_move_vec<'a,T>(words: &mut Peekable<&'a str,T>, is_debug: bool) -> Option<Vec<FromTo>>
where T: Iterator<Item = &'a str> {
    let mut res = Vec::<FromTo>::new();
    if is_debug { debug!("parse_move_vec() peeked at {:?}", words.peek()); }
    while let Some(val) = words.peek().and_then(|val| FromStr::from_str(*val)) {
        if is_debug { debug!("parse_move_vec(): val = {:?}", val) }
        res.push(val);
        words.next();
        if is_debug { debug!("parse_move_vec() peeked at {:?}", words.peek()); }
    }
    let ans = if res.is_empty() { None } else { Some(res) };

    if is_debug { debug!("parse_move_vec() returning {:?}", ans); }

    ans
}

fn parse_go_param_vec<'a,T>(words: &mut Peekable<&'a str,T>, is_debug: bool) -> Option<Vec<GoParam>>
where T: Iterator<Item = &'a str> {
    let mut res = Vec::<GoParam>::new();
    while let Some(next_word) = words.next() {
        match next_word {
            "search moves" => parse_move_vec(words, is_debug).map(|x| GoParam::SearchMoves(x)),
            "ponder" => Some(GoParam::Ponder),
            "wtime"     => words.next().and_then(|s| s.parse::<i64>())
                                       .map(|x| GoParam::Time(White,Duration::milliseconds(x))),
            "btime"     => words.next().and_then(|s| s.parse::<i64>())
                                       .map(|x| GoParam::Time(Black,Duration::milliseconds(x))),
            "winc"      => words.next().and_then(|s| s.parse::<i64>())
                                       .map(|x| GoParam::IncTime(White,Duration::milliseconds(x))),
            "binc"      => words.next().and_then(|s| s.parse::<i64>())
                                       .map(|x| GoParam::IncTime(Black,Duration::milliseconds(x))),
            "movestogo" => words.next().and_then(|s| s.parse::<u16>())
                                       .map(|x| GoParam::MovesToGo(NumMoves(x))),
            "depth"     => words.next().and_then(|s| s.parse::<u16>())
                                       .map(|x| GoParam::Depth(NumPlies(x))),
            "nodes"     => words.next().and_then(|s| s.parse::<u64>())
                                       .map(|x| GoParam::Nodes(NumNodes(x))),
            "mate"      => words.next().and_then(|s| s.parse::<u16>())
                                       .map(|x| GoParam::Mate(NumMoves(x))),
            "movetime"  => words.next().and_then(|s| s.parse::<i64>())
                                       .map(|x| GoParam::MoveTime(Duration::milliseconds(x))),
            "infinite" => Some(GoParam::Infinite),
            _ => None,
        }.map(|val| res.push(val));
    }
    let ans = if res.is_empty() { None } else { Some(res) };

    if is_debug { debug!("parse_go_param_vec() returning {:?}", ans); }

    ans
}
<|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::Client;
/// use std::env;
///
/// let mut client = Client::new(&env::var("DISCORD_BOT_TOKEN").unwrap());
///
/// client.with_framework(|f| f
///     .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>,
}

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::Client;
    /// # let mut client = Client::new("token");
    /// use serenity::model::GuildId;
    ///
    /// client.with_framework(|f| f.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::Client;
    /// # let mut client = Client::new("token");
    /// use serenity::model::UserId;
    ///
    /// client.with_framework(|f| f.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::Client;
    /// # let mut client = Client::new("token");
    ///
    /// let disabled = vec!["ping"].into_iter().map(|x| x.to_owned()).collect();
    ///
    /// client.with_framework(|f| f
    ///     .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::Client;
    /// #
    /// # let mut client = Client::new("token");
    /// client.with_framework(|f| f
    ///     .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::Client;
    /// # let mut client = Client::new("token");
    /// use serenity::model::UserId;
    ///
    /// client.with_framework(|f| f.configure(|c| c
    ///     .owners(vec![UserId(7), UserId(77)].into_iter().collect())));
    /// ```
    ///
    /// Create a HashSet beforehand:
    ///
    /// ```rust
    /// # use serenity::Client;
    /// # let mut client = Client::new("token");
    /// use serenity::model::UserId;
    /// use std::collections::HashSet;
    ///
    /// let mut set = HashSet::new();
    /// set.insert(UserId(7));
    /// set.insert(UserId(77));
    ///
    /// client.with_framework(|f| f.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::Client;
    /// # let mut client = Client::new("token");
    /// #
    /// client.with_framework(|f| f.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::Client;
    /// # let mut client = Client::new("token");
    /// #
    /// client.with_framework(|f| f.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
    }
}

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`
    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,
        }
    }
}
<commit_msg>Split up long documentation line<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::Client;
/// use std::env;
///
/// let mut client = Client::new(&env::var("DISCORD_BOT_TOKEN").unwrap());
///
/// client.with_framework(|f| f
///     .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>,
}

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::Client;
    /// # let mut client = Client::new("token");
    /// use serenity::model::GuildId;
    ///
    /// client.with_framework(|f| f.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::Client;
    /// # let mut client = Client::new("token");
    /// use serenity::model::UserId;
    ///
    /// client.with_framework(|f| f.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::Client;
    /// # let mut client = Client::new("token");
    ///
    /// let disabled = vec!["ping"].into_iter().map(|x| x.to_owned()).collect();
    ///
    /// client.with_framework(|f| f
    ///     .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::Client;
    /// #
    /// # let mut client = Client::new("token");
    /// client.with_framework(|f| f
    ///     .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::Client;
    /// # let mut client = Client::new("token");
    /// use serenity::model::UserId;
    ///
    /// client.with_framework(|f| f.configure(|c| c
    ///     .owners(vec![UserId(7), UserId(77)].into_iter().collect())));
    /// ```
    ///
    /// Create a HashSet beforehand:
    ///
    /// ```rust
    /// # use serenity::Client;
    /// # let mut client = Client::new("token");
    /// use serenity::model::UserId;
    /// use std::collections::HashSet;
    ///
    /// let mut set = HashSet::new();
    /// set.insert(UserId(7));
    /// set.insert(UserId(77));
    ///
    /// client.with_framework(|f| f.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::Client;
    /// # let mut client = Client::new("token");
    /// #
    /// client.with_framework(|f| f.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::Client;
    /// # let mut client = Client::new("token");
    /// #
    /// client.with_framework(|f| f.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
    }
}

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`
    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,
        }
    }
}
<|endoftext|>


<commit_before>//! Adaptors for object-safe hashing.
//!
//! This module defines a trait [`Hashable`](trait.Hashable.html), which can be
//! implemented identically to `std::hash::Hash` but accepts the hash-state
//! object as a trait object (`state: &Hasher`) and thus requires no type
//! parameters on function `hash`.
//!
//! For this reason, `Hashable` is entirely agnostic to the hash implementation being
//! used; any trait object that specifies `Hashable` as a supertrait, and
//! implements `Hash` in terms of Hashable, can be used may be safely used as
//! a (boxed) type parameter to containers like `HashSet`.
//!
//! ```rust
//! #![feature(hash)]
//! use std::cmp::{Eq,PartialEq};
//! use std::hash::{Hash,Hasher,SipHasher};
//! use std::collections::HashSet;
//! use cripes::util::hash::{hash,Hashable};
//!
//! trait MyInterface: Hashable {}
//! impl Eq for MyInterface {}
//! impl PartialEq<MyInterface> for MyInterface {
//!     fn eq(&self, other: &MyInterface) -> bool {
//!         hash::<_,SipHasher>(self) == hash::<_,SipHasher>(other)
//!     }
//! }
//!
//! impl std::hash::Hash for MyInterface {
//!     fn hash<H: Hasher>(&self, state: &mut H) {
//!         <Self as Hashable>::hash(self, state)
//!     }
//! }
//!
//!
//! #[derive(Eq,PartialEq)]
//! struct MyType(u64, &'static str);
//!
//! impl MyInterface for MyType {}
//! impl Hashable for MyType {
//!     fn hash(&self, state: &mut Hasher) {
//!         state.write_u64(self.0);
//!         state.write(self.1.as_bytes());
//!     }
//! }
//!
//!
//! fn main() {
//!     use std::collections::HashSet;
//!     static value_instance: MyType = MyType(123, "abc");
//!     let mut hs: HashSet<&'static MyInterface> = HashSet::new();
//!     hs.insert(&value_instance);
//! }
//!
//! ```
//!
//! `std::hash::Hash` is not object-safe because trait function `hash` takes
//! a type parameter that specifies the concrete hash implementation used.
use std::default::Default;
use std::hash::{Hash,Hasher};

/// Object-safe hash adaptor.  See the [module-level documentation](index.html)
/// for details.
pub trait Hashable {
    fn hash(&self, state: &mut Hasher);
}

impl Hashable for char {
    #[inline(always)]
    fn hash(&self, state: &mut Hasher) {
        state.write_u32(*self as u32);
    }
}

/// Hash an object using the Hashable interface.
///
///
pub fn hash<T: ?Sized, H>(obj: &T) -> u64 where H: Hasher + Default, T: Hashable {
    let mut state: H = Default::default();
    obj.hash(&mut state);
    state.finish()
}
<commit_msg>util::hash: doc tweaks<commit_after>//! Adaptors for object-safe hashing.
//!
//! `std::hash::Hash` is not object-safe because trait function `hash` takes
//! a type parameter that specifies the concrete hash implementation used.
//!
//! This module defines a trait [`Hashable`](trait.Hashable.html), which can be
//! implemented identically to `std::hash::Hash` but accepts the hash-state
//! object as a trait object (`state: &Hasher`) and thus requires no type
//! parameters on function `hash`.
//!
//! For this reason, `Hashable` is entirely agnostic to the hash implementation being
//! used; any trait object that specifies `Hashable` as a supertrait, and
//! implements `Hash` in terms of Hashable, can be used may be safely used as
//! a (boxed) type parameter to containers like `HashSet`.
//!
//! TL;DR => if you want to hash instances of a type using dynamic dispatch
//! through a trait it implements, you must:
//!
//!
//!   1. ensure that the trait lists `Hashable` as a supertrait, and implements
//!      `std::hash::Hash` in terms of `Hashable`;
//!
//!      ```rust,ignore
//!      trait MyTrait: Hashable {
//!          // ...
//!      }
//!
//!      impl std::hash::Hash for MyTrait {
//!          fn hash<H: Hasher>(&self, state: &mut H) {
//!              <Self as Hashable>::hash(self, state);
//!          }
//!      }
//!      ```
//!
//!      and
//!
//!   2. implement `Hashable` for each concrete type that implements the trait.
//!
//!      ```rust,ignore
//!      impl Hashable for MyType {
//!          fn hash(&self, state: &mut std::hash::Hasher) {
//!              // function body should be identical to that of
//!              // an implementation of `Hash::hash`.
//!          }
//!      }
//!      ```
//!
//!
//! ## Complete Example
//!
//! ```rust
//! #![feature(hash)]
//! use std::cmp::{Eq,PartialEq};
//! use std::hash::{Hash,Hasher,SipHasher};
//! use std::collections::HashSet;
//!
//! use cripes::util::hash::{hash,Hashable};
//!
//! trait MyInterface: Hashable {}
//!
//! impl Eq for MyInterface {}
//! impl PartialEq<MyInterface> for MyInterface {
//!     fn eq(&self, other: &MyInterface) -> bool {
//!         hash::<_,SipHasher>(self) == hash::<_,SipHasher>(other)
//!     }
//! }
//!
//! impl std::hash::Hash for MyInterface {
//!     fn hash<H: Hasher>(&self, state: &mut H) {
//!         <Self as Hashable>::hash(self, state)
//!     }
//! }
//!
//!
//! #[derive(Eq,PartialEq)]
//! struct MyType(u64, &'static str);
//!
//! impl MyInterface for MyType {}
//! impl Hashable for MyType {
//!     fn hash(&self, state: &mut Hasher) {
//!         state.write_u64(self.0);
//!         state.write(self.1.as_bytes());
//!     }
//! }
//!
//! fn main() {
//!     use std::collections::HashSet;
//!
//!     let mut hs: HashSet<Box<MyInterface>> = HashSet::new();
//!     hs.insert(Box::new(MyType(123, "abc")));
//!
//!     // ...
//!
//!     for x in &hs {
//!         println!("{}", std::hash::hash::<_,SipHasher>(x));
//!     }
//! }
//!
//! ```
//!
use std::default::Default;
use std::hash::{Hash,Hasher};

/// Object-safe hash adaptor.  See the [module-level documentation](index.html)
/// for details.
pub trait Hashable {
    fn hash(&self, state: &mut Hasher);
}

/// Hash `char` as a `u32`.
impl Hashable for char {
    #[inline(always)]
    fn hash(&self, state: &mut Hasher) {
        state.write_u32(*self as u32);
    }
}

/// Hash an object by using the Hashable interface directly.
///
///
pub fn hash<T: ?Sized, H>(obj: &T) -> u64 where H: Hasher + Default, T: Hashable {
    let mut state: H = Default::default();
    obj.hash(&mut state);
    state.finish()
}
<|endoftext|>