<commit_before>use libeditor::{FileSymbol, file_symbols};
use libsyntax2::{
    File,
    SyntaxKind::{self, *},
};
use fst::{self, Streamer};
use rayon::prelude::*;
use {Query, FileId, JobToken};

#[derive(Debug)]
pub(crate) struct SymbolIndex {
    symbols: Vec<(FileId, FileSymbol)>,
    map: fst::Map,
}

impl SymbolIndex {
    pub(crate) fn for_files(files: impl ParallelIterator<Item=(FileId, File)>) -> SymbolIndex {
        let mut symbols = files
            .flat_map(|(file_id, file)| {
                file_symbols(&file)
                    .into_iter()
                    .map(move |symbol| {
                        (symbol.name.as_str().to_lowercase(), (file_id, symbol))
                    })
                    .collect::<Vec<_>>()
            })
            .collect::<Vec<_>>();
        symbols.par_sort_by(|s1, s2| s1.0.cmp(&s2.0));
        symbols.dedup_by(|s1, s2| s1.0 == s2.0);
        let (names, symbols): (Vec<String>, Vec<(FileId, FileSymbol)>) =
            symbols.into_iter().unzip();
        let map = fst::Map::from_iter(
            names.into_iter().zip(0u64..)
        ).unwrap();
        SymbolIndex { symbols, map }
    }

    pub(crate) fn for_file(file_id: FileId, file: File) -> SymbolIndex {
        SymbolIndex::for_files(::rayon::iter::once((file_id, file)))
    }
}

impl Query {
    pub(crate) fn search(
        mut self,
        indices: &[&SymbolIndex],
        token: &JobToken,
    ) -> Vec<(FileId, FileSymbol)> {

        let mut op = fst::map::OpBuilder::new();
        for file_symbols in indices.iter() {
            let automaton = fst::automaton::Subsequence::new(&self.lowercased);
            op = op.add(file_symbols.map.search(automaton))
        }
        let mut stream = op.union();
        let mut res = Vec::new();
        while let Some((_, indexed_values)) = stream.next() {
            if self.limit == 0 || token.is_canceled() {
                break;
            }
            for indexed_value in indexed_values {
                let file_symbols = &indices[indexed_value.index];
                let idx = indexed_value.value as usize;

                let (file_id, symbol) = &file_symbols.symbols[idx];
                if self.only_types && !is_type(symbol.kind) {
                    continue;
                }
                if self.exact && symbol.name != self.query {
                    continue;
                }
                res.push((*file_id, symbol.clone()));
                self.limit -= 1;
            }
        }
        res
    }
}

fn is_type(kind: SyntaxKind) -> bool {
    match kind {
        STRUCT_DEF | ENUM_DEF | TRAIT_DEF | TYPE_DEF => true,
        _ => false,
    }
}
<commit_msg>Don't overflow when limiting symbol search<commit_after>use libeditor::{FileSymbol, file_symbols};
use libsyntax2::{
    File,
    SyntaxKind::{self, *},
};
use fst::{self, Streamer};
use rayon::prelude::*;
use {Query, FileId, JobToken};

#[derive(Debug)]
pub(crate) struct SymbolIndex {
    symbols: Vec<(FileId, FileSymbol)>,
    map: fst::Map,
}

impl SymbolIndex {
    pub(crate) fn for_files(files: impl ParallelIterator<Item=(FileId, File)>) -> SymbolIndex {
        let mut symbols = files
            .flat_map(|(file_id, file)| {
                file_symbols(&file)
                    .into_iter()
                    .map(move |symbol| {
                        (symbol.name.as_str().to_lowercase(), (file_id, symbol))
                    })
                    .collect::<Vec<_>>()
            })
            .collect::<Vec<_>>();
        symbols.par_sort_by(|s1, s2| s1.0.cmp(&s2.0));
        symbols.dedup_by(|s1, s2| s1.0 == s2.0);
        let (names, symbols): (Vec<String>, Vec<(FileId, FileSymbol)>) =
            symbols.into_iter().unzip();
        let map = fst::Map::from_iter(
            names.into_iter().zip(0u64..)
        ).unwrap();
        SymbolIndex { symbols, map }
    }

    pub(crate) fn for_file(file_id: FileId, file: File) -> SymbolIndex {
        SymbolIndex::for_files(::rayon::iter::once((file_id, file)))
    }
}

impl Query {
    pub(crate) fn search(
        self,
        indices: &[&SymbolIndex],
        token: &JobToken,
    ) -> Vec<(FileId, FileSymbol)> {

        let mut op = fst::map::OpBuilder::new();
        for file_symbols in indices.iter() {
            let automaton = fst::automaton::Subsequence::new(&self.lowercased);
            op = op.add(file_symbols.map.search(automaton))
        }
        let mut stream = op.union();
        let mut res = Vec::new();
        while let Some((_, indexed_values)) = stream.next() {
            if res.len() >= self.limit || token.is_canceled() {
                break;
            }
            for indexed_value in indexed_values {
                let file_symbols = &indices[indexed_value.index];
                let idx = indexed_value.value as usize;

                let (file_id, symbol) = &file_symbols.symbols[idx];
                if self.only_types && !is_type(symbol.kind) {
                    continue;
                }
                if self.exact && symbol.name != self.query {
                    continue;
                }
                res.push((*file_id, symbol.clone()));
            }
        }
        res
    }
}

fn is_type(kind: SyntaxKind) -> bool {
    match kind {
        STRUCT_DEF | ENUM_DEF | TRAIT_DEF | TYPE_DEF => true,
        _ => false,
    }
}
<|endoftext|>


<commit_before>use super::coincident_tokens::CoincidentTokenIndex;
use super::item::TokenSet;
use super::token_conflicts::TokenConflictMap;
use crate::generate::grammars::{LexicalGrammar, SyntaxGrammar};
use crate::generate::nfa::{CharacterSet, NfaCursor};
use crate::generate::rules::Symbol;
use crate::generate::tables::{AdvanceAction, LexState, LexTable, ParseStateId, ParseTable};
use std::collections::hash_map::Entry;
use std::collections::{BTreeMap, HashMap, VecDeque};

pub(crate) fn build_lex_table(
    parse_table: &mut ParseTable,
    syntax_grammar: &SyntaxGrammar,
    lexical_grammar: &LexicalGrammar,
    keywords: &TokenSet,
    coincident_token_index: &CoincidentTokenIndex,
    token_conflict_map: &TokenConflictMap,
    minimize: bool,
) -> (LexTable, LexTable) {
    let keyword_lex_table;
    if syntax_grammar.word_token.is_some() {
        let mut builder = LexTableBuilder::new(lexical_grammar);
        builder.add_state_for_tokens(keywords);
        keyword_lex_table = builder.table;
    } else {
        keyword_lex_table = LexTable::default();
    }

    let mut parse_state_ids_by_token_set: Vec<(TokenSet, Vec<ParseStateId>)> = Vec::new();
    for (i, state) in parse_table.states.iter().enumerate() {
        let tokens = state
            .terminal_entries
            .keys()
            .filter_map(|token| {
                if token.is_terminal() {
                    if keywords.contains(&token) {
                        syntax_grammar.word_token
                    } else {
                        Some(*token)
                    }
                } else if token.is_eof() {
                    Some(*token)
                } else {
                    None
                }
            })
            .collect();

        let mut did_merge = false;
        for entry in parse_state_ids_by_token_set.iter_mut() {
            if merge_token_set(
                &mut entry.0,
                &tokens,
                lexical_grammar,
                token_conflict_map,
                coincident_token_index,
            ) {
                did_merge = true;
                entry.1.push(i);
                break;
            }
        }

        if !did_merge {
            parse_state_ids_by_token_set.push((tokens, vec![i]));
        }
    }

    let mut builder = LexTableBuilder::new(lexical_grammar);
    for (tokens, parse_state_ids) in parse_state_ids_by_token_set {
        let lex_state_id = builder.add_state_for_tokens(&tokens);
        for id in parse_state_ids {
            parse_table.states[id].lex_state_id = lex_state_id;
        }
    }

    let mut table = builder.table;

    if minimize {
        minimize_lex_table(&mut table, parse_table);
    }

    (table, keyword_lex_table)
}

struct QueueEntry {
    state_id: usize,
    nfa_states: Vec<u32>,
    eof_valid: bool,
}

struct LexTableBuilder<'a> {
    lexical_grammar: &'a LexicalGrammar,
    cursor: NfaCursor<'a>,
    table: LexTable,
    state_queue: VecDeque<QueueEntry>,
    state_ids_by_nfa_state_set: HashMap<(Vec<u32>, bool), usize>,
}

impl<'a> LexTableBuilder<'a> {
    fn new(lexical_grammar: &'a LexicalGrammar) -> Self {
        Self {
            lexical_grammar,
            cursor: NfaCursor::new(&lexical_grammar.nfa, vec![]),
            table: LexTable::default(),
            state_queue: VecDeque::new(),
            state_ids_by_nfa_state_set: HashMap::new(),
        }
    }

    fn add_state_for_tokens(&mut self, tokens: &TokenSet) -> usize {
        let mut eof_valid = false;
        let nfa_states = tokens
            .iter()
            .filter_map(|token| {
                if token.is_terminal() {
                    Some(self.lexical_grammar.variables[token.index].start_state)
                } else {
                    eof_valid = true;
                    None
                }
            })
            .collect();
        let (state_id, is_new) = self.add_state(nfa_states, eof_valid);

        if is_new {
            info!(
                "entry point state: {}, tokens: {:?}",
                state_id,
                tokens
                    .iter()
                    .map(|t| &self.lexical_grammar.variables[t.index].name)
                    .collect::<Vec<_>>()
            );
        }

        while let Some(QueueEntry {
            state_id,
            nfa_states,
            eof_valid,
        }) = self.state_queue.pop_front()
        {
            self.populate_state(state_id, nfa_states, eof_valid);
        }
        state_id
    }

    fn add_state(&mut self, nfa_states: Vec<u32>, eof_valid: bool) -> (usize, bool) {
        self.cursor.reset(nfa_states);
        match self
            .state_ids_by_nfa_state_set
            .entry((self.cursor.state_ids.clone(), eof_valid))
        {
            Entry::Occupied(o) => (*o.get(), false),
            Entry::Vacant(v) => {
                let state_id = self.table.states.len();
                self.table.states.push(LexState::default());
                self.state_queue.push_back(QueueEntry {
                    state_id,
                    nfa_states: v.key().0.clone(),
                    eof_valid,
                });
                v.insert(state_id);
                (state_id, true)
            }
        }
    }

    fn populate_state(&mut self, state_id: usize, nfa_states: Vec<u32>, eof_valid: bool) {
        self.cursor.force_reset(nfa_states);

        // The EOF state is represented as an empty list of NFA states.
        let mut completion = None;
        for (id, prec) in self.cursor.completions() {
            if let Some((prev_id, prev_precedence)) = completion {
                if TokenConflictMap::prefer_token(
                    self.lexical_grammar,
                    (prev_precedence, prev_id),
                    (prec, id),
                ) {
                    continue;
                }
            }
            completion = Some((id, prec));
        }

        info!(
            "lex state: {}, completion: {:?}",
            state_id,
            completion.map(|(id, prec)| (&self.lexical_grammar.variables[id].name, prec))
        );

        let transitions = self.cursor.transitions();
        let has_sep = self.cursor.transition_chars().any(|(_, sep)| sep);
        info!("lex state: {}, transitions: {:?}", state_id, transitions);

        // If EOF is a valid lookahead token, add a transition predicated on the null
        // character that leads to the empty set of NFA states.
        if eof_valid {
            let (next_state_id, _) = self.add_state(Vec::new(), false);
            info!("lex state: {}, successor: EOF", state_id);
            self.table.states[state_id].advance_actions.push((
                CharacterSet::empty().add_char('\0'),
                AdvanceAction {
                    state: Some(next_state_id),
                    in_main_token: true,
                },
            ));
        }

        for transition in transitions {
            if let Some((completed_id, completed_precedence)) = completion {
                if !TokenConflictMap::prefer_transition(
                    &self.lexical_grammar,
                    &transition,
                    completed_id,
                    completed_precedence,
                    has_sep,
                ) {
                    continue;
                }
            }

            let (next_state_id, _) =
                self.add_state(transition.states, eof_valid && transition.is_separator);
            let next_state = if next_state_id == state_id {
                None
            } else {
                Some(next_state_id)
            };
            self.table.states[state_id].advance_actions.push((
                transition.characters,
                AdvanceAction {
                    state: next_state,
                    in_main_token: !transition.is_separator,
                },
            ));
        }

        if let Some((complete_id, _)) = completion {
            self.table.states[state_id].accept_action = Some(Symbol::terminal(complete_id));
        } else if self.cursor.state_ids.is_empty() {
            self.table.states[state_id].accept_action = Some(Symbol::end());
        }
    }
}

fn merge_token_set(
    tokens: &mut TokenSet,
    other: &TokenSet,
    lexical_grammar: &LexicalGrammar,
    token_conflict_map: &TokenConflictMap,
    coincident_token_index: &CoincidentTokenIndex,
) -> bool {
    for i in 0..lexical_grammar.variables.len() {
        let symbol = Symbol::terminal(i);
        let set_without_terminal = match (tokens.contains_terminal(i), other.contains_terminal(i)) {
            (true, false) => other,
            (false, true) => tokens,
            _ => continue,
        };

        for existing_token in set_without_terminal.terminals() {
            if token_conflict_map.does_conflict(i, existing_token.index)
                || !coincident_token_index.contains(symbol, existing_token)
            {
                return false;
            }
        }
    }

    tokens.insert_all(other);
    true
}

fn minimize_lex_table(table: &mut LexTable, parse_table: &mut ParseTable) {
    let mut state_replacements = BTreeMap::new();
    let mut done = false;
    while !done {
        done = true;
        for (i, state_i) in table.states.iter().enumerate() {
            if state_replacements.contains_key(&i) {
                continue;
            }
            for (j, state_j) in table.states.iter().enumerate() {
                if j == i {
                    break;
                }
                if state_replacements.contains_key(&j) {
                    continue;
                }
                if state_i == state_j {
                    info!("replace state {} with state {}", i, j);
                    state_replacements.insert(i, j);
                    done = false;
                    break;
                }
            }
        }
        for state in table.states.iter_mut() {
            for (_, advance_action) in state.advance_actions.iter_mut() {
                advance_action.state = advance_action
                    .state
                    .map(|s| state_replacements.get(&s).cloned().unwrap_or(s))
            }
        }
    }

    let final_state_replacements = (0..table.states.len())
        .into_iter()
        .map(|state_id| {
            let replacement = state_replacements
                .get(&state_id)
                .cloned()
                .unwrap_or(state_id);
            let prior_removed = state_replacements
                .iter()
                .take_while(|i| *i.0 < replacement)
                .count();
            replacement - prior_removed
        })
        .collect::<Vec<_>>();

    for state in parse_table.states.iter_mut() {
        state.lex_state_id = final_state_replacements[state.lex_state_id];
    }

    for state in table.states.iter_mut() {
        for (_, advance_action) in state.advance_actions.iter_mut() {
            advance_action.state = advance_action.state.map(|s| final_state_replacements[s]);
        }
    }

    let mut i = 0;
    table.states.retain(|_| {
        let result = !state_replacements.contains_key(&i);
        i += 1;
        result
    });
}
<commit_msg>Remove noisy logging<commit_after>use super::coincident_tokens::CoincidentTokenIndex;
use super::item::TokenSet;
use super::token_conflicts::TokenConflictMap;
use crate::generate::grammars::{LexicalGrammar, SyntaxGrammar};
use crate::generate::nfa::{CharacterSet, NfaCursor};
use crate::generate::rules::Symbol;
use crate::generate::tables::{AdvanceAction, LexState, LexTable, ParseStateId, ParseTable};
use std::collections::hash_map::Entry;
use std::collections::{BTreeMap, HashMap, VecDeque};

pub(crate) fn build_lex_table(
    parse_table: &mut ParseTable,
    syntax_grammar: &SyntaxGrammar,
    lexical_grammar: &LexicalGrammar,
    keywords: &TokenSet,
    coincident_token_index: &CoincidentTokenIndex,
    token_conflict_map: &TokenConflictMap,
    minimize: bool,
) -> (LexTable, LexTable) {
    let keyword_lex_table;
    if syntax_grammar.word_token.is_some() {
        let mut builder = LexTableBuilder::new(lexical_grammar);
        builder.add_state_for_tokens(keywords);
        keyword_lex_table = builder.table;
    } else {
        keyword_lex_table = LexTable::default();
    }

    let mut parse_state_ids_by_token_set: Vec<(TokenSet, Vec<ParseStateId>)> = Vec::new();
    for (i, state) in parse_table.states.iter().enumerate() {
        let tokens = state
            .terminal_entries
            .keys()
            .filter_map(|token| {
                if token.is_terminal() {
                    if keywords.contains(&token) {
                        syntax_grammar.word_token
                    } else {
                        Some(*token)
                    }
                } else if token.is_eof() {
                    Some(*token)
                } else {
                    None
                }
            })
            .collect();

        let mut did_merge = false;
        for entry in parse_state_ids_by_token_set.iter_mut() {
            if merge_token_set(
                &mut entry.0,
                &tokens,
                lexical_grammar,
                token_conflict_map,
                coincident_token_index,
            ) {
                did_merge = true;
                entry.1.push(i);
                break;
            }
        }

        if !did_merge {
            parse_state_ids_by_token_set.push((tokens, vec![i]));
        }
    }

    let mut builder = LexTableBuilder::new(lexical_grammar);
    for (tokens, parse_state_ids) in parse_state_ids_by_token_set {
        let lex_state_id = builder.add_state_for_tokens(&tokens);
        for id in parse_state_ids {
            parse_table.states[id].lex_state_id = lex_state_id;
        }
    }

    let mut table = builder.table;

    if minimize {
        minimize_lex_table(&mut table, parse_table);
    }

    (table, keyword_lex_table)
}

struct QueueEntry {
    state_id: usize,
    nfa_states: Vec<u32>,
    eof_valid: bool,
}

struct LexTableBuilder<'a> {
    lexical_grammar: &'a LexicalGrammar,
    cursor: NfaCursor<'a>,
    table: LexTable,
    state_queue: VecDeque<QueueEntry>,
    state_ids_by_nfa_state_set: HashMap<(Vec<u32>, bool), usize>,
}

impl<'a> LexTableBuilder<'a> {
    fn new(lexical_grammar: &'a LexicalGrammar) -> Self {
        Self {
            lexical_grammar,
            cursor: NfaCursor::new(&lexical_grammar.nfa, vec![]),
            table: LexTable::default(),
            state_queue: VecDeque::new(),
            state_ids_by_nfa_state_set: HashMap::new(),
        }
    }

    fn add_state_for_tokens(&mut self, tokens: &TokenSet) -> usize {
        let mut eof_valid = false;
        let nfa_states = tokens
            .iter()
            .filter_map(|token| {
                if token.is_terminal() {
                    Some(self.lexical_grammar.variables[token.index].start_state)
                } else {
                    eof_valid = true;
                    None
                }
            })
            .collect();
        let (state_id, is_new) = self.add_state(nfa_states, eof_valid);

        if is_new {
            info!(
                "entry point state: {}, tokens: {:?}",
                state_id,
                tokens
                    .iter()
                    .map(|t| &self.lexical_grammar.variables[t.index].name)
                    .collect::<Vec<_>>()
            );
        }

        while let Some(QueueEntry {
            state_id,
            nfa_states,
            eof_valid,
        }) = self.state_queue.pop_front()
        {
            self.populate_state(state_id, nfa_states, eof_valid);
        }
        state_id
    }

    fn add_state(&mut self, nfa_states: Vec<u32>, eof_valid: bool) -> (usize, bool) {
        self.cursor.reset(nfa_states);
        match self
            .state_ids_by_nfa_state_set
            .entry((self.cursor.state_ids.clone(), eof_valid))
        {
            Entry::Occupied(o) => (*o.get(), false),
            Entry::Vacant(v) => {
                let state_id = self.table.states.len();
                self.table.states.push(LexState::default());
                self.state_queue.push_back(QueueEntry {
                    state_id,
                    nfa_states: v.key().0.clone(),
                    eof_valid,
                });
                v.insert(state_id);
                (state_id, true)
            }
        }
    }

    fn populate_state(&mut self, state_id: usize, nfa_states: Vec<u32>, eof_valid: bool) {
        self.cursor.force_reset(nfa_states);

        // The EOF state is represented as an empty list of NFA states.
        let mut completion = None;
        for (id, prec) in self.cursor.completions() {
            if let Some((prev_id, prev_precedence)) = completion {
                if TokenConflictMap::prefer_token(
                    self.lexical_grammar,
                    (prev_precedence, prev_id),
                    (prec, id),
                ) {
                    continue;
                }
            }
            completion = Some((id, prec));
        }

        let transitions = self.cursor.transitions();
        let has_sep = self.cursor.transition_chars().any(|(_, sep)| sep);

        // If EOF is a valid lookahead token, add a transition predicated on the null
        // character that leads to the empty set of NFA states.
        if eof_valid {
            let (next_state_id, _) = self.add_state(Vec::new(), false);
            self.table.states[state_id].advance_actions.push((
                CharacterSet::empty().add_char('\0'),
                AdvanceAction {
                    state: Some(next_state_id),
                    in_main_token: true,
                },
            ));
        }

        for transition in transitions {
            if let Some((completed_id, completed_precedence)) = completion {
                if !TokenConflictMap::prefer_transition(
                    &self.lexical_grammar,
                    &transition,
                    completed_id,
                    completed_precedence,
                    has_sep,
                ) {
                    continue;
                }
            }

            let (next_state_id, _) =
                self.add_state(transition.states, eof_valid && transition.is_separator);
            let next_state = if next_state_id == state_id {
                None
            } else {
                Some(next_state_id)
            };
            self.table.states[state_id].advance_actions.push((
                transition.characters,
                AdvanceAction {
                    state: next_state,
                    in_main_token: !transition.is_separator,
                },
            ));
        }

        if let Some((complete_id, _)) = completion {
            self.table.states[state_id].accept_action = Some(Symbol::terminal(complete_id));
        } else if self.cursor.state_ids.is_empty() {
            self.table.states[state_id].accept_action = Some(Symbol::end());
        }
    }
}

fn merge_token_set(
    tokens: &mut TokenSet,
    other: &TokenSet,
    lexical_grammar: &LexicalGrammar,
    token_conflict_map: &TokenConflictMap,
    coincident_token_index: &CoincidentTokenIndex,
) -> bool {
    for i in 0..lexical_grammar.variables.len() {
        let symbol = Symbol::terminal(i);
        let set_without_terminal = match (tokens.contains_terminal(i), other.contains_terminal(i)) {
            (true, false) => other,
            (false, true) => tokens,
            _ => continue,
        };

        for existing_token in set_without_terminal.terminals() {
            if token_conflict_map.does_conflict(i, existing_token.index)
                || !coincident_token_index.contains(symbol, existing_token)
            {
                return false;
            }
        }
    }

    tokens.insert_all(other);
    true
}

fn minimize_lex_table(table: &mut LexTable, parse_table: &mut ParseTable) {
    let mut state_replacements = BTreeMap::new();
    let mut done = false;
    while !done {
        done = true;
        for (i, state_i) in table.states.iter().enumerate() {
            if state_replacements.contains_key(&i) {
                continue;
            }
            for (j, state_j) in table.states.iter().enumerate() {
                if j == i {
                    break;
                }
                if state_replacements.contains_key(&j) {
                    continue;
                }
                if state_i == state_j {
                    info!("replace state {} with state {}", i, j);
                    state_replacements.insert(i, j);
                    done = false;
                    break;
                }
            }
        }
        for state in table.states.iter_mut() {
            for (_, advance_action) in state.advance_actions.iter_mut() {
                advance_action.state = advance_action
                    .state
                    .map(|s| state_replacements.get(&s).cloned().unwrap_or(s))
            }
        }
    }

    let final_state_replacements = (0..table.states.len())
        .into_iter()
        .map(|state_id| {
            let replacement = state_replacements
                .get(&state_id)
                .cloned()
                .unwrap_or(state_id);
            let prior_removed = state_replacements
                .iter()
                .take_while(|i| *i.0 < replacement)
                .count();
            replacement - prior_removed
        })
        .collect::<Vec<_>>();

    for state in parse_table.states.iter_mut() {
        state.lex_state_id = final_state_replacements[state.lex_state_id];
    }

    for state in table.states.iter_mut() {
        for (_, advance_action) in state.advance_actions.iter_mut() {
            advance_action.state = advance_action.state.map(|s| final_state_replacements[s]);
        }
    }

    let mut i = 0;
    table.states.retain(|_| {
        let result = !state_replacements.contains_key(&i);
        i += 1;
        result
    });
}
<|endoftext|>


<commit_before><commit_msg>Remove temporary GpuLocation enum as it's no longer required.<commit_after><|endoftext|>


<commit_before>//! Completion of names from the current scope, e.g. locals and imported items.

use hir::ScopeDef;
use syntax::{ast, AstNode};

use crate::{patterns::ImmediateLocation, CompletionContext, Completions};

pub(crate) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionContext) {
    if ctx.is_path_disallowed() || !ctx.is_trivial_path() || ctx.has_impl_or_trait_prev_sibling() {
        return;
    }

    if ctx.in_use_tree() {
        // only show modules in a fresh UseTree
        cov_mark::hit!(unqualified_path_only_modules_in_import);
        ctx.process_all_names(&mut |name, res| {
            if let ScopeDef::ModuleDef(hir::ModuleDef::Module(_)) = res {
                acc.add_resolution(ctx, name, &res);
            }
        });

        std::array::IntoIter::new(["self::", "super::", "crate::"])
            .for_each(|kw| acc.add_keyword(ctx, kw));
        return;
    }
    std::array::IntoIter::new(["self", "super", "crate"]).for_each(|kw| acc.add_keyword(ctx, kw));

    match &ctx.completion_location {
        Some(ImmediateLocation::Visibility(_)) => return,
        Some(ImmediateLocation::ItemList | ImmediateLocation::Trait | ImmediateLocation::Impl) => {
            // only show macros in {Assoc}ItemList
            ctx.process_all_names(&mut |name, res| {
                if let hir::ScopeDef::MacroDef(mac) = res {
                    if mac.is_fn_like() {
                        acc.add_macro(ctx, Some(name.clone()), mac);
                    }
                }
                if let hir::ScopeDef::ModuleDef(hir::ModuleDef::Module(_)) = res {
                    acc.add_resolution(ctx, name, &res);
                }
            });
            return;
        }
        Some(ImmediateLocation::TypeBound) => {
            ctx.process_all_names(&mut |name, res| {
                let add_resolution = match res {
                    ScopeDef::MacroDef(mac) => mac.is_fn_like(),
                    ScopeDef::ModuleDef(hir::ModuleDef::Trait(_) | hir::ModuleDef::Module(_)) => {
                        true
                    }
                    _ => false,
                };
                if add_resolution {
                    acc.add_resolution(ctx, name, &res);
                }
            });
            return;
        }
        _ => (),
    }

    if !ctx.expects_type() {
        if let Some(hir::Adt::Enum(e)) =
            ctx.expected_type.as_ref().and_then(|ty| ty.strip_references().as_adt())
        {
            super::enum_variants_with_paths(acc, ctx, e, |acc, ctx, variant, path| {
                acc.add_qualified_enum_variant(ctx, variant, path)
            });
        }
    }

    if let Some(ImmediateLocation::GenericArgList(arg_list)) = &ctx.completion_location {
        if let Some(path_seg) = arg_list.syntax().parent().and_then(ast::PathSegment::cast) {
            if let Some(hir::PathResolution::Def(hir::ModuleDef::Trait(trait_))) =
                ctx.sema.resolve_path(&path_seg.parent_path())
            {
                trait_.items(ctx.sema.db).into_iter().for_each(|it| {
                    if let hir::AssocItem::TypeAlias(alias) = it {
                        acc.add_type_alias_with_eq(ctx, alias)
                    }
                });
            }
        }
    }

    ctx.process_all_names(&mut |name, res| {
        if let ScopeDef::GenericParam(hir::GenericParam::LifetimeParam(_)) | ScopeDef::Label(_) =
            res
        {
            cov_mark::hit!(unqualified_skip_lifetime_completion);
            return;
        }
        let add_resolution = match res {
            ScopeDef::ImplSelfType(_) => {
                !ctx.previous_token_is(syntax::T![impl]) && !ctx.previous_token_is(syntax::T![for])
            }
            // Don't suggest attribute macros and derives.
            ScopeDef::MacroDef(mac) => mac.is_fn_like(),
            // no values in type places
            ScopeDef::ModuleDef(
                hir::ModuleDef::Function(_)
                | hir::ModuleDef::Variant(_)
                | hir::ModuleDef::Static(_),
            )
            | ScopeDef::Local(_) => !ctx.expects_type(),
            // unless its a constant in a generic arg list position
            ScopeDef::ModuleDef(hir::ModuleDef::Const(_))
            | ScopeDef::GenericParam(hir::GenericParam::ConstParam(_)) => {
                !ctx.expects_type() || ctx.expects_generic_arg()
            }
            _ => true,
        };
        if add_resolution {
            acc.add_resolution(ctx, name, &res);
        }
    });
}

#[cfg(test)]
mod tests {
    use expect_test::{expect, Expect};

    use crate::{
        tests::{check_edit, filtered_completion_list_with_config, TEST_CONFIG},
        CompletionConfig, CompletionKind,
    };

    fn check(ra_fixture: &str, expect: Expect) {
        check_with_config(TEST_CONFIG, ra_fixture, expect);
    }

    fn check_with_config(config: CompletionConfig, ra_fixture: &str, expect: Expect) {
        let actual =
            filtered_completion_list_with_config(config, ra_fixture, CompletionKind::Reference);
        expect.assert_eq(&actual)
    }

    #[test]
    fn completes_if_prefix_is_keyword() {
        cov_mark::check!(completes_if_prefix_is_keyword);
        check_edit(
            "wherewolf",
            r#"
fn main() {
    let wherewolf = 92;
    drop(where$0)
}
"#,
            r#"
fn main() {
    let wherewolf = 92;
    drop(wherewolf)
}
"#,
        )
    }

    /// Regression test for issue #6091.
    #[test]
    fn correctly_completes_module_items_prefixed_with_underscore() {
        check_edit(
            "_alpha",
            r#"
fn main() {
    _$0
}
fn _alpha() {}
"#,
            r#"
fn main() {
    _alpha()$0
}
fn _alpha() {}
"#,
        )
    }

    #[test]
    fn completes_prelude() {
        check(
            r#"
//- /main.rs crate:main deps:std
fn foo() { let x: $0 }

//- /std/lib.rs crate:std
pub mod prelude {
    pub mod rust_2018 {
        pub struct Option;
    }
}
"#,
            expect![[r#"
                md std
                st Option
            "#]],
        );
    }

    #[test]
    fn completes_prelude_macros() {
        check(
            r#"
//- /main.rs crate:main deps:std
fn f() {$0}

//- /std/lib.rs crate:std
pub mod prelude {
    pub mod rust_2018 {
        pub use crate::concat;
    }
}

mod macros {
    #[rustc_builtin_macro]
    #[macro_export]
    macro_rules! concat { }
}
"#,
            expect![[r##"
                fn f()        fn()
                ma concat!(…) #[macro_export] macro_rules! concat
                md std
            "##]],
        );
    }

    #[test]
    fn completes_std_prelude_if_core_is_defined() {
        check(
            r#"
//- /main.rs crate:main deps:core,std
fn foo() { let x: $0 }

//- /core/lib.rs crate:core
pub mod prelude {
    pub mod rust_2018 {
        pub struct Option;
    }
}

//- /std/lib.rs crate:std deps:core
pub mod prelude {
    pub mod rust_2018 {
        pub struct String;
    }
}
"#,
            expect![[r#"
                md std
                md core
                st String
            "#]],
        );
    }

    #[test]
    fn respects_doc_hidden() {
        check(
            r#"
//- /lib.rs crate:lib deps:std
fn f() {
    format_$0
}

//- /std.rs crate:std
#[doc(hidden)]
#[macro_export]
macro_rules! format_args_nl {
    () => {}
}

pub mod prelude {
    pub mod rust_2018 {}
}
            "#,
            expect![[r#"
                fn f() fn()
                md std
            "#]],
        );
    }

    #[test]
    fn respects_doc_hidden_in_assoc_item_list() {
        check(
            r#"
//- /lib.rs crate:lib deps:std
struct S;
impl S {
    format_$0
}

//- /std.rs crate:std
#[doc(hidden)]
#[macro_export]
macro_rules! format_args_nl {
    () => {}
}

pub mod prelude {
    pub mod rust_2018 {}
}
            "#,
            expect![[r#"
                md std
            "#]],
        );
    }

    #[test]
    fn local_variable_shadowing() {
        // FIXME: this isn't actually correct, should emit `x` only once.
        check(
            r#"
fn main() {
    let x = 92;
    {
        let x = 92;
        x$0;
    }
}
"#,
            expect![[r#"
                lc x      i32
                lc x      i32
                fn main() fn()
            "#]],
        );
    }
}
<commit_msg>simplify<commit_after>//! Completion of names from the current scope, e.g. locals and imported items.

use hir::ScopeDef;
use syntax::{ast, AstNode};

use crate::{patterns::ImmediateLocation, CompletionContext, Completions};

pub(crate) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionContext) {
    if ctx.is_path_disallowed() || !ctx.is_trivial_path() || ctx.has_impl_or_trait_prev_sibling() {
        return;
    }

    if ctx.in_use_tree() {
        // only show modules in a fresh UseTree
        cov_mark::hit!(unqualified_path_only_modules_in_import);
        ctx.process_all_names(&mut |name, res| {
            if let ScopeDef::ModuleDef(hir::ModuleDef::Module(_)) = res {
                acc.add_resolution(ctx, name, &res);
            }
        });

        std::array::IntoIter::new(["self::", "super::", "crate::"])
            .for_each(|kw| acc.add_keyword(ctx, kw));
        return;
    }
    std::array::IntoIter::new(["self", "super", "crate"]).for_each(|kw| acc.add_keyword(ctx, kw));

    match &ctx.completion_location {
        Some(ImmediateLocation::Visibility(_)) => return,
        Some(ImmediateLocation::ItemList | ImmediateLocation::Trait | ImmediateLocation::Impl) => {
            // only show macros in {Assoc}ItemList
            ctx.process_all_names(&mut |name, res| {
                if let hir::ScopeDef::MacroDef(mac) = res {
                    if mac.is_fn_like() {
                        acc.add_macro(ctx, Some(name.clone()), mac);
                    }
                }
                if let hir::ScopeDef::ModuleDef(hir::ModuleDef::Module(_)) = res {
                    acc.add_resolution(ctx, name, &res);
                }
            });
            return;
        }
        Some(ImmediateLocation::TypeBound) => {
            ctx.process_all_names(&mut |name, res| {
                let add_resolution = match res {
                    ScopeDef::MacroDef(mac) => mac.is_fn_like(),
                    ScopeDef::ModuleDef(hir::ModuleDef::Trait(_) | hir::ModuleDef::Module(_)) => {
                        true
                    }
                    _ => false,
                };
                if add_resolution {
                    acc.add_resolution(ctx, name, &res);
                }
            });
            return;
        }
        _ => (),
    }

    if !ctx.expects_type() {
        if let Some(hir::Adt::Enum(e)) =
            ctx.expected_type.as_ref().and_then(|ty| ty.strip_references().as_adt())
        {
            super::enum_variants_with_paths(acc, ctx, e, |acc, ctx, variant, path| {
                acc.add_qualified_enum_variant(ctx, variant, path)
            });
        }
    }

    if let Some(ImmediateLocation::GenericArgList(arg_list)) = &ctx.completion_location {
        if let Some(path_seg) = arg_list.syntax().parent().and_then(ast::PathSegment::cast) {
            if let Some(hir::PathResolution::Def(hir::ModuleDef::Trait(trait_))) =
                ctx.sema.resolve_path(&path_seg.parent_path())
            {
                trait_.items(ctx.sema.db).into_iter().for_each(|it| {
                    if let hir::AssocItem::TypeAlias(alias) = it {
                        acc.add_type_alias_with_eq(ctx, alias)
                    }
                });
            }
        }
    }

    ctx.process_all_names(&mut |name, res| {
        let add_resolution = match res {
            ScopeDef::GenericParam(hir::GenericParam::LifetimeParam(_)) | ScopeDef::Label(_) => {
                cov_mark::hit!(unqualified_skip_lifetime_completion);
                return;
            }
            ScopeDef::ImplSelfType(_) => {
                !ctx.previous_token_is(syntax::T![impl]) && !ctx.previous_token_is(syntax::T![for])
            }
            // Don't suggest attribute macros and derives.
            ScopeDef::MacroDef(mac) => mac.is_fn_like(),
            // no values in type places
            ScopeDef::ModuleDef(
                hir::ModuleDef::Function(_)
                | hir::ModuleDef::Variant(_)
                | hir::ModuleDef::Static(_),
            )
            | ScopeDef::Local(_) => !ctx.expects_type(),
            // unless its a constant in a generic arg list position
            ScopeDef::ModuleDef(hir::ModuleDef::Const(_))
            | ScopeDef::GenericParam(hir::GenericParam::ConstParam(_)) => {
                !ctx.expects_type() || ctx.expects_generic_arg()
            }
            _ => true,
        };
        if add_resolution {
            acc.add_resolution(ctx, name, &res);
        }
    });
}

#[cfg(test)]
mod tests {
    use expect_test::{expect, Expect};

    use crate::{
        tests::{check_edit, filtered_completion_list_with_config, TEST_CONFIG},
        CompletionConfig, CompletionKind,
    };

    fn check(ra_fixture: &str, expect: Expect) {
        check_with_config(TEST_CONFIG, ra_fixture, expect);
    }

    fn check_with_config(config: CompletionConfig, ra_fixture: &str, expect: Expect) {
        let actual =
            filtered_completion_list_with_config(config, ra_fixture, CompletionKind::Reference);
        expect.assert_eq(&actual)
    }

    #[test]
    fn completes_if_prefix_is_keyword() {
        cov_mark::check!(completes_if_prefix_is_keyword);
        check_edit(
            "wherewolf",
            r#"
fn main() {
    let wherewolf = 92;
    drop(where$0)
}
"#,
            r#"
fn main() {
    let wherewolf = 92;
    drop(wherewolf)
}
"#,
        )
    }

    /// Regression test for issue #6091.
    #[test]
    fn correctly_completes_module_items_prefixed_with_underscore() {
        check_edit(
            "_alpha",
            r#"
fn main() {
    _$0
}
fn _alpha() {}
"#,
            r#"
fn main() {
    _alpha()$0
}
fn _alpha() {}
"#,
        )
    }

    #[test]
    fn completes_prelude() {
        check(
            r#"
//- /main.rs crate:main deps:std
fn foo() { let x: $0 }

//- /std/lib.rs crate:std
pub mod prelude {
    pub mod rust_2018 {
        pub struct Option;
    }
}
"#,
            expect![[r#"
                md std
                st Option
            "#]],
        );
    }

    #[test]
    fn completes_prelude_macros() {
        check(
            r#"
//- /main.rs crate:main deps:std
fn f() {$0}

//- /std/lib.rs crate:std
pub mod prelude {
    pub mod rust_2018 {
        pub use crate::concat;
    }
}

mod macros {
    #[rustc_builtin_macro]
    #[macro_export]
    macro_rules! concat { }
}
"#,
            expect![[r##"
                fn f()        fn()
                ma concat!(…) #[macro_export] macro_rules! concat
                md std
            "##]],
        );
    }

    #[test]
    fn completes_std_prelude_if_core_is_defined() {
        check(
            r#"
//- /main.rs crate:main deps:core,std
fn foo() { let x: $0 }

//- /core/lib.rs crate:core
pub mod prelude {
    pub mod rust_2018 {
        pub struct Option;
    }
}

//- /std/lib.rs crate:std deps:core
pub mod prelude {
    pub mod rust_2018 {
        pub struct String;
    }
}
"#,
            expect![[r#"
                md std
                md core
                st String
            "#]],
        );
    }

    #[test]
    fn respects_doc_hidden() {
        check(
            r#"
//- /lib.rs crate:lib deps:std
fn f() {
    format_$0
}

//- /std.rs crate:std
#[doc(hidden)]
#[macro_export]
macro_rules! format_args_nl {
    () => {}
}

pub mod prelude {
    pub mod rust_2018 {}
}
            "#,
            expect![[r#"
                fn f() fn()
                md std
            "#]],
        );
    }

    #[test]
    fn respects_doc_hidden_in_assoc_item_list() {
        check(
            r#"
//- /lib.rs crate:lib deps:std
struct S;
impl S {
    format_$0
}

//- /std.rs crate:std
#[doc(hidden)]
#[macro_export]
macro_rules! format_args_nl {
    () => {}
}

pub mod prelude {
    pub mod rust_2018 {}
}
            "#,
            expect![[r#"
                md std
            "#]],
        );
    }

    #[test]
    fn local_variable_shadowing() {
        // FIXME: this isn't actually correct, should emit `x` only once.
        check(
            r#"
fn main() {
    let x = 92;
    {
        let x = 92;
        x$0;
    }
}
"#,
            expect![[r#"
                lc x      i32
                lc x      i32
                fn main() fn()
            "#]],
        );
    }
}
<|endoftext|>


<commit_before><commit_msg>minor fix<commit_after><|endoftext|>


<commit_before>// Copyright 2021 Google LLC
//
// Licensed 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 sfml::graphics::{Color, Image, RectangleShape, RenderTarget, Shape, Texture, Transformable};
use sfml::system::{Clock, Time, Vector2f};

use xsecurelock_saver::simple::Screensaver;

/// Simple screensaver that shows a rotating rectangle over a rotating textured square.
struct RotatingRectScreensaver<'t> {
    /// Clock used to track time.
    clock: Clock,
    /// Last update time, used to compute dt using clock.
    tick: Time,
    /// Rectangle shape to draw. Since it has no textures, uses `'static`.
    rect: RectangleShape<'static>,
    /// Second rectangle, demonstrating use of texture with lifetime. The texture is allocated
    /// before `run_saver` so it will outlive the screensaver instance.
    tex_rect: RectangleShape<'t>,
}

impl<'t> RotatingRectScreensaver<'t> {
    /// Updates the previous tick time and computes delta time.
    fn update_dt(&mut self) -> Time {
        let prev = std::mem::replace(&mut self.tick, self.clock.elapsed_time());
        self.tick - prev
    }
}

impl<'t> Screensaver for RotatingRectScreensaver<'t> {
    fn update(&mut self) {
        let dt = self.update_dt().as_seconds();
        self.rect.rotate(50.0 * dt);
        self.tex_rect.rotate(-20.0 * dt);
    }

    fn draw<T: RenderTarget>(&self, target: &mut T) {
        target.clear(Color::BLACK);
        target.draw(&self.rect);
        target.draw(&self.tex_rect);
    }
}

fn main() {
    let mut img = Image::new(256, 256);
    for x in 0..256 {
        for y in 0..256 {
            img.set_pixel(x, y, Color::rgb(x as u8, 0, y as u8));
        }
    }
    let tex = Texture::from_image(&img).expect("Failed to create texture");

    xsecurelock_saver::simple::run_saver(|screen_size| {
        let center = Vector2f::new(screen_size.x as f32 * 0.5, screen_size.y as f32 * 0.5);

        let mut rect = RectangleShape::with_size(Vector2f::new(500.0, 250.0));
        rect.set_fill_color(Color::GREEN);
        rect.set_outline_thickness(5.0);
        rect.set_outline_color(Color::rgb(0, 128, 0));
        rect.set_position(center);
        rect.set_origin(rect.size() * 0.5);

        let mut tex_rect = RectangleShape::with_texture(&tex);
        tex_rect.set_size((256.0, 256.0));
        tex_rect.set_position(center);
        tex_rect.set_origin(tex_rect.size() * 0.5);
        tex_rect.set_outline_thickness(2.0);
        tex_rect.set_outline_color(Color::MAGENTA);

        let clock = Clock::start();
        let tick = clock.elapsed_time();
        RotatingRectScreensaver {
            clock,
            tick,
            rect,
            tex_rect,
        }
    });
}
<commit_msg>a little more doc<commit_after>// Copyright 2021 Google LLC
//
// Licensed 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 sfml::graphics::{Color, Image, RectangleShape, RenderTarget, Shape, Texture, Transformable};
use sfml::system::{Clock, Time, Vector2f};

use xsecurelock_saver::simple::Screensaver;

/// Simple screensaver that shows a rotating rectangle over a rotating textured square.
struct RotatingRectScreensaver<'t> {
    /// Clock used to track time.
    clock: Clock,
    /// Last update time, used to compute dt using clock.
    tick: Time,
    /// Rectangle shape to draw. Since it has no textures, uses `'static`.
    rect: RectangleShape<'static>,
    /// Second rectangle, demonstrating use of texture with lifetime. The texture is allocated
    /// before `run_saver` so it will outlive the screensaver instance.
    tex_rect: RectangleShape<'t>,
}

impl<'t> RotatingRectScreensaver<'t> {
    /// Updates the previous tick time and computes delta time.
    fn update_dt(&mut self) -> Time {
        let prev = std::mem::replace(&mut self.tick, self.clock.elapsed_time());
        self.tick - prev
    }
}

impl<'t> Screensaver for RotatingRectScreensaver<'t> {
    fn update(&mut self) {
        let dt = self.update_dt().as_seconds();
        self.rect.rotate(50.0 * dt);
        self.tex_rect.rotate(-20.0 * dt);
    }

    fn draw<T: RenderTarget>(&self, target: &mut T) {
        target.clear(Color::BLACK);
        target.draw(&self.rect);
        target.draw(&self.tex_rect);
    }
}

fn main() {
    let mut img = Image::new(256, 256);
    for x in 0..256 {
        for y in 0..256 {
            img.set_pixel(x, y, Color::rgb(x as u8, 0, y as u8));
        }
    }
    let tex = Texture::from_image(&img).expect("Failed to create texture");

    // Closure can capture references that outlive the screensaver, allowing you to load textures in
    // `main` before starting the screensaver, and then reference them from the screensaver
    // instance.
    xsecurelock_saver::simple::run_saver(|screen_size| {
        let center = Vector2f::new(screen_size.x as f32 * 0.5, screen_size.y as f32 * 0.5);

        let mut rect = RectangleShape::with_size(Vector2f::new(500.0, 250.0));
        rect.set_fill_color(Color::GREEN);
        rect.set_outline_thickness(5.0);
        rect.set_outline_color(Color::rgb(0, 128, 0));
        rect.set_position(center);
        rect.set_origin(rect.size() * 0.5);

        let mut tex_rect = RectangleShape::with_texture(&tex);
        tex_rect.set_size((256.0, 256.0));
        tex_rect.set_position(center);
        tex_rect.set_origin(tex_rect.size() * 0.5);
        tex_rect.set_outline_thickness(2.0);
        tex_rect.set_outline_color(Color::MAGENTA);

        let clock = Clock::start();
        let tick = clock.elapsed_time();
        RotatingRectScreensaver {
            clock,
            tick,
            rect,
            tex_rect,
        }
    });
}
<|endoftext|>


<commit_before>use attrs::AttributeSpanWrapper;
use field::Field;
use model::Model;
use proc_macro2::TokenStream;
use quote::quote_spanned;
use syn::{DeriveInput, Expr, Path, Type};
use util::{inner_of_option_ty, is_option_ty, wrap_in_dummy_mod};

pub fn derive(item: DeriveInput) -> TokenStream {
    let model = Model::from_item(&item, false);

    let treat_none_as_default_value = model.treat_none_as_default_value();
    let table_name = &model.table_name();
    let struct_name = &item.ident;

    let (impl_generics, ty_generics, where_clause) = item.generics.split_for_impl();

    let mut generate_borrowed_insert = true;

    let mut direct_field_ty = Vec::with_capacity(model.fields().len());
    let mut direct_field_assign = Vec::with_capacity(model.fields().len());
    let mut ref_field_ty = Vec::with_capacity(model.fields().len());
    let mut ref_field_assign = Vec::with_capacity(model.fields().len());

    for field in model.fields() {
        match (field.serialize_as.as_ref(), field.embed()) {
            (None, true) => {
                direct_field_ty.push(field_ty_embed(field, None));
                direct_field_assign.push(field_expr_embed(field, None));
                ref_field_ty.push(field_ty_embed(field, Some(quote!(&'insert))));
                ref_field_assign.push(field_expr_embed(field, Some(quote!(&))));
            }
            (None, false) => {
                direct_field_ty.push(field_ty(
                    field,
                    table_name,
                    None,
                    treat_none_as_default_value,
                ));
                direct_field_assign.push(field_expr(
                    field,
                    table_name,
                    None,
                    treat_none_as_default_value,
                ));
                ref_field_ty.push(field_ty(
                    field,
                    table_name,
                    Some(quote!(&'insert)),
                    treat_none_as_default_value,
                ));
                ref_field_assign.push(field_expr(
                    field,
                    table_name,
                    Some(quote!(&)),
                    treat_none_as_default_value,
                ));
            }
            (Some(AttributeSpanWrapper { item: ty, .. }), false) => {
                direct_field_ty.push(field_ty_serialize_as(
                    field,
                    table_name,
                    ty,
                    treat_none_as_default_value,
                ));
                direct_field_assign.push(field_expr_serialize_as(
                    field,
                    table_name,
                    ty,
                    treat_none_as_default_value,
                ));

                generate_borrowed_insert = false; // as soon as we hit one field with #[diesel(serialize_as)] there is no point in generating the impl of Insertable for borrowed structs
            }
            (Some(AttributeSpanWrapper { attribute_span, .. }), true) => {
                abort!(
                    attribute_span,
                    "`#[diesel(embed)]` cannot be combined with `#[diesel(serialize_as)]`"
                )
            }
        }
    }

    let insert_owned = quote! {
        impl #impl_generics Insertable<#table_name::table> for #struct_name #ty_generics
            #where_clause
        {
            type Values = <(#(#direct_field_ty,)*) as Insertable<#table_name::table>>::Values;

            fn values(self) -> <(#(#direct_field_ty,)*) as Insertable<#table_name::table>>::Values {
                (#(#direct_field_assign,)*).values()
            }
        }
    };

    let insert_borrowed = if generate_borrowed_insert {
        let mut impl_generics = item.generics.clone();
        impl_generics.params.push(parse_quote!('insert));
        let (impl_generics, ..) = impl_generics.split_for_impl();

        quote! {
            impl #impl_generics Insertable<#table_name::table>
                for &'insert #struct_name #ty_generics
            #where_clause
            {
                type Values = <(#(#ref_field_ty,)*) as Insertable<#table_name::table>>::Values;

                fn values(self) -> <(#(#ref_field_ty,)*) as Insertable<#table_name::table>>::Values {
                    (#(#ref_field_assign,)*).values()
                }
            }
        }
    } else {
        quote! {}
    };

    wrap_in_dummy_mod(quote! {
        use diesel::insertable::Insertable;
        use diesel::internal::derives::insertable::UndecoratedInsertRecord;
        use diesel::prelude::*;

        #insert_owned

        #insert_borrowed

        impl #impl_generics UndecoratedInsertRecord<#table_name::table>
                for #struct_name #ty_generics
            #where_clause
        {
        }
    })
}

fn field_ty_embed(field: &Field, lifetime: Option<TokenStream>) -> TokenStream {
    let field_ty = &field.ty;
    let span = field.span;
    quote_spanned!(span=> #lifetime #field_ty)
}

fn field_expr_embed(field: &Field, lifetime: Option<TokenStream>) -> TokenStream {
    let field_name = &field.name;
    quote!(#lifetime self.#field_name)
}

fn field_ty_serialize_as(
    field: &Field,
    table_name: &Path,
    ty: &Type,
    treat_none_as_default_value: bool,
) -> TokenStream {
    let column_name = field.column_name();
    let span = field.span;
    if treat_none_as_default_value {
        let inner_ty = inner_of_option_ty(ty);

        quote_spanned! {span=>
            std::option::Option<diesel::dsl::Eq<
                #table_name::#column_name,
                #inner_ty,
            >>
        }
    } else {
        quote_spanned! {span=>
            diesel::dsl::Eq<
                #table_name::#column_name,
                #ty,
            >
        }
    }
}

fn field_expr_serialize_as(
    field: &Field,
    table_name: &Path,
    ty: &Type,
    treat_none_as_default_value: bool,
) -> TokenStream {
    let field_name = &field.name;
    let column_name = field.column_name();
    let column = quote!(#table_name::#column_name);
    if treat_none_as_default_value {
        if is_option_ty(ty) {
            quote!(self.#field_name.map(|x| #column.eq(::std::convert::Into::<#ty>::into(x))))
        } else {
            quote!(std::option::Option::Some(#column.eq(::std::convert::Into::<#ty>::into(self.#field_name))))
        }
    } else {
        quote!(#column.eq(::std::convert::Into::<#ty>::into(self.#field_name)))
    }
}

fn field_ty(
    field: &Field,
    table_name: &Path,
    lifetime: Option<TokenStream>,
    treat_none_as_default_value: bool,
) -> TokenStream {
    let column_name = field.column_name();
    let span = field.span;
    if treat_none_as_default_value {
        let inner_ty = inner_of_option_ty(&field.ty);

        quote_spanned! {span=>
            std::option::Option<diesel::dsl::Eq<
                #table_name::#column_name,
                #lifetime #inner_ty,
            >>
        }
    } else {
        let inner_ty = &field.ty;

        quote_spanned! {span=>
            diesel::dsl::Eq<
                #table_name::#column_name,
                #lifetime #inner_ty,
            >
        }
    }
}

fn field_expr(
    field: &Field,
    table_name: &Path,
    lifetime: Option<TokenStream>,
    treat_none_as_default_value: bool,
) -> TokenStream {
    let field_name = &field.name;
    let column_name = field.column_name();

    let column: Expr = parse_quote!(#table_name::#column_name);
    if treat_none_as_default_value {
        if is_option_ty(&field.ty) {
            if lifetime.is_some() {
                quote!(self.#field_name.as_ref().map(|x| #column.eq(x)))
            } else {
                quote!(self.#field_name.map(|x| #column.eq(x)))
            }
        } else {
            quote!(std::option::Option::Some(#column.eq(#lifetime self.#field_name)))
        }
    } else {
        quote!(#column.eq(#lifetime self.#field_name))
    }
}
<commit_msg>fixed unused qualifications issue in insertable<commit_after>use attrs::AttributeSpanWrapper;
use field::Field;
use model::Model;
use proc_macro2::TokenStream;
use quote::quote_spanned;
use syn::{DeriveInput, Expr, Path, Type};
use util::{inner_of_option_ty, is_option_ty, wrap_in_dummy_mod};

pub fn derive(item: DeriveInput) -> TokenStream {
    let model = Model::from_item(&item, false);

    let treat_none_as_default_value = model.treat_none_as_default_value();
    let table_name = &model.table_name();
    let struct_name = &item.ident;

    let (impl_generics, ty_generics, where_clause) = item.generics.split_for_impl();

    let mut generate_borrowed_insert = true;

    let mut direct_field_ty = Vec::with_capacity(model.fields().len());
    let mut direct_field_assign = Vec::with_capacity(model.fields().len());
    let mut ref_field_ty = Vec::with_capacity(model.fields().len());
    let mut ref_field_assign = Vec::with_capacity(model.fields().len());

    for field in model.fields() {
        match (field.serialize_as.as_ref(), field.embed()) {
            (None, true) => {
                direct_field_ty.push(field_ty_embed(field, None));
                direct_field_assign.push(field_expr_embed(field, None));
                ref_field_ty.push(field_ty_embed(field, Some(quote!(&'insert))));
                ref_field_assign.push(field_expr_embed(field, Some(quote!(&))));
            }
            (None, false) => {
                direct_field_ty.push(field_ty(
                    field,
                    table_name,
                    None,
                    treat_none_as_default_value,
                ));
                direct_field_assign.push(field_expr(
                    field,
                    table_name,
                    None,
                    treat_none_as_default_value,
                ));
                ref_field_ty.push(field_ty(
                    field,
                    table_name,
                    Some(quote!(&'insert)),
                    treat_none_as_default_value,
                ));
                ref_field_assign.push(field_expr(
                    field,
                    table_name,
                    Some(quote!(&)),
                    treat_none_as_default_value,
                ));
            }
            (Some(AttributeSpanWrapper { item: ty, .. }), false) => {
                direct_field_ty.push(field_ty_serialize_as(
                    field,
                    table_name,
                    ty,
                    treat_none_as_default_value,
                ));
                direct_field_assign.push(field_expr_serialize_as(
                    field,
                    table_name,
                    ty,
                    treat_none_as_default_value,
                ));

                generate_borrowed_insert = false; // as soon as we hit one field with #[diesel(serialize_as)] there is no point in generating the impl of Insertable for borrowed structs
            }
            (Some(AttributeSpanWrapper { attribute_span, .. }), true) => {
                abort!(
                    attribute_span,
                    "`#[diesel(embed)]` cannot be combined with `#[diesel(serialize_as)]`"
                )
            }
        }
    }

    let insert_owned = quote! {
        impl #impl_generics Insertable<#table_name::table> for #struct_name #ty_generics
            #where_clause
        {
            type Values = <(#(#direct_field_ty,)*) as Insertable<#table_name::table>>::Values;

            fn values(self) -> <(#(#direct_field_ty,)*) as Insertable<#table_name::table>>::Values {
                (#(#direct_field_assign,)*).values()
            }
        }
    };

    let insert_borrowed = if generate_borrowed_insert {
        let mut impl_generics = item.generics.clone();
        impl_generics.params.push(parse_quote!('insert));
        let (impl_generics, ..) = impl_generics.split_for_impl();

        quote! {
            impl #impl_generics Insertable<#table_name::table>
                for &'insert #struct_name #ty_generics
            #where_clause
            {
                type Values = <(#(#ref_field_ty,)*) as Insertable<#table_name::table>>::Values;

                fn values(self) -> <(#(#ref_field_ty,)*) as Insertable<#table_name::table>>::Values {
                    (#(#ref_field_assign,)*).values()
                }
            }
        }
    } else {
        quote! {}
    };

    wrap_in_dummy_mod(quote! {
        use diesel::insertable::Insertable;
        use diesel::internal::derives::insertable::UndecoratedInsertRecord;
        use diesel::prelude::*;

        #[allow(unused_qualifications)]
        #insert_owned

	#[allow(unused_qualifications)]
        #insert_borrowed

        impl #impl_generics UndecoratedInsertRecord<#table_name::table>
                for #struct_name #ty_generics
            #where_clause
        {
        }
    })
}

fn field_ty_embed(field: &Field, lifetime: Option<TokenStream>) -> TokenStream {
    let field_ty = &field.ty;
    let span = field.span;
    quote_spanned!(span=> #lifetime #field_ty)
}

fn field_expr_embed(field: &Field, lifetime: Option<TokenStream>) -> TokenStream {
    let field_name = &field.name;
    quote!(#lifetime self.#field_name)
}

fn field_ty_serialize_as(
    field: &Field,
    table_name: &Path,
    ty: &Type,
    treat_none_as_default_value: bool,
) -> TokenStream {
    let column_name = field.column_name();
    let span = field.span;
    if treat_none_as_default_value {
        let inner_ty = inner_of_option_ty(ty);

        quote_spanned! {span=>
            std::option::Option<diesel::dsl::Eq<
                #table_name::#column_name,
                #inner_ty,
            >>
        }
    } else {
        quote_spanned! {span=>
            diesel::dsl::Eq<
                #table_name::#column_name,
                #ty,
            >
        }
    }
}

fn field_expr_serialize_as(
    field: &Field,
    table_name: &Path,
    ty: &Type,
    treat_none_as_default_value: bool,
) -> TokenStream {
    let field_name = &field.name;
    let column_name = field.column_name();
    let column = quote!(#table_name::#column_name);
    if treat_none_as_default_value {
        if is_option_ty(ty) {
            quote!(self.#field_name.map(|x| #column.eq(::std::convert::Into::<#ty>::into(x))))
        } else {
            quote!(std::option::Option::Some(#column.eq(::std::convert::Into::<#ty>::into(self.#field_name))))
        }
    } else {
        quote!(#column.eq(::std::convert::Into::<#ty>::into(self.#field_name)))
    }
}

fn field_ty(
    field: &Field,
    table_name: &Path,
    lifetime: Option<TokenStream>,
    treat_none_as_default_value: bool,
) -> TokenStream {
    let column_name = field.column_name();
    let span = field.span;
    if treat_none_as_default_value {
        let inner_ty = inner_of_option_ty(&field.ty);

        quote_spanned! {span=>
            std::option::Option<diesel::dsl::Eq<
                #table_name::#column_name,
                #lifetime #inner_ty,
            >>
        }
    } else {
        let inner_ty = &field.ty;

        quote_spanned! {span=>
            diesel::dsl::Eq<
                #table_name::#column_name,
                #lifetime #inner_ty,
            >
        }
    }
}

fn field_expr(
    field: &Field,
    table_name: &Path,
    lifetime: Option<TokenStream>,
    treat_none_as_default_value: bool,
) -> TokenStream {
    let field_name = &field.name;
    let column_name = field.column_name();

    let column: Expr = parse_quote!(#table_name::#column_name);
    if treat_none_as_default_value {
        if is_option_ty(&field.ty) {
            if lifetime.is_some() {
                quote!(self.#field_name.as_ref().map(|x| #column.eq(x)))
            } else {
                quote!(self.#field_name.map(|x| #column.eq(x)))
            }
        } else {
            quote!(std::option::Option::Some(#column.eq(#lifetime self.#field_name)))
        }
    } else {
        quote!(#column.eq(#lifetime self.#field_name))
    }
}
<|endoftext|>


<commit_before>use std::mem;
use std::cmp::Ordering;
use std::ffi::CString;
use std::ops::Range;
use std::marker;
use std::path::Path;
use std::str;
use libc;

use {raw, Oid, Repository, Error, Object, ObjectType};
use util::{Binding, IntoCString};

/// A structure to represent a git [tree][1]
///
/// [1]: http://git-scm.com/book/en/Git-Internals-Git-Objects
pub struct Tree<'repo> {
    raw: *mut raw::git_tree,
    _marker: marker::PhantomData<Object<'repo>>,
}

/// A structure representing an entry inside of a tree. An entry is borrowed
/// from a tree.
pub struct TreeEntry<'tree> {
    raw: *mut raw::git_tree_entry,
    owned: bool,
    _marker: marker::PhantomData<&'tree raw::git_tree_entry>,
}

/// An iterator over the entries in a tree.
pub struct TreeIter<'tree> {
    range: Range<usize>,
    tree: &'tree Tree<'tree>,
}

impl<'repo> Tree<'repo> {
    /// Get the id (SHA1) of a repository object
    pub fn id(&self) -> Oid {
        unsafe { Binding::from_raw(raw::git_tree_id(&*self.raw)) }
    }

    /// Get the number of entries listed in this tree.
    pub fn len(&self) -> usize {
        unsafe { raw::git_tree_entrycount(&*self.raw) as usize }
    }

    /// Returns an iterator over the entries in this tree.
    pub fn iter(&self) -> TreeIter {
        TreeIter { range: 0..self.len(), tree: self }
    }

    /// Lookup a tree entry by SHA value.
    pub fn get_id(&self, id: Oid) -> Option<TreeEntry> {
        unsafe {
            let ptr = raw::git_tree_entry_byid(&*self.raw(), &*id.raw());
            if ptr.is_null() {
                None
            } else {
                Some(entry_from_raw_const(ptr))
            }
        }
    }

    /// Lookup a tree entry by its position in the tree
    pub fn get(&self, n: usize) -> Option<TreeEntry> {
        unsafe {
            let ptr = raw::git_tree_entry_byindex(&*self.raw(),
                                                  n as libc::size_t);
            if ptr.is_null() {
                None
            } else {
                Some(entry_from_raw_const(ptr))
            }
        }
    }

    /// Lookup a tree entry by its filename
    pub fn get_name(&self, filename: &str) -> Option<TreeEntry> {
        let filename = CString::new(filename).unwrap();
        unsafe {
            let ptr = call!(raw::git_tree_entry_byname(&*self.raw(), filename));
            if ptr.is_null() {
                None
            } else {
                Some(entry_from_raw_const(ptr))
            }
        }
    }

    /// Retrieve a tree entry contained in a tree or in any of its subtrees,
    /// given its relative path.
    pub fn get_path(&self, path: &Path) -> Result<TreeEntry<'static>, Error> {
        let path = try!(path.into_c_string());
        let mut ret = 0 as *mut raw::git_tree_entry;
        unsafe {
            try_call!(raw::git_tree_entry_bypath(&mut ret, &*self.raw(), path));
            Ok(Binding::from_raw(ret))
        }
    }

    /// Casts this Tree to be usable as an `Object`
    pub fn as_object(&self) -> &Object<'repo> {
        unsafe {
            &*(self as *const _ as *const Object<'repo>)
        }
    }

    /// Consumes Commit to be returned as an `Object`
    pub fn into_object(self) -> Object<'repo> {
        assert_eq!(mem::size_of_val(&self), mem::size_of::<Object>());
        unsafe {
            mem::transmute(self)
        }
    }
}

impl<'repo> Binding for Tree<'repo> {
    type Raw = *mut raw::git_tree;

    unsafe fn from_raw(raw: *mut raw::git_tree) -> Tree<'repo> {
        Tree { raw: raw, _marker: marker::PhantomData }
    }
    fn raw(&self) -> *mut raw::git_tree { self.raw }
}

impl<'repo> Drop for Tree<'repo> {
    fn drop(&mut self) {
        unsafe { raw::git_tree_free(self.raw) }
    }
}

/// Create a new tree entry from the raw pointer provided.
///
/// The lifetime of the entry is tied to the tree provided and the function
/// is unsafe because the validity of the pointer cannot be guaranteed.
pub unsafe fn entry_from_raw_const<'tree>(raw: *const raw::git_tree_entry)
                                          -> TreeEntry<'tree> {
    TreeEntry {
        raw: raw as *mut raw::git_tree_entry,
        owned: false,
        _marker: marker::PhantomData,
    }
}

impl<'tree> TreeEntry<'tree> {
    /// Get the id of the object pointed by the entry
    pub fn id(&self) -> Oid {
        unsafe { Binding::from_raw(raw::git_tree_entry_id(&*self.raw)) }
    }

    /// Get the filename of a tree entry
    ///
    /// Returns `None` if the name is not valid utf-8
    pub fn name(&self) -> Option<&str> {
        str::from_utf8(self.name_bytes()).ok()
    }

    /// Get the filename of a tree entry
    pub fn name_bytes(&self) -> &[u8] {
        unsafe {
            ::opt_bytes(self, raw::git_tree_entry_name(&*self.raw())).unwrap()
        }
    }

    /// Convert a tree entry to the object it points to.
    pub fn to_object<'a>(&self, repo: &'a Repository)
                         -> Result<Object<'a>, Error> {
        let mut ret = 0 as *mut raw::git_object;
        unsafe {
            try_call!(raw::git_tree_entry_to_object(&mut ret, repo.raw(),
                                                    &*self.raw()));
            Ok(Binding::from_raw(ret))
        }
    }

    /// Get the type of the object pointed by the entry
    pub fn kind(&self) -> Option<ObjectType> {
        ObjectType::from_raw(unsafe { raw::git_tree_entry_type(&*self.raw) })
    }

    /// Get the UNIX file attributes of a tree entry
    pub fn filemode(&self) -> i32 {
        unsafe { raw::git_tree_entry_filemode(&*self.raw) as i32 }
    }

    /// Get the raw UNIX file attributes of a tree entry
    pub fn filemode_raw(&self) -> i32 {
        unsafe { raw::git_tree_entry_filemode_raw(&*self.raw) as i32 }
    }

    /// Convert this entry of any lifetime into an owned signature with a static
    /// lifetime.
    ///
    /// This will use the `Clone::clone` implementation under the hood.
    pub fn to_owned(&self) -> TreeEntry<'static> {
        unsafe {
            let me = mem::transmute::<&TreeEntry<'tree>, &TreeEntry<'static>>(self);
            me.clone()
        }
    }
}

impl<'a> Binding for TreeEntry<'a> {
    type Raw = *mut raw::git_tree_entry;
    unsafe fn from_raw(raw: *mut raw::git_tree_entry) -> TreeEntry<'a> {
        TreeEntry {
            raw: raw,
            owned: true,
            _marker: marker::PhantomData,
        }
    }
    fn raw(&self) -> *mut raw::git_tree_entry { self.raw }
}

impl<'a> Clone for TreeEntry<'a> {
    fn clone(&self) -> TreeEntry<'a> {
        let mut ret = 0 as *mut raw::git_tree_entry;
        unsafe {
            assert_eq!(raw::git_tree_entry_dup(&mut ret, &*self.raw()), 0);
            Binding::from_raw(ret)
        }
    }
}

impl<'a> PartialOrd for TreeEntry<'a> {
    fn partial_cmp(&self, other: &TreeEntry<'a>) -> Option<Ordering> {
        Some(self.cmp(other))
    }
}
impl<'a> Ord for TreeEntry<'a> {
    fn cmp(&self, other: &TreeEntry<'a>) -> Ordering {
        match unsafe { raw::git_tree_entry_cmp(&*self.raw(), &*other.raw()) } {
            0 => Ordering::Equal,
            n if n < 0 => Ordering::Less,
            _ => Ordering::Greater,
        }
    }
}

impl<'a> PartialEq for TreeEntry<'a> {
    fn eq(&self, other: &TreeEntry<'a>) -> bool {
        self.cmp(other) == Ordering::Equal
    }
}
impl<'a> Eq for TreeEntry<'a> {}

impl<'a> Drop for TreeEntry<'a> {
    fn drop(&mut self) {
        if self.owned {
            unsafe { raw::git_tree_entry_free(self.raw) }
        }
    }
}

impl<'tree> Iterator for TreeIter<'tree> {
    type Item = TreeEntry<'tree>;
    fn next(&mut self) -> Option<TreeEntry<'tree>> {
        self.range.next().and_then(|i| self.tree.get(i))
    }
    fn size_hint(&self) -> (usize, Option<usize>) { self.range.size_hint() }
}
impl<'tree> DoubleEndedIterator for TreeIter<'tree> {
    fn next_back(&mut self) -> Option<TreeEntry<'tree>> {
        self.range.next_back().and_then(|i| self.tree.get(i))
    }
}
impl<'tree> ExactSizeIterator for TreeIter<'tree> {}

#[cfg(test)]
mod tests {
    use {Repository,Tree,TreeEntry,ObjectType,Object};
    use tempdir::TempDir;
    use std::fs::File;
    use std::io::prelude::*;
    use std::path::Path;

    pub struct TestTreeIter<'a> {
        entries: Vec<TreeEntry<'a>>,
        repo: &'a Repository,
    }

    impl<'a> Iterator for TestTreeIter<'a> {
        type Item = TreeEntry<'a>;

        fn next(&mut self) -> Option<TreeEntry<'a> > {
            if self.entries.is_empty() {
                None
            } else {
                let entry = self.entries.remove(0);

                match entry.kind() {
                    Some(ObjectType::Tree) => {
                        let obj: Object<'a> = entry.to_object(self.repo).unwrap();

                        let tree: &Tree<'a> = obj.as_tree().unwrap();

                        for entry in tree.iter() {
                            self.entries.push(entry.to_owned());
                        }
                    }
                    _ => {}
                }

                Some(entry)
            }
        }
    }

    fn tree_iter<'repo>(tree: &Tree<'repo>, repo: &'repo Repository)
                        -> TestTreeIter<'repo> {
        let mut initial = vec![];

        for entry in tree.iter() {
            initial.push(entry.to_owned());
        }

        TestTreeIter {
            entries: initial,
            repo: repo,
        }
    }

    #[test]
    fn smoke_tree_iter() {
        let (td, repo) = ::test::repo_init();

        setup_repo(&td, &repo);

        let head = repo.head().unwrap();
        let target = head.target().unwrap();
        let commit = repo.find_commit(target).unwrap();

        let tree = repo.find_tree(commit.tree_id()).unwrap();
        assert_eq!(tree.id(), commit.tree_id());
        assert_eq!(tree.len(), 1);

        for entry in tree_iter(&tree, &repo) {
            println!("iter entry {:?}", entry.name());
        }
    }

    fn setup_repo(td: &TempDir, repo: &Repository) {
        let mut index = repo.index().unwrap();
        File::create(&td.path().join("foo")).unwrap().write_all(b"foo").unwrap();
        index.add_path(Path::new("foo")).unwrap();
        let id = index.write_tree().unwrap();
        let sig = repo.signature().unwrap();
        let tree = repo.find_tree(id).unwrap();
        let parent = repo.find_commit(repo.head().unwrap().target()
                                      .unwrap()).unwrap();
        repo.commit(Some("HEAD"), &sig, &sig, "another commit",
                    &tree, &[&parent]).unwrap();
    }

    #[test]
    fn smoke() {
        let (td, repo) = ::test::repo_init();

        setup_repo(&td, &repo);

        let head = repo.head().unwrap();
        let target = head.target().unwrap();
        let commit = repo.find_commit(target).unwrap();

        let tree = repo.find_tree(commit.tree_id()).unwrap();
        assert_eq!(tree.id(), commit.tree_id());
        assert_eq!(tree.len(), 1);
        {
            let e1 = tree.get(0).unwrap();
            assert!(e1 == tree.get_id(e1.id()).unwrap());
            assert!(e1 == tree.get_name("foo").unwrap());
            assert!(e1 == tree.get_path(Path::new("foo")).unwrap());
            assert_eq!(e1.name(), Some("foo"));
            e1.to_object(&repo).unwrap();
        }
        tree.into_object();

        repo.find_object(commit.tree_id(), None).unwrap().as_tree().unwrap();
        repo.find_object(commit.tree_id(), None).unwrap().into_tree().ok().unwrap();
    }
}
<commit_msg>Add IntoIterator trait around Tree::iter() method<commit_after>use std::mem;
use std::cmp::Ordering;
use std::ffi::CString;
use std::ops::Range;
use std::marker;
use std::path::Path;
use std::str;
use libc;

use {raw, Oid, Repository, Error, Object, ObjectType};
use util::{Binding, IntoCString};

/// A structure to represent a git [tree][1]
///
/// [1]: http://git-scm.com/book/en/Git-Internals-Git-Objects
pub struct Tree<'repo> {
    raw: *mut raw::git_tree,
    _marker: marker::PhantomData<Object<'repo>>,
}

/// A structure representing an entry inside of a tree. An entry is borrowed
/// from a tree.
pub struct TreeEntry<'tree> {
    raw: *mut raw::git_tree_entry,
    owned: bool,
    _marker: marker::PhantomData<&'tree raw::git_tree_entry>,
}

/// An iterator over the entries in a tree.
pub struct TreeIter<'tree> {
    range: Range<usize>,
    tree: &'tree Tree<'tree>,
}

impl<'repo> Tree<'repo> {
    /// Get the id (SHA1) of a repository object
    pub fn id(&self) -> Oid {
        unsafe { Binding::from_raw(raw::git_tree_id(&*self.raw)) }
    }

    /// Get the number of entries listed in this tree.
    pub fn len(&self) -> usize {
        unsafe { raw::git_tree_entrycount(&*self.raw) as usize }
    }

    /// Returns an iterator over the entries in this tree.
    pub fn iter(&self) -> TreeIter {
        TreeIter { range: 0..self.len(), tree: self }
    }

    /// Lookup a tree entry by SHA value.
    pub fn get_id(&self, id: Oid) -> Option<TreeEntry> {
        unsafe {
            let ptr = raw::git_tree_entry_byid(&*self.raw(), &*id.raw());
            if ptr.is_null() {
                None
            } else {
                Some(entry_from_raw_const(ptr))
            }
        }
    }

    /// Lookup a tree entry by its position in the tree
    pub fn get(&self, n: usize) -> Option<TreeEntry> {
        unsafe {
            let ptr = raw::git_tree_entry_byindex(&*self.raw(),
                                                  n as libc::size_t);
            if ptr.is_null() {
                None
            } else {
                Some(entry_from_raw_const(ptr))
            }
        }
    }

    /// Lookup a tree entry by its filename
    pub fn get_name(&self, filename: &str) -> Option<TreeEntry> {
        let filename = CString::new(filename).unwrap();
        unsafe {
            let ptr = call!(raw::git_tree_entry_byname(&*self.raw(), filename));
            if ptr.is_null() {
                None
            } else {
                Some(entry_from_raw_const(ptr))
            }
        }
    }

    /// Retrieve a tree entry contained in a tree or in any of its subtrees,
    /// given its relative path.
    pub fn get_path(&self, path: &Path) -> Result<TreeEntry<'static>, Error> {
        let path = try!(path.into_c_string());
        let mut ret = 0 as *mut raw::git_tree_entry;
        unsafe {
            try_call!(raw::git_tree_entry_bypath(&mut ret, &*self.raw(), path));
            Ok(Binding::from_raw(ret))
        }
    }

    /// Casts this Tree to be usable as an `Object`
    pub fn as_object(&self) -> &Object<'repo> {
        unsafe {
            &*(self as *const _ as *const Object<'repo>)
        }
    }

    /// Consumes Commit to be returned as an `Object`
    pub fn into_object(self) -> Object<'repo> {
        assert_eq!(mem::size_of_val(&self), mem::size_of::<Object>());
        unsafe {
            mem::transmute(self)
        }
    }
}

impl<'repo> Binding for Tree<'repo> {
    type Raw = *mut raw::git_tree;

    unsafe fn from_raw(raw: *mut raw::git_tree) -> Tree<'repo> {
        Tree { raw: raw, _marker: marker::PhantomData }
    }
    fn raw(&self) -> *mut raw::git_tree { self.raw }
}

impl<'repo> Drop for Tree<'repo> {
    fn drop(&mut self) {
        unsafe { raw::git_tree_free(self.raw) }
    }
}

impl<'repo, 'iter> IntoIterator for &'iter Tree<'repo> {
    type Item = TreeEntry<'iter>;
    type IntoIter = TreeIter<'iter>;
    fn into_iter(self) -> Self::IntoIter {
        self.iter()
    }
}

/// Create a new tree entry from the raw pointer provided.
///
/// The lifetime of the entry is tied to the tree provided and the function
/// is unsafe because the validity of the pointer cannot be guaranteed.
pub unsafe fn entry_from_raw_const<'tree>(raw: *const raw::git_tree_entry)
                                          -> TreeEntry<'tree> {
    TreeEntry {
        raw: raw as *mut raw::git_tree_entry,
        owned: false,
        _marker: marker::PhantomData,
    }
}

impl<'tree> TreeEntry<'tree> {
    /// Get the id of the object pointed by the entry
    pub fn id(&self) -> Oid {
        unsafe { Binding::from_raw(raw::git_tree_entry_id(&*self.raw)) }
    }

    /// Get the filename of a tree entry
    ///
    /// Returns `None` if the name is not valid utf-8
    pub fn name(&self) -> Option<&str> {
        str::from_utf8(self.name_bytes()).ok()
    }

    /// Get the filename of a tree entry
    pub fn name_bytes(&self) -> &[u8] {
        unsafe {
            ::opt_bytes(self, raw::git_tree_entry_name(&*self.raw())).unwrap()
        }
    }

    /// Convert a tree entry to the object it points to.
    pub fn to_object<'a>(&self, repo: &'a Repository)
                         -> Result<Object<'a>, Error> {
        let mut ret = 0 as *mut raw::git_object;
        unsafe {
            try_call!(raw::git_tree_entry_to_object(&mut ret, repo.raw(),
                                                    &*self.raw()));
            Ok(Binding::from_raw(ret))
        }
    }

    /// Get the type of the object pointed by the entry
    pub fn kind(&self) -> Option<ObjectType> {
        ObjectType::from_raw(unsafe { raw::git_tree_entry_type(&*self.raw) })
    }

    /// Get the UNIX file attributes of a tree entry
    pub fn filemode(&self) -> i32 {
        unsafe { raw::git_tree_entry_filemode(&*self.raw) as i32 }
    }

    /// Get the raw UNIX file attributes of a tree entry
    pub fn filemode_raw(&self) -> i32 {
        unsafe { raw::git_tree_entry_filemode_raw(&*self.raw) as i32 }
    }

    /// Convert this entry of any lifetime into an owned signature with a static
    /// lifetime.
    ///
    /// This will use the `Clone::clone` implementation under the hood.
    pub fn to_owned(&self) -> TreeEntry<'static> {
        unsafe {
            let me = mem::transmute::<&TreeEntry<'tree>, &TreeEntry<'static>>(self);
            me.clone()
        }
    }
}

impl<'a> Binding for TreeEntry<'a> {
    type Raw = *mut raw::git_tree_entry;
    unsafe fn from_raw(raw: *mut raw::git_tree_entry) -> TreeEntry<'a> {
        TreeEntry {
            raw: raw,
            owned: true,
            _marker: marker::PhantomData,
        }
    }
    fn raw(&self) -> *mut raw::git_tree_entry { self.raw }
}

impl<'a> Clone for TreeEntry<'a> {
    fn clone(&self) -> TreeEntry<'a> {
        let mut ret = 0 as *mut raw::git_tree_entry;
        unsafe {
            assert_eq!(raw::git_tree_entry_dup(&mut ret, &*self.raw()), 0);
            Binding::from_raw(ret)
        }
    }
}

impl<'a> PartialOrd for TreeEntry<'a> {
    fn partial_cmp(&self, other: &TreeEntry<'a>) -> Option<Ordering> {
        Some(self.cmp(other))
    }
}
impl<'a> Ord for TreeEntry<'a> {
    fn cmp(&self, other: &TreeEntry<'a>) -> Ordering {
        match unsafe { raw::git_tree_entry_cmp(&*self.raw(), &*other.raw()) } {
            0 => Ordering::Equal,
            n if n < 0 => Ordering::Less,
            _ => Ordering::Greater,
        }
    }
}

impl<'a> PartialEq for TreeEntry<'a> {
    fn eq(&self, other: &TreeEntry<'a>) -> bool {
        self.cmp(other) == Ordering::Equal
    }
}
impl<'a> Eq for TreeEntry<'a> {}

impl<'a> Drop for TreeEntry<'a> {
    fn drop(&mut self) {
        if self.owned {
            unsafe { raw::git_tree_entry_free(self.raw) }
        }
    }
}

impl<'tree> Iterator for TreeIter<'tree> {
    type Item = TreeEntry<'tree>;
    fn next(&mut self) -> Option<TreeEntry<'tree>> {
        self.range.next().and_then(|i| self.tree.get(i))
    }
    fn size_hint(&self) -> (usize, Option<usize>) { self.range.size_hint() }
}
impl<'tree> DoubleEndedIterator for TreeIter<'tree> {
    fn next_back(&mut self) -> Option<TreeEntry<'tree>> {
        self.range.next_back().and_then(|i| self.tree.get(i))
    }
}
impl<'tree> ExactSizeIterator for TreeIter<'tree> {}

#[cfg(test)]
mod tests {
    use {Repository,Tree,TreeEntry,ObjectType,Object};
    use tempdir::TempDir;
    use std::fs::File;
    use std::io::prelude::*;
    use std::path::Path;

    pub struct TestTreeIter<'a> {
        entries: Vec<TreeEntry<'a>>,
        repo: &'a Repository,
    }

    impl<'a> Iterator for TestTreeIter<'a> {
        type Item = TreeEntry<'a>;

        fn next(&mut self) -> Option<TreeEntry<'a> > {
            if self.entries.is_empty() {
                None
            } else {
                let entry = self.entries.remove(0);

                match entry.kind() {
                    Some(ObjectType::Tree) => {
                        let obj: Object<'a> = entry.to_object(self.repo).unwrap();

                        let tree: &Tree<'a> = obj.as_tree().unwrap();

                        for entry in tree.iter() {
                            self.entries.push(entry.to_owned());
                        }
                    }
                    _ => {}
                }

                Some(entry)
            }
        }
    }

    fn tree_iter<'repo>(tree: &Tree<'repo>, repo: &'repo Repository)
                        -> TestTreeIter<'repo> {
        let mut initial = vec![];

        for entry in tree.iter() {
            initial.push(entry.to_owned());
        }

        TestTreeIter {
            entries: initial,
            repo: repo,
        }
    }

    #[test]
    fn smoke_tree_iter() {
        let (td, repo) = ::test::repo_init();

        setup_repo(&td, &repo);

        let head = repo.head().unwrap();
        let target = head.target().unwrap();
        let commit = repo.find_commit(target).unwrap();

        let tree = repo.find_tree(commit.tree_id()).unwrap();
        assert_eq!(tree.id(), commit.tree_id());
        assert_eq!(tree.len(), 1);

        for entry in tree_iter(&tree, &repo) {
            println!("iter entry {:?}", entry.name());
        }
    }

    fn setup_repo(td: &TempDir, repo: &Repository) {
        let mut index = repo.index().unwrap();
        File::create(&td.path().join("foo")).unwrap().write_all(b"foo").unwrap();
        index.add_path(Path::new("foo")).unwrap();
        let id = index.write_tree().unwrap();
        let sig = repo.signature().unwrap();
        let tree = repo.find_tree(id).unwrap();
        let parent = repo.find_commit(repo.head().unwrap().target()
                                      .unwrap()).unwrap();
        repo.commit(Some("HEAD"), &sig, &sig, "another commit",
                    &tree, &[&parent]).unwrap();
    }

    #[test]
    fn smoke() {
        let (td, repo) = ::test::repo_init();

        setup_repo(&td, &repo);

        let head = repo.head().unwrap();
        let target = head.target().unwrap();
        let commit = repo.find_commit(target).unwrap();

        let tree = repo.find_tree(commit.tree_id()).unwrap();
        assert_eq!(tree.id(), commit.tree_id());
        assert_eq!(tree.len(), 1);
        {
            let e1 = tree.get(0).unwrap();
            assert!(e1 == tree.get_id(e1.id()).unwrap());
            assert!(e1 == tree.get_name("foo").unwrap());
            assert!(e1 == tree.get_path(Path::new("foo")).unwrap());
            assert_eq!(e1.name(), Some("foo"));
            e1.to_object(&repo).unwrap();
        }
        tree.into_object();

        repo.find_object(commit.tree_id(), None).unwrap().as_tree().unwrap();
        repo.find_object(commit.tree_id(), None).unwrap().into_tree().ok().unwrap();
    }
}
<|endoftext|>