<commit_before>use std::{
    fmt,
    collections::HashMap,
};

use ra_syntax::{
    SyntaxNodeRef, SyntaxNode, SmolStr, AstNode,
    ast::{self, NameOwner, LoopBodyOwner, ArgListOwner},
    algo::{generate}
};

type ScopeId = usize;

#[derive(Debug)]
pub struct FnScopes {
    pub self_param: Option<SyntaxNode>,
    scopes: Vec<ScopeData>,
    scope_for: HashMap<SyntaxNode, ScopeId>,
}

impl FnScopes {
    pub fn new(fn_def: ast::FnDef) -> FnScopes {
        let mut scopes = FnScopes {
            self_param: fn_def.param_list()
                .and_then(|it| it.self_param())
                .map(|it| it.syntax().owned()),
            scopes: Vec::new(),
            scope_for: HashMap::new()
        };
        let root = scopes.root_scope();
        scopes.add_params_bindings(root, fn_def.param_list());
        if let Some(body) = fn_def.body() {
            compute_block_scopes(body, &mut scopes, root)
        }
        scopes
    }
    pub fn entries(&self, scope: ScopeId) -> &[ScopeEntry] {
        &self.scopes[scope].entries
    }
    pub fn scope_chain<'a>(&'a self, node: SyntaxNodeRef) -> impl Iterator<Item=ScopeId> + 'a {
        generate(self.scope_for(node), move |&scope| self.scopes[scope].parent)
    }
    fn root_scope(&mut self) -> ScopeId {
        let res = self.scopes.len();
        self.scopes.push(ScopeData { parent: None, entries: vec![] });
        res
    }
    fn new_scope(&mut self, parent: ScopeId) -> ScopeId {
        let res = self.scopes.len();
        self.scopes.push(ScopeData { parent: Some(parent), entries: vec![] });
        res
    }
    fn add_bindings(&mut self, scope: ScopeId, pat: ast::Pat) {
        let entries = pat.syntax().descendants()
            .filter_map(ast::BindPat::cast)
            .filter_map(ScopeEntry::new);
        self.scopes[scope].entries.extend(entries);
    }
    fn add_params_bindings(&mut self, scope: ScopeId, params: Option<ast::ParamList>) {
        params.into_iter()
            .flat_map(|it| it.params())
            .filter_map(|it| it.pat())
            .for_each(|it| self.add_bindings(scope, it));
    }
    fn set_scope(&mut self, node: SyntaxNodeRef, scope: ScopeId) {
        self.scope_for.insert(node.owned(), scope);
    }
    fn scope_for(&self, node: SyntaxNodeRef) -> Option<ScopeId> {
        node.ancestors()
            .filter_map(|it| self.scope_for.get(&it.owned()).map(|&scope| scope))
            .next()
    }
}

pub struct ScopeEntry {
    syntax: SyntaxNode
}

impl ScopeEntry {
    fn new(pat: ast::BindPat) -> Option<ScopeEntry> {
        if pat.name().is_some() {
            Some(ScopeEntry { syntax: pat.syntax().owned() })
        } else {
            None
        }
    }
    pub fn name(&self) -> SmolStr {
        self.ast().name()
            .unwrap()
            .text()
    }
    pub fn ast(&self) -> ast::BindPat {
        ast::BindPat::cast(self.syntax.borrowed())
            .unwrap()
    }
}

impl fmt::Debug for ScopeEntry {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.debug_struct("ScopeEntry")
         .field("name", &self.name())
         .field("syntax", &self.syntax)
         .finish()
    }
}

fn compute_block_scopes(block: ast::Block, scopes: &mut FnScopes, mut scope: ScopeId) {
    for stmt in block.statements() {
        match stmt {
            ast::Stmt::LetStmt(stmt) => {
                if let Some(expr) = stmt.initializer() {
                    scopes.set_scope(expr.syntax(), scope);
                    compute_expr_scopes(expr, scopes, scope);
                }
                scope = scopes.new_scope(scope);
                if let Some(pat) = stmt.pat() {
                    scopes.add_bindings(scope, pat);
                }
            }
            ast::Stmt::ExprStmt(expr_stmt) => {
                if let Some(expr) = expr_stmt.expr() {
                    scopes.set_scope(expr.syntax(), scope);
                    compute_expr_scopes(expr, scopes, scope);
                }
            }
        }
    }
    if let Some(expr) = block.expr() {
        scopes.set_scope(expr.syntax(), scope);
        compute_expr_scopes(expr, scopes, scope);
    }
}

fn compute_expr_scopes(expr: ast::Expr, scopes: &mut FnScopes, scope: ScopeId) {
    match expr {
        ast::Expr::IfExpr(e) => {
            let cond_scope = e.condition().and_then(|cond| {
                compute_cond_scopes(cond, scopes, scope)
            });
            if let Some(block) = e.then_branch() {
                compute_block_scopes(block, scopes, cond_scope.unwrap_or(scope));
            }
            if let Some(block) = e.else_branch() {
                compute_block_scopes(block, scopes, scope);
            }
        },
        ast::Expr::BlockExpr(e) => {
            if let Some(block) = e.block() {
                compute_block_scopes(block, scopes, scope);
            }
        }
        ast::Expr::LoopExpr(e) => {
            if let Some(block) = e.loop_body() {
                compute_block_scopes(block, scopes, scope);
            }
        }
        ast::Expr::WhileExpr(e) => {
            let cond_scope = e.condition().and_then(|cond| {
                compute_cond_scopes(cond, scopes, scope)
            });
            if let Some(block) = e.loop_body() {
                compute_block_scopes(block, scopes, cond_scope.unwrap_or(scope));
            }
        }
        ast::Expr::ForExpr(e) => {
            if let Some(expr) = e.iterable() {
                compute_expr_scopes(expr, scopes, scope);
            }
            let mut scope = scope;
            if let Some(pat) = e.pat() {
                scope = scopes.new_scope(scope);
                scopes.add_bindings(scope, pat);
            }
            if let Some(block) = e.loop_body() {
                compute_block_scopes(block, scopes, scope);
            }
        }
        ast::Expr::LambdaExpr(e) => {
            let mut scope = scopes.new_scope(scope);
            scopes.add_params_bindings(scope, e.param_list());
            if let Some(body) = e.body() {
                scopes.set_scope(body.syntax(), scope);
                compute_expr_scopes(body, scopes, scope);
            }
        }
        ast::Expr::CallExpr(e) => {
            compute_call_scopes(e.expr(), e.arg_list(), scopes, scope);
        }
        ast::Expr::MethodCallExpr(e) => {
            compute_call_scopes(e.expr(), e.arg_list(), scopes, scope);
        }
        ast::Expr::MatchExpr(e) => {
            if let Some(expr) = e.expr() {
                compute_expr_scopes(expr, scopes, scope);
            }
            for arm in e.match_arm_list().into_iter().flat_map(|it| it.arms()) {
                let scope = scopes.new_scope(scope);
                for pat in arm.pats() {
                    scopes.add_bindings(scope, pat);
                }
                if let Some(expr) = arm.expr() {
                    compute_expr_scopes(expr, scopes, scope);
                }
            }
        }
        _ => {
            expr.syntax().children()
                .filter_map(ast::Expr::cast)
                .for_each(|expr| compute_expr_scopes(expr, scopes, scope))
        }
    };

    fn compute_call_scopes(
        receiver: Option<ast::Expr>,
        arg_list: Option<ast::ArgList>,
        scopes: &mut FnScopes, scope: ScopeId,
    ) {
        arg_list.into_iter()
            .flat_map(|it| it.args())
            .chain(receiver)
            .for_each(|expr| compute_expr_scopes(expr, scopes, scope));
    }

    fn compute_cond_scopes(cond: ast::Condition, scopes: &mut FnScopes, scope: ScopeId) -> Option<ScopeId> {
        if let Some(expr) = cond.expr() {
            compute_expr_scopes(expr, scopes, scope);
        }
        if let Some(pat) = cond.pat() {
            let s = scopes.new_scope(scope);
            scopes.add_bindings(s, pat);
            Some(s)
        } else {
            None
        }
    }
}

#[derive(Debug)]
struct ScopeData {
    parent: Option<ScopeId>,
    entries: Vec<ScopeEntry>
}

pub fn resolve_local_name<'a>(name_ref: ast::NameRef, scopes: &'a FnScopes) -> Option<&'a ScopeEntry> {
    use std::collections::HashSet;

    let mut shadowed = HashSet::new();
    scopes.scope_chain(name_ref.syntax())
        .flat_map(|scope| scopes.entries(scope).iter())
        .filter(|entry| shadowed.insert(entry.name()))
        .filter(|entry| entry.name() == name_ref.text())
        .nth(0)
}

#[cfg(test)]
mod tests {
    use super::*;
    use ra_syntax::File;
    use {find_node_at_offset, test_utils::extract_offset};

    fn do_check(code: &str, expected: &[&str]) {
        let (off, code) = extract_offset(code);
        let code = {
            let mut buf = String::new();
            let off = u32::from(off) as usize;
            buf.push_str(&code[..off]);
            buf.push_str("marker");
            buf.push_str(&code[off..]);
            buf
        };
        let file = File::parse(&code);
        let marker: ast::PathExpr = find_node_at_offset(file.syntax(), off).unwrap();
        let fn_def: ast::FnDef = find_node_at_offset(file.syntax(), off).unwrap();
        let scopes = FnScopes::new(fn_def);
        let actual = scopes.scope_chain(marker.syntax())
            .flat_map(|scope| scopes.entries(scope))
            .map(|it| it.name())
            .collect::<Vec<_>>();
        assert_eq!(actual.as_slice(), expected);
    }

    #[test]
    fn test_lambda_scope() {
        do_check(r"
            fn quux(foo: i32) {
                let f = |bar, baz: i32| {
                    <|>
                };
            }",
            &["bar", "baz", "foo"],
        );
    }

    #[test]
    fn test_call_scope() {
        do_check(r"
            fn quux() {
                f(|x| <|> );
            }",
            &["x"],
        );
    }

    #[test]
    fn test_metod_call_scope() {
        do_check(r"
            fn quux() {
                z.f(|x| <|> );
            }",
            &["x"],
        );
    }

    #[test]
    fn test_loop_scope() {
        do_check(r"
            fn quux() {
                loop {
                    let x = ();
                    <|>
                };
            }",
            &["x"],
        );
    }

    #[test]
    fn test_match() {
        do_check(r"
            fn quux() {
                match () {
                    Some(x) => {
                        <|>
                    }
                };
            }",
            &["x"],
        );
    }

    #[test]
    fn test_shadow_variable() {
        do_check(r"
            fn foo(x: String) {
                let x : &str = &x<|>;
            }",
            &["x"],
        );
    }

    fn do_check_local_name(code: &str, expected_offset: u32) {
        let (off, code) = extract_offset(code);
        let code = {
            let mut buf = String::new();
            let off = u32::from(off) as usize;
            buf.push_str(&code[..off]);
            buf.push_str(&code[off..]);
            buf
        };

        let file = File::parse(&code);
        let fn_def: ast::FnDef = find_node_at_offset(file.syntax(), off).unwrap();
        let name_ref: ast::NameRef = find_node_at_offset(file.syntax(), off).unwrap();

        let scopes = FnScopes::new(fn_def);

        let local_name = resolve_local_name(name_ref, &scopes).unwrap().ast().name().unwrap();

        let expected_name = find_node_at_offset::<ast::Name>(file.syntax(), expected_offset.into()).unwrap();
        assert_eq!(local_name.syntax().range(), expected_name.syntax().range());
    }

    #[test]
    fn test_resolve_local_name() {
        do_check_local_name(r#"
            fn foo(x: i32, y: u32) {
                {
                    let z = x * 2;
                }
                {
                    let t = x<|> * 3;
                }
            }"#,
            21);
    }

    #[test]
    fn test_resolve_local_name_declaration() {
        do_check_local_name(r#"
            fn foo(x: String) {
                let x : &str = &x<|>;
            }"#,
            21);
    }

    #[test]
    fn test_resolve_local_name_shadow() {
        do_check_local_name(r"
        fn foo(x: String) {
            let x : &str = &x;
            x<|>
        }",
        46);
    }
}<commit_msg>Remove functional noop<commit_after>use std::{
    fmt,
    collections::HashMap,
};

use ra_syntax::{
    SyntaxNodeRef, SyntaxNode, SmolStr, AstNode,
    ast::{self, NameOwner, LoopBodyOwner, ArgListOwner},
    algo::{generate}
};

type ScopeId = usize;

#[derive(Debug)]
pub struct FnScopes {
    pub self_param: Option<SyntaxNode>,
    scopes: Vec<ScopeData>,
    scope_for: HashMap<SyntaxNode, ScopeId>,
}

impl FnScopes {
    pub fn new(fn_def: ast::FnDef) -> FnScopes {
        let mut scopes = FnScopes {
            self_param: fn_def.param_list()
                .and_then(|it| it.self_param())
                .map(|it| it.syntax().owned()),
            scopes: Vec::new(),
            scope_for: HashMap::new()
        };
        let root = scopes.root_scope();
        scopes.add_params_bindings(root, fn_def.param_list());
        if let Some(body) = fn_def.body() {
            compute_block_scopes(body, &mut scopes, root)
        }
        scopes
    }
    pub fn entries(&self, scope: ScopeId) -> &[ScopeEntry] {
        &self.scopes[scope].entries
    }
    pub fn scope_chain<'a>(&'a self, node: SyntaxNodeRef) -> impl Iterator<Item=ScopeId> + 'a {
        generate(self.scope_for(node), move |&scope| self.scopes[scope].parent)
    }
    fn root_scope(&mut self) -> ScopeId {
        let res = self.scopes.len();
        self.scopes.push(ScopeData { parent: None, entries: vec![] });
        res
    }
    fn new_scope(&mut self, parent: ScopeId) -> ScopeId {
        let res = self.scopes.len();
        self.scopes.push(ScopeData { parent: Some(parent), entries: vec![] });
        res
    }
    fn add_bindings(&mut self, scope: ScopeId, pat: ast::Pat) {
        let entries = pat.syntax().descendants()
            .filter_map(ast::BindPat::cast)
            .filter_map(ScopeEntry::new);
        self.scopes[scope].entries.extend(entries);
    }
    fn add_params_bindings(&mut self, scope: ScopeId, params: Option<ast::ParamList>) {
        params.into_iter()
            .flat_map(|it| it.params())
            .filter_map(|it| it.pat())
            .for_each(|it| self.add_bindings(scope, it));
    }
    fn set_scope(&mut self, node: SyntaxNodeRef, scope: ScopeId) {
        self.scope_for.insert(node.owned(), scope);
    }
    fn scope_for(&self, node: SyntaxNodeRef) -> Option<ScopeId> {
        node.ancestors()
            .filter_map(|it| self.scope_for.get(&it.owned()).map(|&scope| scope))
            .next()
    }
}

pub struct ScopeEntry {
    syntax: SyntaxNode
}

impl ScopeEntry {
    fn new(pat: ast::BindPat) -> Option<ScopeEntry> {
        if pat.name().is_some() {
            Some(ScopeEntry { syntax: pat.syntax().owned() })
        } else {
            None
        }
    }
    pub fn name(&self) -> SmolStr {
        self.ast().name()
            .unwrap()
            .text()
    }
    pub fn ast(&self) -> ast::BindPat {
        ast::BindPat::cast(self.syntax.borrowed())
            .unwrap()
    }
}

impl fmt::Debug for ScopeEntry {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.debug_struct("ScopeEntry")
         .field("name", &self.name())
         .field("syntax", &self.syntax)
         .finish()
    }
}

fn compute_block_scopes(block: ast::Block, scopes: &mut FnScopes, mut scope: ScopeId) {
    for stmt in block.statements() {
        match stmt {
            ast::Stmt::LetStmt(stmt) => {
                if let Some(expr) = stmt.initializer() {
                    scopes.set_scope(expr.syntax(), scope);
                    compute_expr_scopes(expr, scopes, scope);
                }
                scope = scopes.new_scope(scope);
                if let Some(pat) = stmt.pat() {
                    scopes.add_bindings(scope, pat);
                }
            }
            ast::Stmt::ExprStmt(expr_stmt) => {
                if let Some(expr) = expr_stmt.expr() {
                    scopes.set_scope(expr.syntax(), scope);
                    compute_expr_scopes(expr, scopes, scope);
                }
            }
        }
    }
    if let Some(expr) = block.expr() {
        scopes.set_scope(expr.syntax(), scope);
        compute_expr_scopes(expr, scopes, scope);
    }
}

fn compute_expr_scopes(expr: ast::Expr, scopes: &mut FnScopes, scope: ScopeId) {
    match expr {
        ast::Expr::IfExpr(e) => {
            let cond_scope = e.condition().and_then(|cond| {
                compute_cond_scopes(cond, scopes, scope)
            });
            if let Some(block) = e.then_branch() {
                compute_block_scopes(block, scopes, cond_scope.unwrap_or(scope));
            }
            if let Some(block) = e.else_branch() {
                compute_block_scopes(block, scopes, scope);
            }
        },
        ast::Expr::BlockExpr(e) => {
            if let Some(block) = e.block() {
                compute_block_scopes(block, scopes, scope);
            }
        }
        ast::Expr::LoopExpr(e) => {
            if let Some(block) = e.loop_body() {
                compute_block_scopes(block, scopes, scope);
            }
        }
        ast::Expr::WhileExpr(e) => {
            let cond_scope = e.condition().and_then(|cond| {
                compute_cond_scopes(cond, scopes, scope)
            });
            if let Some(block) = e.loop_body() {
                compute_block_scopes(block, scopes, cond_scope.unwrap_or(scope));
            }
        }
        ast::Expr::ForExpr(e) => {
            if let Some(expr) = e.iterable() {
                compute_expr_scopes(expr, scopes, scope);
            }
            let mut scope = scope;
            if let Some(pat) = e.pat() {
                scope = scopes.new_scope(scope);
                scopes.add_bindings(scope, pat);
            }
            if let Some(block) = e.loop_body() {
                compute_block_scopes(block, scopes, scope);
            }
        }
        ast::Expr::LambdaExpr(e) => {
            let mut scope = scopes.new_scope(scope);
            scopes.add_params_bindings(scope, e.param_list());
            if let Some(body) = e.body() {
                scopes.set_scope(body.syntax(), scope);
                compute_expr_scopes(body, scopes, scope);
            }
        }
        ast::Expr::CallExpr(e) => {
            compute_call_scopes(e.expr(), e.arg_list(), scopes, scope);
        }
        ast::Expr::MethodCallExpr(e) => {
            compute_call_scopes(e.expr(), e.arg_list(), scopes, scope);
        }
        ast::Expr::MatchExpr(e) => {
            if let Some(expr) = e.expr() {
                compute_expr_scopes(expr, scopes, scope);
            }
            for arm in e.match_arm_list().into_iter().flat_map(|it| it.arms()) {
                let scope = scopes.new_scope(scope);
                for pat in arm.pats() {
                    scopes.add_bindings(scope, pat);
                }
                if let Some(expr) = arm.expr() {
                    compute_expr_scopes(expr, scopes, scope);
                }
            }
        }
        _ => {
            expr.syntax().children()
                .filter_map(ast::Expr::cast)
                .for_each(|expr| compute_expr_scopes(expr, scopes, scope))
        }
    };

    fn compute_call_scopes(
        receiver: Option<ast::Expr>,
        arg_list: Option<ast::ArgList>,
        scopes: &mut FnScopes, scope: ScopeId,
    ) {
        arg_list.into_iter()
            .flat_map(|it| it.args())
            .chain(receiver)
            .for_each(|expr| compute_expr_scopes(expr, scopes, scope));
    }

    fn compute_cond_scopes(cond: ast::Condition, scopes: &mut FnScopes, scope: ScopeId) -> Option<ScopeId> {
        if let Some(expr) = cond.expr() {
            compute_expr_scopes(expr, scopes, scope);
        }
        if let Some(pat) = cond.pat() {
            let s = scopes.new_scope(scope);
            scopes.add_bindings(s, pat);
            Some(s)
        } else {
            None
        }
    }
}

#[derive(Debug)]
struct ScopeData {
    parent: Option<ScopeId>,
    entries: Vec<ScopeEntry>
}

pub fn resolve_local_name<'a>(name_ref: ast::NameRef, scopes: &'a FnScopes) -> Option<&'a ScopeEntry> {
    use std::collections::HashSet;

    let mut shadowed = HashSet::new();
    scopes.scope_chain(name_ref.syntax())
        .flat_map(|scope| scopes.entries(scope).iter())
        .filter(|entry| shadowed.insert(entry.name()))
        .filter(|entry| entry.name() == name_ref.text())
        .nth(0)
}

#[cfg(test)]
mod tests {
    use super::*;
    use ra_syntax::File;
    use {find_node_at_offset, test_utils::extract_offset};

    fn do_check(code: &str, expected: &[&str]) {
        let (off, code) = extract_offset(code);
        let code = {
            let mut buf = String::new();
            let off = u32::from(off) as usize;
            buf.push_str(&code[..off]);
            buf.push_str("marker");
            buf.push_str(&code[off..]);
            buf
        };
        let file = File::parse(&code);
        let marker: ast::PathExpr = find_node_at_offset(file.syntax(), off).unwrap();
        let fn_def: ast::FnDef = find_node_at_offset(file.syntax(), off).unwrap();
        let scopes = FnScopes::new(fn_def);
        let actual = scopes.scope_chain(marker.syntax())
            .flat_map(|scope| scopes.entries(scope))
            .map(|it| it.name())
            .collect::<Vec<_>>();
        assert_eq!(actual.as_slice(), expected);
    }

    #[test]
    fn test_lambda_scope() {
        do_check(r"
            fn quux(foo: i32) {
                let f = |bar, baz: i32| {
                    <|>
                };
            }",
            &["bar", "baz", "foo"],
        );
    }

    #[test]
    fn test_call_scope() {
        do_check(r"
            fn quux() {
                f(|x| <|> );
            }",
            &["x"],
        );
    }

    #[test]
    fn test_metod_call_scope() {
        do_check(r"
            fn quux() {
                z.f(|x| <|> );
            }",
            &["x"],
        );
    }

    #[test]
    fn test_loop_scope() {
        do_check(r"
            fn quux() {
                loop {
                    let x = ();
                    <|>
                };
            }",
            &["x"],
        );
    }

    #[test]
    fn test_match() {
        do_check(r"
            fn quux() {
                match () {
                    Some(x) => {
                        <|>
                    }
                };
            }",
            &["x"],
        );
    }

    #[test]
    fn test_shadow_variable() {
        do_check(r"
            fn foo(x: String) {
                let x : &str = &x<|>;
            }",
            &["x"],
        );
    }

    fn do_check_local_name(code: &str, expected_offset: u32) {
        let (off, code) = extract_offset(code);
        let file = File::parse(&code);
        let fn_def: ast::FnDef = find_node_at_offset(file.syntax(), off).unwrap();
        let name_ref: ast::NameRef = find_node_at_offset(file.syntax(), off).unwrap();

        let scopes = FnScopes::new(fn_def);

        let local_name = resolve_local_name(name_ref, &scopes).unwrap().ast().name().unwrap();

        let expected_name = find_node_at_offset::<ast::Name>(file.syntax(), expected_offset.into()).unwrap();
        assert_eq!(local_name.syntax().range(), expected_name.syntax().range());
    }

    #[test]
    fn test_resolve_local_name() {
        do_check_local_name(r#"
            fn foo(x: i32, y: u32) {
                {
                    let z = x * 2;
                }
                {
                    let t = x<|> * 3;
                }
            }"#,
            21);
    }

    #[test]
    fn test_resolve_local_name_declaration() {
        do_check_local_name(r#"
            fn foo(x: String) {
                let x : &str = &x<|>;
            }"#,
            21);
    }

    #[test]
    fn test_resolve_local_name_shadow() {
        do_check_local_name(r"
        fn foo(x: String) {
            let x : &str = &x;
            x<|>
        }",
        46);
    }
}<|endoftext|>


<commit_before>#[derive(Copy, Clone, PartialEq, PartialOrd)]
struct CacheTableEntry<T: Copy + Clone + PartialEq + PartialOrd> {
    hash: u64,
    entry: T,
}

/// Store a cache of entries, each with an associated hash.
pub struct CacheTable<T: Copy + Clone + PartialEq + PartialOrd> {
    table: Box<[CacheTableEntry<T>]>,
    mask: usize,
}

impl<T: Copy + Clone + PartialEq + PartialOrd> CacheTable<T> {
    /// Create a new `CacheTable` with each associated entry initialized with a hash of '0'
    /// Note: You must pass in a size where only 1 bit is set. (AKA: 2, 4, 8, 16, 1024, 65536,
    /// etc.)
    /// Panics when size is invalid.
    #[inline]
    pub fn new(size: usize, default: T) -> CacheTable<T> {
        if size.count_ones() != 1 {
            panic!("You cannot create a CacheTable with a non-binary number.");
        }
        let values = vec![
            CacheTableEntry {
                hash: 0,
                entry: default
            };
            size
        ];
        CacheTable {
            table: values.into_boxed_slice(),
            mask: size - 1,
        }
    }

    /// Get a particular entry with the hash specified
    #[inline]
    pub fn get(&self, hash: u64) -> Option<T> {
        let entry = unsafe { *self.table.get_unchecked((hash as usize) & self.mask) };
        if entry.hash == hash {
            Some(entry.entry)
        } else {
            None
        }
    }

    /// Add (or overwrite) an entry with the associated hash
    #[inline]
    pub fn add(&mut self, hash: u64, entry: T) {
        let e = unsafe { self.table.get_unchecked_mut((hash as usize) & self.mask) };
        *e = CacheTableEntry {
            hash: hash,
            entry: entry,
        };
    }
}
<commit_msg>Support custom eviction policies in CacheTable<commit_after>#[derive(Copy, Clone, PartialEq, PartialOrd)]
struct CacheTableEntry<T: Copy + Clone + PartialEq + PartialOrd> {
    hash: u64,
    entry: T,
}

/// Store a cache of entries, each with an associated hash.
pub struct CacheTable<T: Copy + Clone + PartialEq + PartialOrd> {
    table: Box<[CacheTableEntry<T>]>,
    mask: usize,
}

impl<T: Copy + Clone + PartialEq + PartialOrd> CacheTable<T> {
    /// Create a new `CacheTable` with each associated entry initialized with a hash of '0'
    /// Note: You must pass in a size where only 1 bit is set. (AKA: 2, 4, 8, 16, 1024, 65536,
    /// etc.)
    /// Panics when size is invalid.
    #[inline]
    pub fn new(size: usize, default: T) -> CacheTable<T> {
        if size.count_ones() != 1 {
            panic!("You cannot create a CacheTable with a non-binary number.");
        }
        let values = vec![
            CacheTableEntry {
                hash: 0,
                entry: default
            };
            size
        ];
        CacheTable {
            table: values.into_boxed_slice(),
            mask: size - 1,
        }
    }

    /// Get a particular entry with the hash specified
    #[inline]
    pub fn get(&self, hash: u64) -> Option<T> {
        let entry = unsafe { *self.table.get_unchecked((hash as usize) & self.mask) };
        if entry.hash == hash {
            Some(entry.entry)
        } else {
            None
        }
    }

    /// Add (or overwrite) an entry with the associated hash
    #[inline]
    pub fn add(&mut self, hash: u64, entry: T) {
        let e = unsafe { self.table.get_unchecked_mut((hash as usize) & self.mask) };
        *e = CacheTableEntry {
            hash: hash,
            entry: entry,
        };
    }

    /// Replace an entry in the hash table with a user-specified replacement policy specified by
    /// `replace`. The `replace` closure is called with the previous entry occupying the hash table
    /// slot, and returns true or false to specify whether the entry should be replaced. Note that
    /// the previous entry may not have the same hash, but merely be the default initialization or
    /// a hash collision with `hash`.
    ///
    /// ```
    /// use chess::CacheTable;
    ///
    /// # fn main() {
    ///
    /// let mut table: CacheTable<char> = CacheTable::new(256, 'a');
    ///
    /// assert_eq!(table.get(5), None);
    /// // Note that 'a' is the default initialization value.
    /// table.replace_if(5, 'b', |old_entry| old_entry != 'a');
    /// assert_eq!(table.get(5), None);
    /// table.replace_if(5, 'c', |old_entry| old_entry == 'a');
    /// assert_eq!(table.get(5), Some('c'));
    /// table.replace_if(5, 'd', |old_entry| old_entry == 'c');
    /// assert_eq!(table.get(5), Some('d'));
    ///
    /// # }
    /// ```
    #[inline(always)]
    pub fn replace_if<F: Fn(T) -> bool>(&mut self, hash: u64, entry: T, replace: F) {
        let e = unsafe { self.table.get_unchecked_mut((hash as usize) & self.mask) };
        if replace(e.entry) {
            *e = CacheTableEntry {
                hash: hash,
                entry: entry,
            };
        }
    }
}
<|endoftext|>


<commit_before>//! Tests for the -Zrustdoc-map feature.

use cargo_test_support::registry::{self, Package};
use cargo_test_support::{is_nightly, paths, project, Project};

fn basic_project() -> Project {
    Package::new("bar", "1.0.0")
        .file("src/lib.rs", "pub struct Straw;")
        .publish();

    project()
        .file(
            "Cargo.toml",
            r#"
                [package]
                name = "foo"
                version = "0.1.0"
                edition = "2018"

                [dependencies]
                bar = "1.0"
            "#,
        )
        .file(
            "src/lib.rs",
            r#"
                pub fn myfun() -> Option<bar::Straw> {
                    None
                }
            "#,
        )
        .build()
}

#[cargo_test]
fn ignores_on_stable() {
    // Requires -Zrustdoc-map to use.
    let p = basic_project();
    p.cargo("doc -v --no-deps")
        .with_stderr_does_not_contain("[..]--extern-html-root-url[..]")
        .run();
}

#[cargo_test]
fn simple() {
    // Basic test that it works with crates.io.
    if !is_nightly() {
        // --extern-html-root-url is unstable
        return;
    }
    let p = basic_project();
    p.cargo("doc -v --no-deps -Zrustdoc-map")
        .masquerade_as_nightly_cargo()
        .with_stderr_contains(
            "[RUNNING] `rustdoc [..]--crate-name foo [..]bar=https://docs.rs/bar/1.0.0/[..]",
        )
        .run();
    let myfun = p.read_file("target/doc/foo/fn.myfun.html");
    assert!(myfun.contains(r#"href="https://docs.rs/bar/1.0.0/bar/struct.Straw.html""#));
}

#[cargo_test]
fn std_docs() {
    // Mapping std docs somewhere else.
    if !is_nightly() {
        // --extern-html-root-url is unstable
        return;
    }
    // For local developers, skip this test if docs aren't installed.
    let docs = std::path::Path::new(&paths::sysroot()).join("share/doc/rust/html");
    if !docs.exists() {
        if cargo_util::is_ci() {
            panic!("std docs are not installed, check that the rust-docs component is installed");
        } else {
            eprintln!(
                "documentation not found at {}, \
                skipping test (run `rustdoc component add rust-docs` to install",
                docs.display()
            );
            return;
        }
    }
    let p = basic_project();
    p.change_file(
        ".cargo/config",
        r#"
            [doc.extern-map]
            std = "local"
        "#,
    );
    p.cargo("doc -v --no-deps -Zrustdoc-map")
        .masquerade_as_nightly_cargo()
        .with_stderr_contains("[RUNNING] `rustdoc [..]--crate-name foo [..]std=file://[..]")
        .run();
    let myfun = p.read_file("target/doc/foo/fn.myfun.html");
    assert!(myfun.contains(r#"share/doc/rust/html/core/option/enum.Option.html""#));

    p.change_file(
        ".cargo/config",
        r#"
            [doc.extern-map]
            std = "https://example.com/rust/"
        "#,
    );
    p.cargo("doc -v --no-deps -Zrustdoc-map")
        .masquerade_as_nightly_cargo()
        .with_stderr_contains(
            "[RUNNING] `rustdoc [..]--crate-name foo [..]std=https://example.com/rust/[..]",
        )
        .run();
    let myfun = p.read_file("target/doc/foo/fn.myfun.html");
    assert!(myfun.contains(r#"href="https://example.com/rust/core/option/enum.Option.html""#));
}

#[cargo_test]
fn renamed_dep() {
    // Handles renamed dependencies.
    if !is_nightly() {
        // --extern-html-root-url is unstable
        return;
    }
    Package::new("bar", "1.0.0")
        .file("src/lib.rs", "pub struct Straw;")
        .publish();

    let p = project()
        .file(
            "Cargo.toml",
            r#"
                [package]
                name = "foo"
                version = "0.1.0"
                edition = "2018"

                [dependencies]
                groovy = { version = "1.0", package = "bar" }
            "#,
        )
        .file(
            "src/lib.rs",
            r#"
                pub fn myfun() -> Option<groovy::Straw> {
                    None
                }
            "#,
        )
        .build();
    p.cargo("doc -v --no-deps -Zrustdoc-map")
        .masquerade_as_nightly_cargo()
        .with_stderr_contains(
            "[RUNNING] `rustdoc [..]--crate-name foo [..]bar=https://docs.rs/bar/1.0.0/[..]",
        )
        .run();
    let myfun = p.read_file("target/doc/foo/fn.myfun.html");
    assert!(myfun.contains(r#"href="https://docs.rs/bar/1.0.0/bar/struct.Straw.html""#));
}

#[cargo_test]
fn lib_name() {
    // Handles lib name != package name.
    if !is_nightly() {
        // --extern-html-root-url is unstable
        return;
    }
    Package::new("bar", "1.0.0")
        .file(
            "Cargo.toml",
            r#"
                [package]
                name = "bar"
                version = "1.0.0"

                [lib]
                name = "rumpelstiltskin"
            "#,
        )
        .file("src/lib.rs", "pub struct Straw;")
        .publish();

    let p = project()
        .file(
            "Cargo.toml",
            r#"
                [package]
                name = "foo"
                version = "0.1.0"

                [dependencies]
                bar = "1.0"
            "#,
        )
        .file(
            "src/lib.rs",
            r#"
                pub fn myfun() -> Option<rumpelstiltskin::Straw> {
                    None
                }
            "#,
        )
        .build();
    p.cargo("doc -v --no-deps -Zrustdoc-map")
        .masquerade_as_nightly_cargo()
        .with_stderr_contains(
            "[RUNNING] `rustdoc [..]--crate-name foo [..]rumpelstiltskin=https://docs.rs/bar/1.0.0/[..]",
        )
        .run();
    let myfun = p.read_file("target/doc/foo/fn.myfun.html");
    assert!(myfun.contains(r#"href="https://docs.rs/bar/1.0.0/rumpelstiltskin/struct.Straw.html""#));
}

#[cargo_test]
fn alt_registry() {
    // Supports other registry names.
    if !is_nightly() {
        // --extern-html-root-url is unstable
        return;
    }
    registry::alt_init();
    Package::new("bar", "1.0.0")
        .alternative(true)
        .file(
            "src/lib.rs",
            r#"
                extern crate baz;
                pub struct Queen;
                pub use baz::King;
            "#,
        )
        .registry_dep("baz", "1.0")
        .publish();
    Package::new("baz", "1.0.0")
        .alternative(true)
        .file("src/lib.rs", "pub struct King;")
        .publish();
    Package::new("grimm", "1.0.0")
        .file("src/lib.rs", "pub struct Gold;")
        .publish();

    let p = project()
        .file(
            "Cargo.toml",
            r#"
                [package]
                name = "foo"
                version = "0.1.0"
                edition = "2018"

                [dependencies]
                bar = { version = "1.0", registry="alternative" }
                grimm = "1.0"
            "#,
        )
        .file(
            "src/lib.rs",
            r#"
                pub fn queen() -> bar::Queen { bar::Queen }
                pub fn king() -> bar::King { bar::King }
                pub fn gold() -> grimm::Gold { grimm::Gold }
            "#,
        )
        .file(
            ".cargo/config",
            r#"
                [doc.extern-map.registries]
                alternative = "https://example.com/{pkg_name}/{version}/"
                crates-io = "https://docs.rs/"
            "#,
        )
        .build();
    p.cargo("doc -v --no-deps -Zrustdoc-map")
        .masquerade_as_nightly_cargo()
        .with_stderr_contains(
            "[RUNNING] `rustdoc [..]--crate-name foo \
            [..]bar=https://example.com/bar/1.0.0/[..]grimm=https://docs.rs/grimm/1.0.0/[..]",
        )
        .run();
    let queen = p.read_file("target/doc/foo/fn.queen.html");
    assert!(queen.contains(r#"href="https://example.com/bar/1.0.0/bar/struct.Queen.html""#));
    // The king example fails to link. Rustdoc seems to want the origin crate
    // name (baz) for re-exports. There are many issues in the issue tracker
    // for rustdoc re-exports, so I'm not sure, but I think this is maybe a
    // rustdoc issue. Alternatively, Cargo could provide mappings for all
    // transitive dependencies to fix this.
    let king = p.read_file("target/doc/foo/fn.king.html");
    assert!(king.contains(r#"-> King"#));

    let gold = p.read_file("target/doc/foo/fn.gold.html");
    assert!(gold.contains(r#"href="https://docs.rs/grimm/1.0.0/grimm/struct.Gold.html""#));
}

#[cargo_test]
fn multiple_versions() {
    // What happens when there are multiple versions.
    // NOTE: This is currently broken behavior. Rustdoc does not provide a way
    // to match renamed dependencies.
    if !is_nightly() {
        // --extern-html-root-url is unstable
        return;
    }
    Package::new("bar", "1.0.0")
        .file("src/lib.rs", "pub struct Spin;")
        .publish();
    Package::new("bar", "2.0.0")
        .file("src/lib.rs", "pub struct Straw;")
        .publish();
    let p = project()
        .file(
            "Cargo.toml",
            r#"
                [package]
                name = "foo"
                version = "0.1.0"
                edition = "2018"

                [dependencies]
                bar = "1.0"
                bar2 = {version="2.0", package="bar"}
            "#,
        )
        .file(
            "src/lib.rs",
            "
                pub fn fn1() -> bar::Spin {bar::Spin}
                pub fn fn2() -> bar2::Straw {bar2::Straw}
            ",
        )
        .build();
    p.cargo("doc -v --no-deps -Zrustdoc-map")
        .masquerade_as_nightly_cargo()
        .with_stderr_contains(
            "[RUNNING] `rustdoc [..]--crate-name foo \
            [..]bar=https://docs.rs/bar/1.0.0/[..]bar=https://docs.rs/bar/2.0.0/[..]",
        )
        .run();
    let fn1 = p.read_file("target/doc/foo/fn.fn1.html");
    // This should be 1.0.0, rustdoc seems to use the last entry when there
    // are duplicates.
    assert!(fn1.contains(r#"href="https://docs.rs/bar/2.0.0/bar/struct.Spin.html""#));
    let fn2 = p.read_file("target/doc/foo/fn.fn2.html");
    assert!(fn2.contains(r#"href="https://docs.rs/bar/2.0.0/bar/struct.Straw.html""#));
}

#[cargo_test]
fn rebuilds_when_changing() {
    // Make sure it rebuilds if the map changes.
    if !is_nightly() {
        // --extern-html-root-url is unstable
        return;
    }
    let p = basic_project();
    p.cargo("doc -v --no-deps -Zrustdoc-map")
        .masquerade_as_nightly_cargo()
        .with_stderr_contains("[..]--extern-html-root-url[..]")
        .run();

    // This also tests that the map for docs.rs can be overridden.
    p.change_file(
        ".cargo/config",
        r#"
            [doc.extern-map.registries]
            crates-io = "https://example.com/"
        "#,
    );
    p.cargo("doc -v --no-deps -Zrustdoc-map")
        .masquerade_as_nightly_cargo()
        .with_stderr_contains(
            "[RUNNING] `rustdoc [..]--extern-html-root-url [..]bar=https://example.com/bar/1.0.0/[..]",
        )
        .run();
}
<commit_msg>Auto merge of #9824 - ehuss:disable-std_docs, r=alexcrichton<commit_after>//! Tests for the -Zrustdoc-map feature.

use cargo_test_support::registry::{self, Package};
use cargo_test_support::{is_nightly, paths, project, Project};

fn basic_project() -> Project {
    Package::new("bar", "1.0.0")
        .file("src/lib.rs", "pub struct Straw;")
        .publish();

    project()
        .file(
            "Cargo.toml",
            r#"
                [package]
                name = "foo"
                version = "0.1.0"
                edition = "2018"

                [dependencies]
                bar = "1.0"
            "#,
        )
        .file(
            "src/lib.rs",
            r#"
                pub fn myfun() -> Option<bar::Straw> {
                    None
                }
            "#,
        )
        .build()
}

#[cargo_test]
fn ignores_on_stable() {
    // Requires -Zrustdoc-map to use.
    let p = basic_project();
    p.cargo("doc -v --no-deps")
        .with_stderr_does_not_contain("[..]--extern-html-root-url[..]")
        .run();
}

#[cargo_test]
fn simple() {
    // Basic test that it works with crates.io.
    if !is_nightly() {
        // --extern-html-root-url is unstable
        return;
    }
    let p = basic_project();
    p.cargo("doc -v --no-deps -Zrustdoc-map")
        .masquerade_as_nightly_cargo()
        .with_stderr_contains(
            "[RUNNING] `rustdoc [..]--crate-name foo [..]bar=https://docs.rs/bar/1.0.0/[..]",
        )
        .run();
    let myfun = p.read_file("target/doc/foo/fn.myfun.html");
    assert!(myfun.contains(r#"href="https://docs.rs/bar/1.0.0/bar/struct.Straw.html""#));
}

#[cargo_test]
// Broken, temporarily disable until https://github.com/rust-lang/rust/pull/82776 is resolved.
#[ignore]
fn std_docs() {
    // Mapping std docs somewhere else.
    if !is_nightly() {
        // --extern-html-root-url is unstable
        return;
    }
    // For local developers, skip this test if docs aren't installed.
    let docs = std::path::Path::new(&paths::sysroot()).join("share/doc/rust/html");
    if !docs.exists() {
        if cargo_util::is_ci() {
            panic!("std docs are not installed, check that the rust-docs component is installed");
        } else {
            eprintln!(
                "documentation not found at {}, \
                skipping test (run `rustdoc component add rust-docs` to install",
                docs.display()
            );
            return;
        }
    }
    let p = basic_project();
    p.change_file(
        ".cargo/config",
        r#"
            [doc.extern-map]
            std = "local"
        "#,
    );
    p.cargo("doc -v --no-deps -Zrustdoc-map")
        .masquerade_as_nightly_cargo()
        .with_stderr_contains("[RUNNING] `rustdoc [..]--crate-name foo [..]std=file://[..]")
        .run();
    let myfun = p.read_file("target/doc/foo/fn.myfun.html");
    assert!(myfun.contains(r#"share/doc/rust/html/core/option/enum.Option.html""#));

    p.change_file(
        ".cargo/config",
        r#"
            [doc.extern-map]
            std = "https://example.com/rust/"
        "#,
    );
    p.cargo("doc -v --no-deps -Zrustdoc-map")
        .masquerade_as_nightly_cargo()
        .with_stderr_contains(
            "[RUNNING] `rustdoc [..]--crate-name foo [..]std=https://example.com/rust/[..]",
        )
        .run();
    let myfun = p.read_file("target/doc/foo/fn.myfun.html");
    assert!(myfun.contains(r#"href="https://example.com/rust/core/option/enum.Option.html""#));
}

#[cargo_test]
fn renamed_dep() {
    // Handles renamed dependencies.
    if !is_nightly() {
        // --extern-html-root-url is unstable
        return;
    }
    Package::new("bar", "1.0.0")
        .file("src/lib.rs", "pub struct Straw;")
        .publish();

    let p = project()
        .file(
            "Cargo.toml",
            r#"
                [package]
                name = "foo"
                version = "0.1.0"
                edition = "2018"

                [dependencies]
                groovy = { version = "1.0", package = "bar" }
            "#,
        )
        .file(
            "src/lib.rs",
            r#"
                pub fn myfun() -> Option<groovy::Straw> {
                    None
                }
            "#,
        )
        .build();
    p.cargo("doc -v --no-deps -Zrustdoc-map")
        .masquerade_as_nightly_cargo()
        .with_stderr_contains(
            "[RUNNING] `rustdoc [..]--crate-name foo [..]bar=https://docs.rs/bar/1.0.0/[..]",
        )
        .run();
    let myfun = p.read_file("target/doc/foo/fn.myfun.html");
    assert!(myfun.contains(r#"href="https://docs.rs/bar/1.0.0/bar/struct.Straw.html""#));
}

#[cargo_test]
fn lib_name() {
    // Handles lib name != package name.
    if !is_nightly() {
        // --extern-html-root-url is unstable
        return;
    }
    Package::new("bar", "1.0.0")
        .file(
            "Cargo.toml",
            r#"
                [package]
                name = "bar"
                version = "1.0.0"

                [lib]
                name = "rumpelstiltskin"
            "#,
        )
        .file("src/lib.rs", "pub struct Straw;")
        .publish();

    let p = project()
        .file(
            "Cargo.toml",
            r#"
                [package]
                name = "foo"
                version = "0.1.0"

                [dependencies]
                bar = "1.0"
            "#,
        )
        .file(
            "src/lib.rs",
            r#"
                pub fn myfun() -> Option<rumpelstiltskin::Straw> {
                    None
                }
            "#,
        )
        .build();
    p.cargo("doc -v --no-deps -Zrustdoc-map")
        .masquerade_as_nightly_cargo()
        .with_stderr_contains(
            "[RUNNING] `rustdoc [..]--crate-name foo [..]rumpelstiltskin=https://docs.rs/bar/1.0.0/[..]",
        )
        .run();
    let myfun = p.read_file("target/doc/foo/fn.myfun.html");
    assert!(myfun.contains(r#"href="https://docs.rs/bar/1.0.0/rumpelstiltskin/struct.Straw.html""#));
}

#[cargo_test]
fn alt_registry() {
    // Supports other registry names.
    if !is_nightly() {
        // --extern-html-root-url is unstable
        return;
    }
    registry::alt_init();
    Package::new("bar", "1.0.0")
        .alternative(true)
        .file(
            "src/lib.rs",
            r#"
                extern crate baz;
                pub struct Queen;
                pub use baz::King;
            "#,
        )
        .registry_dep("baz", "1.0")
        .publish();
    Package::new("baz", "1.0.0")
        .alternative(true)
        .file("src/lib.rs", "pub struct King;")
        .publish();
    Package::new("grimm", "1.0.0")
        .file("src/lib.rs", "pub struct Gold;")
        .publish();

    let p = project()
        .file(
            "Cargo.toml",
            r#"
                [package]
                name = "foo"
                version = "0.1.0"
                edition = "2018"

                [dependencies]
                bar = { version = "1.0", registry="alternative" }
                grimm = "1.0"
            "#,
        )
        .file(
            "src/lib.rs",
            r#"
                pub fn queen() -> bar::Queen { bar::Queen }
                pub fn king() -> bar::King { bar::King }
                pub fn gold() -> grimm::Gold { grimm::Gold }
            "#,
        )
        .file(
            ".cargo/config",
            r#"
                [doc.extern-map.registries]
                alternative = "https://example.com/{pkg_name}/{version}/"
                crates-io = "https://docs.rs/"
            "#,
        )
        .build();
    p.cargo("doc -v --no-deps -Zrustdoc-map")
        .masquerade_as_nightly_cargo()
        .with_stderr_contains(
            "[RUNNING] `rustdoc [..]--crate-name foo \
            [..]bar=https://example.com/bar/1.0.0/[..]grimm=https://docs.rs/grimm/1.0.0/[..]",
        )
        .run();
    let queen = p.read_file("target/doc/foo/fn.queen.html");
    assert!(queen.contains(r#"href="https://example.com/bar/1.0.0/bar/struct.Queen.html""#));
    // The king example fails to link. Rustdoc seems to want the origin crate
    // name (baz) for re-exports. There are many issues in the issue tracker
    // for rustdoc re-exports, so I'm not sure, but I think this is maybe a
    // rustdoc issue. Alternatively, Cargo could provide mappings for all
    // transitive dependencies to fix this.
    let king = p.read_file("target/doc/foo/fn.king.html");
    assert!(king.contains(r#"-> King"#));

    let gold = p.read_file("target/doc/foo/fn.gold.html");
    assert!(gold.contains(r#"href="https://docs.rs/grimm/1.0.0/grimm/struct.Gold.html""#));
}

#[cargo_test]
fn multiple_versions() {
    // What happens when there are multiple versions.
    // NOTE: This is currently broken behavior. Rustdoc does not provide a way
    // to match renamed dependencies.
    if !is_nightly() {
        // --extern-html-root-url is unstable
        return;
    }
    Package::new("bar", "1.0.0")
        .file("src/lib.rs", "pub struct Spin;")
        .publish();
    Package::new("bar", "2.0.0")
        .file("src/lib.rs", "pub struct Straw;")
        .publish();
    let p = project()
        .file(
            "Cargo.toml",
            r#"
                [package]
                name = "foo"
                version = "0.1.0"
                edition = "2018"

                [dependencies]
                bar = "1.0"
                bar2 = {version="2.0", package="bar"}
            "#,
        )
        .file(
            "src/lib.rs",
            "
                pub fn fn1() -> bar::Spin {bar::Spin}
                pub fn fn2() -> bar2::Straw {bar2::Straw}
            ",
        )
        .build();
    p.cargo("doc -v --no-deps -Zrustdoc-map")
        .masquerade_as_nightly_cargo()
        .with_stderr_contains(
            "[RUNNING] `rustdoc [..]--crate-name foo \
            [..]bar=https://docs.rs/bar/1.0.0/[..]bar=https://docs.rs/bar/2.0.0/[..]",
        )
        .run();
    let fn1 = p.read_file("target/doc/foo/fn.fn1.html");
    // This should be 1.0.0, rustdoc seems to use the last entry when there
    // are duplicates.
    assert!(fn1.contains(r#"href="https://docs.rs/bar/2.0.0/bar/struct.Spin.html""#));
    let fn2 = p.read_file("target/doc/foo/fn.fn2.html");
    assert!(fn2.contains(r#"href="https://docs.rs/bar/2.0.0/bar/struct.Straw.html""#));
}

#[cargo_test]
fn rebuilds_when_changing() {
    // Make sure it rebuilds if the map changes.
    if !is_nightly() {
        // --extern-html-root-url is unstable
        return;
    }
    let p = basic_project();
    p.cargo("doc -v --no-deps -Zrustdoc-map")
        .masquerade_as_nightly_cargo()
        .with_stderr_contains("[..]--extern-html-root-url[..]")
        .run();

    // This also tests that the map for docs.rs can be overridden.
    p.change_file(
        ".cargo/config",
        r#"
            [doc.extern-map.registries]
            crates-io = "https://example.com/"
        "#,
    );
    p.cargo("doc -v --no-deps -Zrustdoc-map")
        .masquerade_as_nightly_cargo()
        .with_stderr_contains(
            "[RUNNING] `rustdoc [..]--extern-html-root-url [..]bar=https://example.com/bar/1.0.0/[..]",
        )
        .run();
}
<|endoftext|>


<commit_before>// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements.  See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership.  The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License.  You may obtain a copy of the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied.  See the License for the
// specific language governing permissions and limitations
// under the License.

use std::io::{Read, Write};

use basic::Compression as CodecType;
use errors::{Result, ParquetError};
use snap::{Decoder, Encoder, decompress_len};
use brotli;
use flate2::Compression;
use flate2::read::GzDecoder;
use flate2::write::GzEncoder;

pub trait Codec {
  /// Compress data stored in slice `input_buf` and return a new
  /// vector with the compressed data.
  /// TODO: it's better to pass in vec here (e.g., allow reuse),
  ///   but flate2 api doesn't support this.
  fn compress(&mut self, input_buf: &[u8]) -> Result<Vec<u8>>;

  /// Decompress data stored in slice `input_buf` and write output
  /// to `output_buf`. Return the total number of bytes written.
  fn decompress(&mut self, input_buf: &[u8], output_buf: &mut Vec<u8>) -> Result<usize>;
}

/// Given the compression type `codec`, return a codec used to
/// compress & decompress bytes for the compression type.
/// This returns `None` if the codec type is `UNCOMPRESSED`.
pub fn create_codec(codec: CodecType) -> Result<Option<Box<Codec>>> {
  match codec {
    CodecType::BROTLI => Ok(Some(Box::new(BrotliCodec::new()))),
    CodecType::GZIP => Ok(Some(Box::new(GZipCodec::new()))),
    CodecType::SNAPPY => Ok(Some(Box::new(SnappyCodec::new()))),
    CodecType::UNCOMPRESSED => Ok(None),
    _ => Err(nyi_err!("The codec type {} is not supported yet", codec))
  }
}

pub struct SnappyCodec {
  decoder: Decoder,
  encoder: Encoder
}

impl SnappyCodec {
  fn new() -> Self {
    Self { decoder: Decoder::new(), encoder: Encoder::new() }
  }
}

impl Codec for SnappyCodec {
  fn decompress(&mut self, input_buf: &[u8], output_buf: &mut Vec<u8>) -> Result<usize> {
    let len = decompress_len(input_buf)?;
    output_buf.resize(len, 0);
    self.decoder.decompress(input_buf, output_buf)
      .map_err(|e| general_err!("Error when decompressing using Snappy: {}", e))
  }

  fn compress(&mut self, input_buf: &[u8]) -> Result<Vec<u8>> {
    self.encoder.compress_vec(input_buf)
      .map_err(|e| general_err!("Error when compressing using Snappy: {}", e))
  }
}

pub struct GZipCodec {
}

impl GZipCodec {
  fn new() -> Self {
    Self { }
  }
}

impl Codec for GZipCodec {
  fn decompress(&mut self, input_buf: &[u8], output_buf: &mut Vec<u8>) -> Result<usize> {
    let mut decoder = GzDecoder::new(input_buf)?;
    decoder.read_to_end(output_buf).map_err(
      |e| general_err!("Error when decompressing using GZip: {}", e))
  }

  fn compress(&mut self, input_buf: &[u8]) -> Result<Vec<u8>> {
    let mut encoder = GzEncoder::new(Vec::new(), Compression::Default);
    encoder.write_all(input_buf)?;
    encoder.finish().map_err(
      |e| general_err!("Error when compressing using GZip: {}", e))
  }
}

pub struct BrotliCodec {
}

impl BrotliCodec {
  fn new() -> Self {
    Self { }
  }
}

impl Codec for BrotliCodec {
  fn decompress(&mut self, input_buf: &[u8], output_buf: &mut Vec<u8>) -> Result<usize>{
    brotli::Decompressor::new(input_buf, 4096).read(output_buf)
      .map_err(|e| general_err!("Error when decompressing using Brotli: {}", e))
  }

  fn compress(&mut self, _: &[u8]) -> Result<Vec<u8>> {
    // TODO: add this once rust-brotli implements the functionality
    unimplemented!()
  }
}

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

  fn test_roundtrip(c: CodecType, data: &Vec<u8>) {
    let mut c1 = create_codec(c).unwrap().unwrap();
    let mut c2 = create_codec(c).unwrap().unwrap();

    // compress with c1
    let mut decompressed = Vec::new();
    let mut compressed_res = c1.compress(data.as_slice());
    assert!(compressed_res.is_ok());
    let mut compressed = compressed_res.unwrap();

    // decompress with c2
    let mut decompressed_size = c2.decompress(compressed.as_slice(), &mut decompressed);
    assert!(decompressed_size.is_ok());
    decompressed.truncate(decompressed_size.unwrap());
    assert!(*data == decompressed);

    // compress with c2
    compressed_res = c2.compress(data.as_slice());
    assert!(compressed_res.is_ok());
    compressed = compressed_res.unwrap();

    // decompress with c1
    decompressed_size = c1.decompress(compressed.as_slice(), &mut decompressed);
    assert!(decompressed_size.is_ok());
    decompressed.truncate(decompressed_size.unwrap());
    assert!(*data == decompressed);
  }

  fn test_codec(c: CodecType) {
    let sizes = vec![100, 10000, 100000];
    for size in sizes {
      let mut data = random_bytes(size);
      test_roundtrip(c, &mut data);
    }
  }

  #[test]
  fn test_codec_snappy() {
    test_codec(CodecType::SNAPPY);
  }

  #[test]
  fn test_codec_gzip() {
    test_codec(CodecType::GZIP);
  }
}
<commit_msg>Add brotli compression (#23)<commit_after>// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements.  See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership.  The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License.  You may obtain a copy of the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied.  See the License for the
// specific language governing permissions and limitations
// under the License.

use std::io::{Read, Write};

use basic::Compression as CodecType;
use errors::{Result, ParquetError};
use snap::{Decoder, Encoder, decompress_len};
use brotli;
use flate2::Compression;
use flate2::read::GzDecoder;
use flate2::write::GzEncoder;

pub trait Codec {
  /// Compress data stored in slice `input_buf` and return a new
  /// vector with the compressed data.
  /// TODO: it's better to pass in vec here (e.g., allow reuse),
  ///   but flate2 api doesn't support this.
  fn compress(&mut self, input_buf: &[u8]) -> Result<Vec<u8>>;

  /// Decompress data stored in slice `input_buf` and write output
  /// to `output_buf`. Return the total number of bytes written.
  fn decompress(&mut self, input_buf: &[u8], output_buf: &mut Vec<u8>) -> Result<usize>;
}

/// Given the compression type `codec`, return a codec used to
/// compress & decompress bytes for the compression type.
/// This returns `None` if the codec type is `UNCOMPRESSED`.
pub fn create_codec(codec: CodecType) -> Result<Option<Box<Codec>>> {
  match codec {
    CodecType::BROTLI => Ok(Some(Box::new(BrotliCodec::new()))),
    CodecType::GZIP => Ok(Some(Box::new(GZipCodec::new()))),
    CodecType::SNAPPY => Ok(Some(Box::new(SnappyCodec::new()))),
    CodecType::UNCOMPRESSED => Ok(None),
    _ => Err(nyi_err!("The codec type {} is not supported yet", codec))
  }
}

pub struct SnappyCodec {
  decoder: Decoder,
  encoder: Encoder
}

impl SnappyCodec {
  fn new() -> Self {
    Self { decoder: Decoder::new(), encoder: Encoder::new() }
  }
}

impl Codec for SnappyCodec {
  fn decompress(&mut self, input_buf: &[u8], output_buf: &mut Vec<u8>) -> Result<usize> {
    let len = decompress_len(input_buf)?;
    output_buf.resize(len, 0);
    self.decoder.decompress(input_buf, output_buf)
      .map_err(|e| general_err!("Error when decompressing using Snappy: {}", e))
  }

  fn compress(&mut self, input_buf: &[u8]) -> Result<Vec<u8>> {
    self.encoder.compress_vec(input_buf)
      .map_err(|e| general_err!("Error when compressing using Snappy: {}", e))
  }
}

pub struct GZipCodec {
}

impl GZipCodec {
  fn new() -> Self {
    Self { }
  }
}

impl Codec for GZipCodec {
  fn decompress(&mut self, input_buf: &[u8], output_buf: &mut Vec<u8>) -> Result<usize> {
    let mut decoder = GzDecoder::new(input_buf)?;
    decoder.read_to_end(output_buf).map_err(
      |e| general_err!("Error when decompressing using GZip: {}", e))
  }

  fn compress(&mut self, input_buf: &[u8]) -> Result<Vec<u8>> {
    let mut encoder = GzEncoder::new(Vec::new(), Compression::Default);
    encoder.write_all(input_buf)?;
    encoder.finish().map_err(
      |e| general_err!("Error when compressing using GZip: {}", e))
  }
}

const BROTLI_DEFAULT_BUFFER_SIZE: usize = 4096;
const BROTLI_DEFAULT_COMPRESSION_QUALITY: u32 = 9; // supported levels 0-9
const BROTLI_DEFAULT_LG_WINDOW_SIZE: u32 = 22; // recommended between 20-22

pub struct BrotliCodec {
}

impl BrotliCodec {
  fn new() -> Self {
    Self { }
  }
}

impl Codec for BrotliCodec {
  fn decompress(&mut self, input_buf: &[u8], output_buf: &mut Vec<u8>) -> Result<usize> {
    brotli::Decompressor::new(input_buf, BROTLI_DEFAULT_BUFFER_SIZE).read_to_end(output_buf)
      .map_err(|e| general_err!("Error when decompressing using Brotli: {}", e))
  }

  fn compress(&mut self, input_buf: &[u8]) -> Result<Vec<u8>> {
    let mut buffer = Vec::new();
    let mut reader = brotli::CompressorReader::new(
      input_buf,
      BROTLI_DEFAULT_BUFFER_SIZE,
      BROTLI_DEFAULT_COMPRESSION_QUALITY,
      BROTLI_DEFAULT_LG_WINDOW_SIZE
    );
    reader.read_to_end(&mut buffer)?;
    Ok(buffer)
  }
}

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

  fn test_roundtrip(c: CodecType, data: &Vec<u8>) {
    let mut c1 = create_codec(c).unwrap().unwrap();
    let mut c2 = create_codec(c).unwrap().unwrap();

    // compress with c1
    let mut decompressed = Vec::new();
    let mut compressed_res = c1.compress(data.as_slice());
    assert!(compressed_res.is_ok());
    let mut compressed = compressed_res.unwrap();

    // decompress with c2
    let mut decompressed_size = c2.decompress(compressed.as_slice(), &mut decompressed);
    assert!(decompressed_size.is_ok());
    decompressed.truncate(decompressed_size.unwrap());
    assert!(*data == decompressed);

    // compress with c2
    compressed_res = c2.compress(data.as_slice());
    assert!(compressed_res.is_ok());
    compressed = compressed_res.unwrap();

    // decompress with c1
    decompressed_size = c1.decompress(compressed.as_slice(), &mut decompressed);
    assert!(decompressed_size.is_ok());
    decompressed.truncate(decompressed_size.unwrap());
    assert!(*data == decompressed);
  }

  fn test_codec(c: CodecType) {
    let sizes = vec![100, 10000, 100000];
    for size in sizes {
      let mut data = random_bytes(size);
      test_roundtrip(c, &mut data);
    }
  }

  #[test]
  fn test_codec_snappy() {
    test_codec(CodecType::SNAPPY);
  }

  #[test]
  fn test_codec_gzip() {
    test_codec(CodecType::GZIP);
  }

  #[test]
  fn test_codec_brotli() {
    test_codec(CodecType::BROTLI);
  }
}
<|endoftext|>


<commit_before>//! Implementations of the low-level parser combinators.

use core::num::{NonZeroU128, NonZeroU16, NonZeroU32, NonZeroU64, NonZeroU8, NonZeroUsize};
use core::str::FromStr;

use crate::format_description::modifier::Padding;
use crate::parsing::ParsedItem;

/// Marker trait for integers.
pub(crate) trait Integer: FromStr {}
impl Integer for u8 {}
impl Integer for u16 {}
impl Integer for u32 {}
impl Integer for u64 {}
impl Integer for u128 {}
impl Integer for usize {}
impl Integer for NonZeroU8 {}
impl Integer for NonZeroU16 {}
impl Integer for NonZeroU32 {}
impl Integer for NonZeroU64 {}
impl Integer for NonZeroU128 {}
impl Integer for NonZeroUsize {}

/// Parse a string.
pub(crate) fn string<'a, 'b: 'a>(
    expected: &'b str,
) -> impl Fn(&'a str) -> Option<ParsedItem<'a, &'a str>> {
    move |input| Some(ParsedItem(input.strip_prefix(expected)?, expected))
}

/// Parse a "+" or "-" sign. Returns the ASCII byte representing the sign, if present.
pub(crate) fn sign(input: &str) -> Option<ParsedItem<'_, char>> {
    if let Some(remaining) = input.strip_prefix('-') {
        Some(ParsedItem(remaining, '-'))
    } else {
        let remaining = input.strip_prefix('+')?;
        Some(ParsedItem(remaining, '+'))
    }
}

/// Consume the first matching string, returning its associated value.
pub(crate) fn first_match<'a, 'b: 'a, T: Copy + 'a>(
    mut options: impl Iterator<Item = &'a (&'b str, T)>,
) -> impl FnMut(&'b str) -> Option<ParsedItem<'b, T>> {
    move |input| options.find_map(|&(expected, t)| Some(ParsedItem(string(expected)(input)?.0, t)))
}

/// Consume between `n` and `m` instances of the provided parser.
pub(crate) fn n_to_m<'a, T>(
    n: u8,
    m: u8,
    parser: impl Fn(&'a str) -> Option<ParsedItem<'a, T>>,
) -> impl Fn(&'a str) -> Option<ParsedItem<'a, &'a str>> {
    debug_assert!(m >= n);
    move |mut input| {
        // We need to keep this to determine the total length eventually consumed.
        let orig_input = input;

        // Mandatory
        for _ in 0..n {
            input = parser(input)?.0;
        }

        // Optional
        for _ in n..m {
            match parser(input) {
                Some(parsed) => input = parsed.0,
                None => break,
            }
        }

        Some(ParsedItem(
            input,
            &orig_input[..(orig_input.len() - input.len())],
        ))
    }
}

/// Consume exactly `n` instances of the provided parser.
pub(crate) fn exactly_n<'a, T>(
    n: u8,
    parser: impl Fn(&'a str) -> Option<ParsedItem<'a, T>>,
) -> impl Fn(&'a str) -> Option<ParsedItem<'a, &'a str>> {
    n_to_m(n, n, parser)
}

/// Consume between `n` and `m` digits, returning the numerical value.
pub(crate) fn n_to_m_digits<'a, T: Integer>(
    n: u8,
    m: u8,
) -> impl Fn(&'a str) -> Option<ParsedItem<'a, T>> {
    debug_assert!(m >= n);
    move |input| n_to_m(n, m, any_digit)(input)?.flat_map(|value| value.parse().ok())
}

/// Consume exactly `n` digits, returning the numerical value.
pub(crate) fn exactly_n_digits_padded<'a, T: Integer>(
    n: u8,
    padding: Padding,
) -> impl Fn(&'a str) -> Option<ParsedItem<'a, T>> {
    n_to_m_digits_padded(n, n, padding)
}

/// Consume between `n` and `m` digits, returning the numerical value.
pub(crate) fn n_to_m_digits_padded<'a, T: Integer>(
    n: u8,
    m: u8,
    padding: Padding,
) -> impl Fn(&'a str) -> Option<ParsedItem<'a, T>> {
    debug_assert!(m >= n);
    move |input| {
        let pad_char = match padding {
            Padding::None => return n_to_m_digits(1, m)(input),
            Padding::Space => b' ',
            Padding::Zero => b'0',
        };

        let ParsedItem(input, value) = n_to_m(0, n - 1, ascii_char(pad_char))(input)?;
        let pad_width = value.len() as u8;

        n_to_m_digits(n - pad_width, m - pad_width)(input)
    }
}

/// Consume exactly one digit.
pub(crate) fn any_digit(input: &str) -> Option<ParsedItem<'_, u8>> {
    match input.as_bytes() {
        [c, ..] if c.is_ascii_digit() => Some(ParsedItem(&input[1..], *c)),
        _ => None,
    }
}

/// Consume exactly one of the provided ASCII characters.
pub(crate) fn ascii_char(char: u8) -> impl Fn(&str) -> Option<ParsedItem<'_, ()>> {
    move |input| match input.as_bytes() {
        [c, ..] if *c == char => Some(ParsedItem(&input[1..], ())),
        _ => None,
    }
}

/// Optionally consume an input with a given parser.
pub(crate) fn opt<'a, T>(
    parser: impl Fn(&'a str) -> Option<ParsedItem<'a, T>>,
) -> impl Fn(&'a str) -> ParsedItem<'a, Option<T>> {
    move |input| match parser(input) {
        Some(value) => value.map(Some),
        None => ParsedItem(input, None),
    }
}
<commit_msg>Simplify `n_to_m_digits_padded`<commit_after>//! Implementations of the low-level parser combinators.

use core::num::{NonZeroU128, NonZeroU16, NonZeroU32, NonZeroU64, NonZeroU8, NonZeroUsize};
use core::str::FromStr;

use crate::format_description::modifier::Padding;
use crate::parsing::ParsedItem;

/// Marker trait for integers.
pub(crate) trait Integer: FromStr {}
impl Integer for u8 {}
impl Integer for u16 {}
impl Integer for u32 {}
impl Integer for u64 {}
impl Integer for u128 {}
impl Integer for usize {}
impl Integer for NonZeroU8 {}
impl Integer for NonZeroU16 {}
impl Integer for NonZeroU32 {}
impl Integer for NonZeroU64 {}
impl Integer for NonZeroU128 {}
impl Integer for NonZeroUsize {}

/// Parse a string.
pub(crate) fn string<'a, 'b: 'a>(
    expected: &'b str,
) -> impl Fn(&'a str) -> Option<ParsedItem<'a, &'a str>> {
    move |input| Some(ParsedItem(input.strip_prefix(expected)?, expected))
}

/// Parse a "+" or "-" sign. Returns the ASCII byte representing the sign, if present.
pub(crate) fn sign(input: &str) -> Option<ParsedItem<'_, char>> {
    if let Some(remaining) = input.strip_prefix('-') {
        Some(ParsedItem(remaining, '-'))
    } else {
        let remaining = input.strip_prefix('+')?;
        Some(ParsedItem(remaining, '+'))
    }
}

/// Consume the first matching string, returning its associated value.
pub(crate) fn first_match<'a, 'b: 'a, T: Copy + 'a>(
    mut options: impl Iterator<Item = &'a (&'b str, T)>,
) -> impl FnMut(&'b str) -> Option<ParsedItem<'b, T>> {
    move |input| options.find_map(|&(expected, t)| Some(ParsedItem(string(expected)(input)?.0, t)))
}

/// Consume between `n` and `m` instances of the provided parser.
pub(crate) fn n_to_m<'a, T>(
    n: u8,
    m: u8,
    parser: impl Fn(&'a str) -> Option<ParsedItem<'a, T>>,
) -> impl Fn(&'a str) -> Option<ParsedItem<'a, &'a str>> {
    debug_assert!(m >= n);
    move |mut input| {
        // We need to keep this to determine the total length eventually consumed.
        let orig_input = input;

        // Mandatory
        for _ in 0..n {
            input = parser(input)?.0;
        }

        // Optional
        for _ in n..m {
            match parser(input) {
                Some(parsed) => input = parsed.0,
                None => break,
            }
        }

        Some(ParsedItem(
            input,
            &orig_input[..(orig_input.len() - input.len())],
        ))
    }
}

/// Consume exactly `n` instances of the provided parser.
pub(crate) fn exactly_n<'a, T>(
    n: u8,
    parser: impl Fn(&'a str) -> Option<ParsedItem<'a, T>>,
) -> impl Fn(&'a str) -> Option<ParsedItem<'a, &'a str>> {
    n_to_m(n, n, parser)
}

/// Consume between `n` and `m` digits, returning the numerical value.
pub(crate) fn n_to_m_digits<'a, T: Integer>(
    n: u8,
    m: u8,
) -> impl Fn(&'a str) -> Option<ParsedItem<'a, T>> {
    debug_assert!(m >= n);
    move |input| n_to_m(n, m, any_digit)(input)?.flat_map(|value| value.parse().ok())
}

/// Consume exactly `n` digits, returning the numerical value.
pub(crate) fn exactly_n_digits_padded<'a, T: Integer>(
    n: u8,
    padding: Padding,
) -> impl Fn(&'a str) -> Option<ParsedItem<'a, T>> {
    n_to_m_digits_padded(n, n, padding)
}

/// Consume between `n` and `m` digits, returning the numerical value.
pub(crate) fn n_to_m_digits_padded<'a, T: Integer>(
    n: u8,
    m: u8,
    padding: Padding,
) -> impl Fn(&'a str) -> Option<ParsedItem<'a, T>> {
    debug_assert!(m >= n);
    move |input| match padding {
        Padding::None => return n_to_m_digits(1, m)(input),
        Padding::Space => {
            let ParsedItem(input, value) = n_to_m(0, n - 1, ascii_char(b' '))(input)?;
            let pad_width = value.len() as u8;
            n_to_m_digits(n - pad_width, m - pad_width)(input)
        }
        Padding::Zero => return n_to_m_digits(n, m)(input),
    }
}

/// Consume exactly one digit.
pub(crate) fn any_digit(input: &str) -> Option<ParsedItem<'_, u8>> {
    match input.as_bytes() {
        [c, ..] if c.is_ascii_digit() => Some(ParsedItem(&input[1..], *c)),
        _ => None,
    }
}

/// Consume exactly one of the provided ASCII characters.
pub(crate) fn ascii_char(char: u8) -> impl Fn(&str) -> Option<ParsedItem<'_, ()>> {
    move |input| match input.as_bytes() {
        [c, ..] if *c == char => Some(ParsedItem(&input[1..], ())),
        _ => None,
    }
}

/// Optionally consume an input with a given parser.
pub(crate) fn opt<'a, T>(
    parser: impl Fn(&'a str) -> Option<ParsedItem<'a, T>>,
) -> impl Fn(&'a str) -> ParsedItem<'a, Option<T>> {
    move |input| match parser(input) {
        Some(value) => value.map(Some),
        None => ParsedItem(input, None),
    }
}
<|endoftext|>


<commit_before>pub mod home {
    use chrono::{NaiveDate};

    pub struct NewsItem {
        pub title: String,
        pub date: NaiveDate,
    }

    fn changeFuncName() -> [NewsItem; 1] {
        let aNewsItems: [NewsItem; 1] = [
            NewsItem{
                title: String::from(
                    "Ethereum bounty contractshave now been added for anyone that would like to try to push the boundaries of human knowledge. The first contract is a competition for the correct solution to a³+b³+c³ = 33. Tweet me if you need assistance using these contracts!"),
                date: NaiveDate::from_ymd(2019, 1, 22)
            }
        ];
        aNewsItems
    }

}<commit_msg>struct NewsItem now private<commit_after>pub mod home {
    use chrono::{NaiveDate};

    struct NewsItem {
        pub title: String,
        pub date: NaiveDate,
    }

    fn changeFuncName() -> [NewsItem; 1] {
        let aNewsItems: [NewsItem; 1] = [
            NewsItem{
                title: String::from(
                    "Ethereum bounty contractshave now been added for anyone that would like to try to push the boundaries of human knowledge. The first contract is a competition for the correct solution to a³+b³+c³ = 33. Tweet me if you need assistance using these contracts!"),
                date: NaiveDate::from_ymd(2019, 1, 22)
            }
        ];
        aNewsItems
    }

}<|endoftext|>


<commit_before>//! A module defining an API for IRC user and channel modes.
use std::fmt;

use error;
use proto::Command;

/// A marker trait for different kinds of Modes.
pub trait ModeType: fmt::Display + fmt::Debug + Clone + PartialEq {
    /// Creates a command of this kind.
    fn mode(target: &str, modes: &[Mode<Self>]) -> Command;

    /// Returns true if this mode takes an argument, and false otherwise.
    fn takes_arg(&self) -> bool;
}

/// User modes for the MODE command.
#[derive(Clone, Debug, PartialEq)]
pub enum UserMode {
    /// a - user is flagged as away
    Away,
    /// i - marks a users as invisible
    Invisible,
    /// w - user receives wallops
    Wallops,
    /// r - restricted user connection
    Restricted,
    /// o - operator flag
    Oper,
    /// O - local operator flag
    LocalOper,
    /// s - marks a user for receipt of server notices
    ServerNotices,

    /// Any other unknown-to-the-crate mode.
    Unknown(char),
}

impl ModeType for UserMode {
    fn mode(target: &str, modes: &[Mode<Self>]) -> Command {
        Command::UserMODE(target.to_owned(), modes.to_owned())
    }

    fn takes_arg(&self) -> bool {
        false
    }
}

impl UserMode {
    fn from_char(c: char) -> error::Result<UserMode> {
        use self::UserMode::*;

        Ok(match c {
            'a' => Away,
            'i' => Invisible,
            'w' => Wallops,
            'r' => Restricted,
            'o' => Oper,
            'O' => LocalOper,
            's' => ServerNotices,
            _ => Unknown(c),
        })
    }
}

impl fmt::Display for UserMode {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        use self::UserMode::*;

        write!(f, "{}", match *self {
            Away => 'a',
            Invisible => 'i',
            Wallops => 'w',
            Restricted => 'r',
            Oper => 'o',
            LocalOper => 'O',
            ServerNotices => 's',
            Unknown(c) => c,
        })
    }
}

/// Channel modes for the MODE command.
#[derive(Clone, Debug, PartialEq)]
pub enum ChannelMode {
    /// b - ban the user from joining or speaking in the channel
    Ban,
    /// e - exemptions from bans
    Exception,
    /// l - limit the maximum number of users in a channel
    Limit,
    /// i - channel becomes invite-only
    InviteOnly,
    /// I - exception to invite-only rule
    InviteException,
    /// k - specify channel key
    Key,
    /// m - channel is in moderated mode
    Moderated,
    /// s - channel is hidden from listings
    Secret,
    /// t - require permissions to edit topic
    ProtectedTopic,
    /// n - users must join channels to message them
    NoExternalMessages,

    /// q - user gets founder permission
    Founder,
    /// a - user gets admin or protected permission
    Admin,
    /// o - user gets oper permission
    Oper,
    /// h - user gets halfop permission
    Halfop,
    /// v - user gets voice permission
    Voice,

    /// Any other unknown-to-the-crate mode.
    Unknown(char),
}

impl ModeType for ChannelMode {
    fn mode(target: &str, modes: &[Mode<Self>]) -> Command {
        Command::ChannelMODE(target.to_owned(), modes.to_owned())
    }

    fn takes_arg(&self) -> bool {
        use self::ChannelMode::*;

        match *self {
            Ban | Exception | Limit | InviteException | Key | Founder | Admin | Oper | Halfop |
            Voice => true,
            _ => false,
        }
    }
}

impl ChannelMode {
    fn from_char(c: char) -> error::Result<ChannelMode> {
        use self::ChannelMode::*;

        Ok(match c {
            'b' => Ban,
            'e' => Exception,
            'l' => Limit,
            'i' => InviteOnly,
            'I' => InviteException,
            'k' => Key,
            'm' => Moderated,
            's' => Secret,
            't' => ProtectedTopic,
            'n' => NoExternalMessages,
            'q' => Founder,
            'a' => Admin,
            'o' => Oper,
            'h' => Halfop,
            'v' => Voice,
            _ => Unknown(c),
        })
    }
}

impl fmt::Display for ChannelMode {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        use self::ChannelMode::*;

        write!(f, "{}", match *self {
            Ban => 'b',
            Exception => 'e',
            Limit => 'l',
            InviteOnly => 'i',
            InviteException => 'I',
            Key => 'k',
            Moderated => 'm',
            Secret => 's',
            ProtectedTopic => 't',
            NoExternalMessages => 'n',
            Founder => 'q',
            Admin => 'a',
            Oper => 'o',
            Halfop => 'h',
            Voice => 'v',
            Unknown(c) => c,
        })
    }
}

/// A mode argument for the MODE command.
#[derive(Clone, Debug, PartialEq)]
pub enum Mode<T>
where
    T: ModeType,
{
    /// Adding the specified mode, optionally with an argument.
    Plus(T, Option<String>),
    /// Removing the specified mode, optionally with an argument.
    Minus(T, Option<String>),
}

impl<T> Mode<T>
where
    T: ModeType,
{
    /// Creates a plus mode with an `&str` argument.
    pub fn plus(inner: T, arg: Option<&str>) -> Mode<T> {
        Mode::Plus(inner, arg.map(|s| s.to_owned()))
    }

    /// Creates a minus mode with an `&str` argument.
    pub fn minus(inner: T, arg: Option<&str>) -> Mode<T> {
        Mode::Minus(inner, arg.map(|s| s.to_owned()))
    }
}

impl<T> fmt::Display for Mode<T>
where
    T: ModeType,
{
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            &Mode::Plus(ref mode, Some(ref arg)) => write!(f, "{}{} {}", "+", mode, arg),
            &Mode::Minus(ref mode, Some(ref arg)) => write!(f, "{}{} {}", "-", mode, arg),
            &Mode::Plus(ref mode, None) => write!(f, "{}{}", "+", mode),
            &Mode::Minus(ref mode, None) => write!(f, "{}{}", "-", mode),
        }
    }
}

enum PlusMinus {
    Plus,
    Minus,
}

// MODE user [modes]
impl Mode<UserMode> {
    // TODO: turning more edge cases into errors.
    /// Parses the specified mode string as user modes.
    pub fn as_user_modes(s: &str) -> error::Result<Vec<Mode<UserMode>>> {
        use self::PlusMinus::*;

        let mut res = vec![];
        let mut pieces = s.split(" ");
        for term in pieces.clone() {
            if term.starts_with("+") || term.starts_with("-") {
                let _ = pieces.next();

                let mut chars = term.chars();
                let init = match chars.next() {
                    Some('+') => Plus,
                    Some('-') => Minus,
                    _ => return Err(error::ErrorKind::ModeParsingFailed.into()),
                };

                for c in chars {
                    let mode = UserMode::from_char(c)?;
                    let arg = if mode.takes_arg() {
                        pieces.next()
                    } else {
                        None
                    };
                    res.push(match init {
                        Plus => Mode::Plus(mode, arg.map(|s| s.to_owned())),
                        Minus => Mode::Minus(mode, arg.map(|s| s.to_owned())),
                    })
                }
            }
        }

        Ok(res)
    }
}

// MODE channel [modes [modeparams]]
impl Mode<ChannelMode> {
    // TODO: turning more edge cases into errors.
    /// Parses the specified mode string as channel modes.
    pub fn as_channel_modes(s: &str) -> error::Result<Vec<Mode<ChannelMode>>> {
        use self::PlusMinus::*;

        let mut res = vec![];
        let mut pieces = s.split(" ");
        for term in pieces.clone() {
            if term.starts_with("+") || term.starts_with("-") {
                let _ = pieces.next();

                let mut chars = term.chars();
                let init = match chars.next() {
                    Some('+') => Plus,
                    Some('-') => Minus,
                    _ => return Err(error::ErrorKind::ModeParsingFailed.into()),
                };

                for c in chars {
                    let mode = ChannelMode::from_char(c)?;
                    let arg = if mode.takes_arg() {
                        pieces.next()
                    } else {
                        None
                    };
                    res.push(match init {
                        Plus => Mode::Plus(mode, arg.map(|s| s.to_owned())),
                        Minus => Mode::Minus(mode, arg.map(|s| s.to_owned())),
                    })
                }
            }
        }

        Ok(res)
    }
}
<commit_msg>Add usermode +x and channelmode +r <commit_after>//! A module defining an API for IRC user and channel modes.
use std::fmt;

use error;
use proto::Command;

/// A marker trait for different kinds of Modes.
pub trait ModeType: fmt::Display + fmt::Debug + Clone + PartialEq {
    /// Creates a command of this kind.
    fn mode(target: &str, modes: &[Mode<Self>]) -> Command;

    /// Returns true if this mode takes an argument, and false otherwise.
    fn takes_arg(&self) -> bool;
}

/// User modes for the MODE command.
#[derive(Clone, Debug, PartialEq)]
pub enum UserMode {
    /// a - user is flagged as away
    Away,
    /// i - marks a users as invisible
    Invisible,
    /// w - user receives wallops
    Wallops,
    /// r - restricted user connection
    Restricted,
    /// o - operator flag
    Oper,
    /// O - local operator flag
    LocalOper,
    /// s - marks a user for receipt of server notices
    ServerNotices,
    /// x - masked hostname
    MaskedHost,

    /// Any other unknown-to-the-crate mode.
    Unknown(char),
}

impl ModeType for UserMode {
    fn mode(target: &str, modes: &[Mode<Self>]) -> Command {
        Command::UserMODE(target.to_owned(), modes.to_owned())
    }

    fn takes_arg(&self) -> bool {
        false
    }
}

impl UserMode {
    fn from_char(c: char) -> error::Result<UserMode> {
        use self::UserMode::*;

        Ok(match c {
            'a' => Away,
            'i' => Invisible,
            'w' => Wallops,
            'r' => Restricted,
            'o' => Oper,
            'O' => LocalOper,
            's' => ServerNotices,
            'x' => MaskedHost,
            _ => Unknown(c),
        })
    }
}

impl fmt::Display for UserMode {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        use self::UserMode::*;

        write!(f, "{}", match *self {
            Away => 'a',
            Invisible => 'i',
            Wallops => 'w',
            Restricted => 'r',
            Oper => 'o',
            LocalOper => 'O',
            ServerNotices => 's',
            MaskedHost => 'x',
            Unknown(c) => c,
        })
    }
}

/// Channel modes for the MODE command.
#[derive(Clone, Debug, PartialEq)]
pub enum ChannelMode {
    /// b - ban the user from joining or speaking in the channel
    Ban,
    /// e - exemptions from bans
    Exception,
    /// l - limit the maximum number of users in a channel
    Limit,
    /// i - channel becomes invite-only
    InviteOnly,
    /// I - exception to invite-only rule
    InviteException,
    /// k - specify channel key
    Key,
    /// m - channel is in moderated mode
    Moderated,
    /// r - entry for registered users only
    RegisteredOnly,
    /// s - channel is hidden from listings
    Secret,
    /// t - require permissions to edit topic
    ProtectedTopic,
    /// n - users must join channels to message them
    NoExternalMessages,

    /// q - user gets founder permission
    Founder,
    /// a - user gets admin or protected permission
    Admin,
    /// o - user gets oper permission
    Oper,
    /// h - user gets halfop permission
    Halfop,
    /// v - user gets voice permission
    Voice,

    /// Any other unknown-to-the-crate mode.
    Unknown(char),
}

impl ModeType for ChannelMode {
    fn mode(target: &str, modes: &[Mode<Self>]) -> Command {
        Command::ChannelMODE(target.to_owned(), modes.to_owned())
    }

    fn takes_arg(&self) -> bool {
        use self::ChannelMode::*;

        match *self {
            Ban | Exception | Limit | InviteException | Key | Founder | Admin | Oper | Halfop |
            Voice => true,
            _ => false,
        }
    }
}

impl ChannelMode {
    fn from_char(c: char) -> error::Result<ChannelMode> {
        use self::ChannelMode::*;

        Ok(match c {
            'b' => Ban,
            'e' => Exception,
            'l' => Limit,
            'i' => InviteOnly,
            'I' => InviteException,
            'k' => Key,
            'm' => Moderated,
            'r' => RegisteredOnly,
            's' => Secret,
            't' => ProtectedTopic,
            'n' => NoExternalMessages,
            'q' => Founder,
            'a' => Admin,
            'o' => Oper,
            'h' => Halfop,
            'v' => Voice,
            _ => Unknown(c),
        })
    }
}

impl fmt::Display for ChannelMode {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        use self::ChannelMode::*;

        write!(f, "{}", match *self {
            Ban => 'b',
            Exception => 'e',
            Limit => 'l',
            InviteOnly => 'i',
            InviteException => 'I',
            Key => 'k',
            Moderated => 'm',
            RegisteredOnly => 'r',
            Secret => 's',
            ProtectedTopic => 't',
            NoExternalMessages => 'n',
            Founder => 'q',
            Admin => 'a',
            Oper => 'o',
            Halfop => 'h',
            Voice => 'v',
            Unknown(c) => c,
        })
    }
}

/// A mode argument for the MODE command.
#[derive(Clone, Debug, PartialEq)]
pub enum Mode<T>
where
    T: ModeType,
{
    /// Adding the specified mode, optionally with an argument.
    Plus(T, Option<String>),
    /// Removing the specified mode, optionally with an argument.
    Minus(T, Option<String>),
}

impl<T> Mode<T>
where
    T: ModeType,
{
    /// Creates a plus mode with an `&str` argument.
    pub fn plus(inner: T, arg: Option<&str>) -> Mode<T> {
        Mode::Plus(inner, arg.map(|s| s.to_owned()))
    }

    /// Creates a minus mode with an `&str` argument.
    pub fn minus(inner: T, arg: Option<&str>) -> Mode<T> {
        Mode::Minus(inner, arg.map(|s| s.to_owned()))
    }
}

impl<T> fmt::Display for Mode<T>
where
    T: ModeType,
{
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            &Mode::Plus(ref mode, Some(ref arg)) => write!(f, "{}{} {}", "+", mode, arg),
            &Mode::Minus(ref mode, Some(ref arg)) => write!(f, "{}{} {}", "-", mode, arg),
            &Mode::Plus(ref mode, None) => write!(f, "{}{}", "+", mode),
            &Mode::Minus(ref mode, None) => write!(f, "{}{}", "-", mode),
        }
    }
}

enum PlusMinus {
    Plus,
    Minus,
}

// MODE user [modes]
impl Mode<UserMode> {
    // TODO: turning more edge cases into errors.
    /// Parses the specified mode string as user modes.
    pub fn as_user_modes(s: &str) -> error::Result<Vec<Mode<UserMode>>> {
        use self::PlusMinus::*;

        let mut res = vec![];
        let mut pieces = s.split(" ");
        for term in pieces.clone() {
            if term.starts_with("+") || term.starts_with("-") {
                let _ = pieces.next();

                let mut chars = term.chars();
                let init = match chars.next() {
                    Some('+') => Plus,
                    Some('-') => Minus,
                    _ => return Err(error::ErrorKind::ModeParsingFailed.into()),
                };

                for c in chars {
                    let mode = UserMode::from_char(c)?;
                    let arg = if mode.takes_arg() {
                        pieces.next()
                    } else {
                        None
                    };
                    res.push(match init {
                        Plus => Mode::Plus(mode, arg.map(|s| s.to_owned())),
                        Minus => Mode::Minus(mode, arg.map(|s| s.to_owned())),
                    })
                }
            }
        }

        Ok(res)
    }
}

// MODE channel [modes [modeparams]]
impl Mode<ChannelMode> {
    // TODO: turning more edge cases into errors.
    /// Parses the specified mode string as channel modes.
    pub fn as_channel_modes(s: &str) -> error::Result<Vec<Mode<ChannelMode>>> {
        use self::PlusMinus::*;

        let mut res = vec![];
        let mut pieces = s.split(" ");
        for term in pieces.clone() {
            if term.starts_with("+") || term.starts_with("-") {
                let _ = pieces.next();

                let mut chars = term.chars();
                let init = match chars.next() {
                    Some('+') => Plus,
                    Some('-') => Minus,
                    _ => return Err(error::ErrorKind::ModeParsingFailed.into()),
                };

                for c in chars {
                    let mode = ChannelMode::from_char(c)?;
                    let arg = if mode.takes_arg() {
                        pieces.next()
                    } else {
                        None
                    };
                    res.push(match init {
                        Plus => Mode::Plus(mode, arg.map(|s| s.to_owned())),
                        Minus => Mode::Minus(mode, arg.map(|s| s.to_owned())),
                    })
                }
            }
        }

        Ok(res)
    }
}
<|endoftext|>


<commit_before>use super::schema::*;

#[derive(Queryable, Identifiable)]
#[table_name="question"]
pub struct Question {
    pub id: i32,
    pub category_id: i32,
    pub text: String,
    pub answers: Vec<Answer>,
}

HasMany! {
    (question, foreign_key = category_id)
    #[table_name(category)]
    #[derive(Queryable, Identifiable)]
    pub struct Category {
        pub id: i32,
        pub text: String,
    }
}

#[derive(Queryable, Identifiable)]
#[table_name="answer"]
pub struct Answer {
    pub id: i32,
    pub text: String,
    pub is_correct: bool,
}

pub struct Player {}

pub struct Game {}
<commit_msg>Let a question have multiple answers<commit_after>use super::schema::*;

#[derive(Queryable, Identifiable)]
#[table_name="answer"]
pub struct Answer {
    pub id: i32,
    pub question_id: i32,
    pub text: String,
    pub is_correct: bool,
}

HasMany! {
    (answer, foreign_key = question_id)
    #[table_name(question)]
    #[derive(Queryable, Identifiable)]
    pub struct Question {
        pub id: i32,
        pub category_id: i32,
        pub text: String,
        pub answers: Vec<Answer>,
    }
}

HasMany! {
    (question, foreign_key = category_id)
    #[table_name(category)]
    #[derive(Queryable, Identifiable)]
    pub struct Category {
        pub id: i32,
        pub text: String,
    }
}

pub struct Player {}

pub struct Game {}
<|endoftext|>