<commit_before>//! This module handles fuzzy-searching of functions, structs and other symbols
//! by name across the whole workspace and dependencies.
//!
//! It works by building an incrementally-updated text-search index of all
//! symbols. The backbone of the index is the **awesome** `fst` crate by
//! @BurntSushi.
//!
//! In a nutshell, you give a set of strings to `fst`, and it builds a
//! finite state machine describing this set of strings. The strings which
//! could fuzzy-match a pattern can also be described by a finite state machine.
//! What is freaking cool is that you can now traverse both state machines in
//! lock-step to enumerate the strings which are both in the input set and
//! fuzz-match the query. Or, more formally, given two languages described by
//! FSTs, one can build a product FST which describes the intersection of the
//! languages.
//!
//! `fst` does not support cheap updating of the index, but it supports unioning
//! of state machines. So, to account for changing source code, we build an FST
//! for each library (which is assumed to never change) and an FST for each Rust
//! file in the current workspace, and run a query against the union of all
//! those FSTs.

use std::{
    cmp::Ordering,
    fmt,
    hash::{Hash, Hasher},
    mem,
    sync::Arc,
};

use fst::{self, Streamer};
use hir::db::DefDatabase;
use ra_db::{
    salsa::{self, ParallelDatabase},
    CrateId, FileId, SourceDatabaseExt, SourceRootId,
};
use ra_syntax::{
    ast::{self, NameOwner},
    match_ast, AstNode, Parse, SmolStr, SourceFile,
    SyntaxKind::{self, *},
    SyntaxNode, SyntaxNodePtr, TextRange, WalkEvent,
};
#[cfg(not(feature = "wasm"))]
use rayon::prelude::*;

use crate::RootDatabase;

#[derive(Debug)]
pub struct Query {
    query: String,
    lowercased: String,
    only_types: bool,
    libs: bool,
    exact: bool,
    limit: usize,
}

impl Query {
    pub fn new(query: String) -> Query {
        let lowercased = query.to_lowercase();
        Query {
            query,
            lowercased,
            only_types: false,
            libs: false,
            exact: false,
            limit: usize::max_value(),
        }
    }

    pub fn only_types(&mut self) {
        self.only_types = true;
    }

    pub fn libs(&mut self) {
        self.libs = true;
    }

    pub fn exact(&mut self) {
        self.exact = true;
    }

    pub fn limit(&mut self, limit: usize) {
        self.limit = limit
    }
}

#[salsa::query_group(SymbolsDatabaseStorage)]
pub trait SymbolsDatabase: hir::db::HirDatabase {
    fn file_symbols(&self, file_id: FileId) -> Arc<SymbolIndex>;
    #[salsa::input]
    fn library_symbols(&self, id: SourceRootId) -> Arc<SymbolIndex>;
    /// The set of "local" (that is, from the current workspace) roots.
    /// Files in local roots are assumed to change frequently.
    #[salsa::input]
    fn local_roots(&self) -> Arc<Vec<SourceRootId>>;
    /// The set of roots for crates.io libraries.
    /// Files in libraries are assumed to never change.
    #[salsa::input]
    fn library_roots(&self) -> Arc<Vec<SourceRootId>>;
}

fn file_symbols(db: &impl SymbolsDatabase, file_id: FileId) -> Arc<SymbolIndex> {
    db.check_canceled();
    let parse = db.parse(file_id);

    let symbols = source_file_to_file_symbols(&parse.tree(), file_id);

    // FIXME: add macros here

    Arc::new(SymbolIndex::new(symbols))
}

/// Need to wrap Snapshot to provide `Clone` impl for `map_with`
struct Snap(salsa::Snapshot<RootDatabase>);
impl Clone for Snap {
    fn clone(&self) -> Snap {
        Snap(self.0.snapshot())
    }
}

// Feature: Workspace Symbol
//
// Uses fuzzy-search to find types, modules and functions by name across your
// project and dependencies. This is **the** most useful feature, which improves code
// navigation tremendously. It mostly works on top of the built-in LSP
// functionality, however `#` and `*` symbols can be used to narrow down the
// search. Specifically,
//
// - `Foo` searches for `Foo` type in the current workspace
// - `foo#` searches for `foo` function in the current workspace
// - `Foo*` searches for `Foo` type among dependencies, including `stdlib`
// - `foo#*` searches for `foo` function among dependencies
//
// That is, `#` switches from "types" to all symbols, `*` switches from the current
// workspace to dependencies.
//
// |===
// | Editor  | Shortcut
//
// | VS Code | kbd:[Ctrl+T]
// |===
pub fn world_symbols(db: &RootDatabase, query: Query) -> Vec<FileSymbol> {
    let _p = ra_prof::profile("world_symbols").detail(|| query.query.clone());

    let buf: Vec<Arc<SymbolIndex>> = if query.libs {
        let snap = Snap(db.snapshot());
        #[cfg(not(feature = "wasm"))]
        let buf = db
            .library_roots()
            .par_iter()
            .map_with(snap, |db, &lib_id| db.0.library_symbols(lib_id))
            .collect();

        #[cfg(feature = "wasm")]
        let buf = db.library_roots().iter().map(|&lib_id| snap.0.library_symbols(lib_id)).collect();

        buf
    } else {
        let mut files = Vec::new();
        for &root in db.local_roots().iter() {
            let sr = db.source_root(root);
            files.extend(sr.walk())
        }

        let snap = Snap(db.snapshot());
        #[cfg(not(feature = "wasm"))]
        let buf =
            files.par_iter().map_with(snap, |db, &file_id| db.0.file_symbols(file_id)).collect();

        #[cfg(feature = "wasm")]
        let buf = files.iter().map(|&file_id| snap.0.file_symbols(file_id)).collect();

        buf
    };
    query.search(&buf)
}

pub fn crate_symbols(db: &RootDatabase, krate: CrateId, query: Query) -> Vec<FileSymbol> {
    let def_map = db.crate_def_map(krate);
    let mut files = Vec::new();
    let mut modules = vec![def_map.root];
    while let Some(module) = modules.pop() {
        let data = &def_map[module];
        files.extend(data.origin.file_id());
        modules.extend(data.children.values());
    }

    let snap = Snap(db.snapshot());

    #[cfg(not(feature = "wasm"))]
    let buf = files
        .par_iter()
        .map_with(snap, |db, &file_id| db.0.file_symbols(file_id))
        .collect::<Vec<_>>();

    #[cfg(feature = "wasm")]
    let buf = files.iter().map(|&file_id| snap.0.file_symbols(file_id)).collect::<Vec<_>>();

    query.search(&buf)
}

pub fn index_resolve(db: &RootDatabase, name_ref: &ast::NameRef) -> Vec<FileSymbol> {
    let name = name_ref.text();
    let mut query = Query::new(name.to_string());
    query.exact();
    query.limit(4);
    world_symbols(db, query)
}

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

impl fmt::Debug for SymbolIndex {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.debug_struct("SymbolIndex").field("n_symbols", &self.symbols.len()).finish()
    }
}

impl PartialEq for SymbolIndex {
    fn eq(&self, other: &SymbolIndex) -> bool {
        self.symbols == other.symbols
    }
}

impl Eq for SymbolIndex {}

impl Hash for SymbolIndex {
    fn hash<H: Hasher>(&self, hasher: &mut H) {
        self.symbols.hash(hasher)
    }
}

impl SymbolIndex {
    fn new(mut symbols: Vec<FileSymbol>) -> SymbolIndex {
        fn cmp(lhs: &FileSymbol, rhs: &FileSymbol) -> Ordering {
            let lhs_chars = lhs.name.chars().map(|c| c.to_ascii_lowercase());
            let rhs_chars = rhs.name.chars().map(|c| c.to_ascii_lowercase());
            lhs_chars.cmp(rhs_chars)
        }

        #[cfg(not(feature = "wasm"))]
        symbols.par_sort_by(cmp);

        #[cfg(feature = "wasm")]
        symbols.sort_by(cmp);

        let mut builder = fst::MapBuilder::memory();

        let mut last_batch_start = 0;

        for idx in 0..symbols.len() {
            if let Some(next_symbol) = symbols.get(idx + 1) {
                if cmp(&symbols[last_batch_start], next_symbol) == Ordering::Equal {
                    continue;
                }
            }

            let start = last_batch_start;
            let end = idx + 1;
            last_batch_start = end;

            let key = symbols[start].name.as_str().to_ascii_lowercase();
            let value = SymbolIndex::range_to_map_value(start, end);

            builder.insert(key, value).unwrap();
        }

        let map = fst::Map::new(builder.into_inner().unwrap()).unwrap();
        SymbolIndex { symbols, map }
    }

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

    pub fn memory_size(&self) -> usize {
        self.map.as_fst().size() + self.symbols.len() * mem::size_of::<FileSymbol>()
    }

    #[cfg(not(feature = "wasm"))]
    pub(crate) fn for_files(
        files: impl ParallelIterator<Item = (FileId, Parse<ast::SourceFile>)>,
    ) -> SymbolIndex {
        let symbols = files
            .flat_map(|(file_id, file)| source_file_to_file_symbols(&file.tree(), file_id))
            .collect::<Vec<_>>();
        SymbolIndex::new(symbols)
    }

    #[cfg(feature = "wasm")]
    pub(crate) fn for_files(
        files: impl Iterator<Item = (FileId, Parse<ast::SourceFile>)>,
    ) -> SymbolIndex {
        let symbols = files
            .flat_map(|(file_id, file)| source_file_to_file_symbols(&file.tree(), file_id))
            .collect::<Vec<_>>();
        SymbolIndex::new(symbols)
    }

    fn range_to_map_value(start: usize, end: usize) -> u64 {
        debug_assert![start <= (std::u32::MAX as usize)];
        debug_assert![end <= (std::u32::MAX as usize)];

        ((start as u64) << 32) | end as u64
    }

    fn map_value_to_range(value: u64) -> (usize, usize) {
        let end = value as u32 as usize;
        let start = (value >> 32) as usize;
        (start, end)
    }
}

impl Query {
    pub(crate) fn search(self, indices: &[Arc<SymbolIndex>]) -> Vec<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() {
            for indexed_value in indexed_values {
                let symbol_index = &indices[indexed_value.index];
                let (start, end) = SymbolIndex::map_value_to_range(indexed_value.value);

                for symbol in &symbol_index.symbols[start..end] {
                    if self.only_types && !is_type(symbol.kind) {
                        continue;
                    }
                    if self.exact && symbol.name != self.query {
                        continue;
                    }

                    res.push(symbol.clone());
                    if res.len() >= self.limit {
                        return res;
                    }
                }
            }
        }
        res
    }
}

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

/// The actual data that is stored in the index. It should be as compact as
/// possible.
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct FileSymbol {
    pub file_id: FileId,
    pub name: SmolStr,
    pub kind: SyntaxKind,
    pub range: TextRange,
    pub ptr: SyntaxNodePtr,
    pub name_range: Option<TextRange>,
    pub container_name: Option<SmolStr>,
}

fn source_file_to_file_symbols(source_file: &SourceFile, file_id: FileId) -> Vec<FileSymbol> {
    let mut symbols = Vec::new();
    let mut stack = Vec::new();

    for event in source_file.syntax().preorder() {
        match event {
            WalkEvent::Enter(node) => {
                if let Some(mut symbol) = to_file_symbol(&node, file_id) {
                    symbol.container_name = stack.last().cloned();

                    stack.push(symbol.name.clone());
                    symbols.push(symbol);
                }
            }

            WalkEvent::Leave(node) => {
                if to_symbol(&node).is_some() {
                    stack.pop();
                }
            }
        }
    }

    symbols
}

fn to_symbol(node: &SyntaxNode) -> Option<(SmolStr, SyntaxNodePtr, TextRange)> {
    fn decl<N: NameOwner>(node: N) -> Option<(SmolStr, SyntaxNodePtr, TextRange)> {
        let name = node.name()?;
        let name_range = name.syntax().text_range();
        let name = name.text().clone();
        let ptr = SyntaxNodePtr::new(node.syntax());

        Some((name, ptr, name_range))
    }
    match_ast! {
        match node {
            ast::FnDef(it) => decl(it),
            ast::StructDef(it) => decl(it),
            ast::EnumDef(it) => decl(it),
            ast::TraitDef(it) => decl(it),
            ast::Module(it) => decl(it),
            ast::TypeAliasDef(it) => decl(it),
            ast::ConstDef(it) => decl(it),
            ast::StaticDef(it) => decl(it),
            ast::MacroCall(it) => {
                if it.is_macro_rules().is_some() {
                    decl(it)
                } else {
                    None
                }
            },
            _ => None,
        }
    }
}

fn to_file_symbol(node: &SyntaxNode, file_id: FileId) -> Option<FileSymbol> {
    to_symbol(node).map(move |(name, ptr, name_range)| FileSymbol {
        name,
        kind: node.kind(),
        range: node.text_range(),
        ptr,
        file_id,
        name_range: Some(name_range),
        container_name: None,
    })
}
<commit_msg>Add symbol index FIXME<commit_after>//! This module handles fuzzy-searching of functions, structs and other symbols
//! by name across the whole workspace and dependencies.
//!
//! It works by building an incrementally-updated text-search index of all
//! symbols. The backbone of the index is the **awesome** `fst` crate by
//! @BurntSushi.
//!
//! In a nutshell, you give a set of strings to `fst`, and it builds a
//! finite state machine describing this set of strings. The strings which
//! could fuzzy-match a pattern can also be described by a finite state machine.
//! What is freaking cool is that you can now traverse both state machines in
//! lock-step to enumerate the strings which are both in the input set and
//! fuzz-match the query. Or, more formally, given two languages described by
//! FSTs, one can build a product FST which describes the intersection of the
//! languages.
//!
//! `fst` does not support cheap updating of the index, but it supports unioning
//! of state machines. So, to account for changing source code, we build an FST
//! for each library (which is assumed to never change) and an FST for each Rust
//! file in the current workspace, and run a query against the union of all
//! those FSTs.

use std::{
    cmp::Ordering,
    fmt,
    hash::{Hash, Hasher},
    mem,
    sync::Arc,
};

use fst::{self, Streamer};
use hir::db::DefDatabase;
use ra_db::{
    salsa::{self, ParallelDatabase},
    CrateId, FileId, SourceDatabaseExt, SourceRootId,
};
use ra_syntax::{
    ast::{self, NameOwner},
    match_ast, AstNode, Parse, SmolStr, SourceFile,
    SyntaxKind::{self, *},
    SyntaxNode, SyntaxNodePtr, TextRange, WalkEvent,
};
#[cfg(not(feature = "wasm"))]
use rayon::prelude::*;

use crate::RootDatabase;

#[derive(Debug)]
pub struct Query {
    query: String,
    lowercased: String,
    only_types: bool,
    libs: bool,
    exact: bool,
    limit: usize,
}

impl Query {
    pub fn new(query: String) -> Query {
        let lowercased = query.to_lowercase();
        Query {
            query,
            lowercased,
            only_types: false,
            libs: false,
            exact: false,
            limit: usize::max_value(),
        }
    }

    pub fn only_types(&mut self) {
        self.only_types = true;
    }

    pub fn libs(&mut self) {
        self.libs = true;
    }

    pub fn exact(&mut self) {
        self.exact = true;
    }

    pub fn limit(&mut self, limit: usize) {
        self.limit = limit
    }
}

#[salsa::query_group(SymbolsDatabaseStorage)]
pub trait SymbolsDatabase: hir::db::HirDatabase {
    fn file_symbols(&self, file_id: FileId) -> Arc<SymbolIndex>;
    #[salsa::input]
    fn library_symbols(&self, id: SourceRootId) -> Arc<SymbolIndex>;
    /// The set of "local" (that is, from the current workspace) roots.
    /// Files in local roots are assumed to change frequently.
    #[salsa::input]
    fn local_roots(&self) -> Arc<Vec<SourceRootId>>;
    /// The set of roots for crates.io libraries.
    /// Files in libraries are assumed to never change.
    #[salsa::input]
    fn library_roots(&self) -> Arc<Vec<SourceRootId>>;
}

fn file_symbols(db: &impl SymbolsDatabase, file_id: FileId) -> Arc<SymbolIndex> {
    db.check_canceled();
    let parse = db.parse(file_id);

    let symbols = source_file_to_file_symbols(&parse.tree(), file_id);

    // FIXME: add macros here

    Arc::new(SymbolIndex::new(symbols))
}

/// Need to wrap Snapshot to provide `Clone` impl for `map_with`
struct Snap(salsa::Snapshot<RootDatabase>);
impl Clone for Snap {
    fn clone(&self) -> Snap {
        Snap(self.0.snapshot())
    }
}

// Feature: Workspace Symbol
//
// Uses fuzzy-search to find types, modules and functions by name across your
// project and dependencies. This is **the** most useful feature, which improves code
// navigation tremendously. It mostly works on top of the built-in LSP
// functionality, however `#` and `*` symbols can be used to narrow down the
// search. Specifically,
//
// - `Foo` searches for `Foo` type in the current workspace
// - `foo#` searches for `foo` function in the current workspace
// - `Foo*` searches for `Foo` type among dependencies, including `stdlib`
// - `foo#*` searches for `foo` function among dependencies
//
// That is, `#` switches from "types" to all symbols, `*` switches from the current
// workspace to dependencies.
//
// |===
// | Editor  | Shortcut
//
// | VS Code | kbd:[Ctrl+T]
// |===
pub fn world_symbols(db: &RootDatabase, query: Query) -> Vec<FileSymbol> {
    let _p = ra_prof::profile("world_symbols").detail(|| query.query.clone());

    let buf: Vec<Arc<SymbolIndex>> = if query.libs {
        let snap = Snap(db.snapshot());
        #[cfg(not(feature = "wasm"))]
        let buf = db
            .library_roots()
            .par_iter()
            .map_with(snap, |db, &lib_id| db.0.library_symbols(lib_id))
            .collect();

        #[cfg(feature = "wasm")]
        let buf = db.library_roots().iter().map(|&lib_id| snap.0.library_symbols(lib_id)).collect();

        buf
    } else {
        let mut files = Vec::new();
        for &root in db.local_roots().iter() {
            let sr = db.source_root(root);
            files.extend(sr.walk())
        }

        let snap = Snap(db.snapshot());
        #[cfg(not(feature = "wasm"))]
        let buf =
            files.par_iter().map_with(snap, |db, &file_id| db.0.file_symbols(file_id)).collect();

        #[cfg(feature = "wasm")]
        let buf = files.iter().map(|&file_id| snap.0.file_symbols(file_id)).collect();

        buf
    };
    query.search(&buf)
}

pub fn crate_symbols(db: &RootDatabase, krate: CrateId, query: Query) -> Vec<FileSymbol> {
    // FIXME(#4842): This now depends on CrateDefMap, why not build the entire symbol index from
    // that instead?

    let def_map = db.crate_def_map(krate);
    let mut files = Vec::new();
    let mut modules = vec![def_map.root];
    while let Some(module) = modules.pop() {
        let data = &def_map[module];
        files.extend(data.origin.file_id());
        modules.extend(data.children.values());
    }

    let snap = Snap(db.snapshot());

    #[cfg(not(feature = "wasm"))]
    let buf = files
        .par_iter()
        .map_with(snap, |db, &file_id| db.0.file_symbols(file_id))
        .collect::<Vec<_>>();

    #[cfg(feature = "wasm")]
    let buf = files.iter().map(|&file_id| snap.0.file_symbols(file_id)).collect::<Vec<_>>();

    query.search(&buf)
}

pub fn index_resolve(db: &RootDatabase, name_ref: &ast::NameRef) -> Vec<FileSymbol> {
    let name = name_ref.text();
    let mut query = Query::new(name.to_string());
    query.exact();
    query.limit(4);
    world_symbols(db, query)
}

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

impl fmt::Debug for SymbolIndex {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.debug_struct("SymbolIndex").field("n_symbols", &self.symbols.len()).finish()
    }
}

impl PartialEq for SymbolIndex {
    fn eq(&self, other: &SymbolIndex) -> bool {
        self.symbols == other.symbols
    }
}

impl Eq for SymbolIndex {}

impl Hash for SymbolIndex {
    fn hash<H: Hasher>(&self, hasher: &mut H) {
        self.symbols.hash(hasher)
    }
}

impl SymbolIndex {
    fn new(mut symbols: Vec<FileSymbol>) -> SymbolIndex {
        fn cmp(lhs: &FileSymbol, rhs: &FileSymbol) -> Ordering {
            let lhs_chars = lhs.name.chars().map(|c| c.to_ascii_lowercase());
            let rhs_chars = rhs.name.chars().map(|c| c.to_ascii_lowercase());
            lhs_chars.cmp(rhs_chars)
        }

        #[cfg(not(feature = "wasm"))]
        symbols.par_sort_by(cmp);

        #[cfg(feature = "wasm")]
        symbols.sort_by(cmp);

        let mut builder = fst::MapBuilder::memory();

        let mut last_batch_start = 0;

        for idx in 0..symbols.len() {
            if let Some(next_symbol) = symbols.get(idx + 1) {
                if cmp(&symbols[last_batch_start], next_symbol) == Ordering::Equal {
                    continue;
                }
            }

            let start = last_batch_start;
            let end = idx + 1;
            last_batch_start = end;

            let key = symbols[start].name.as_str().to_ascii_lowercase();
            let value = SymbolIndex::range_to_map_value(start, end);

            builder.insert(key, value).unwrap();
        }

        let map = fst::Map::new(builder.into_inner().unwrap()).unwrap();
        SymbolIndex { symbols, map }
    }

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

    pub fn memory_size(&self) -> usize {
        self.map.as_fst().size() + self.symbols.len() * mem::size_of::<FileSymbol>()
    }

    #[cfg(not(feature = "wasm"))]
    pub(crate) fn for_files(
        files: impl ParallelIterator<Item = (FileId, Parse<ast::SourceFile>)>,
    ) -> SymbolIndex {
        let symbols = files
            .flat_map(|(file_id, file)| source_file_to_file_symbols(&file.tree(), file_id))
            .collect::<Vec<_>>();
        SymbolIndex::new(symbols)
    }

    #[cfg(feature = "wasm")]
    pub(crate) fn for_files(
        files: impl Iterator<Item = (FileId, Parse<ast::SourceFile>)>,
    ) -> SymbolIndex {
        let symbols = files
            .flat_map(|(file_id, file)| source_file_to_file_symbols(&file.tree(), file_id))
            .collect::<Vec<_>>();
        SymbolIndex::new(symbols)
    }

    fn range_to_map_value(start: usize, end: usize) -> u64 {
        debug_assert![start <= (std::u32::MAX as usize)];
        debug_assert![end <= (std::u32::MAX as usize)];

        ((start as u64) << 32) | end as u64
    }

    fn map_value_to_range(value: u64) -> (usize, usize) {
        let end = value as u32 as usize;
        let start = (value >> 32) as usize;
        (start, end)
    }
}

impl Query {
    pub(crate) fn search(self, indices: &[Arc<SymbolIndex>]) -> Vec<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() {
            for indexed_value in indexed_values {
                let symbol_index = &indices[indexed_value.index];
                let (start, end) = SymbolIndex::map_value_to_range(indexed_value.value);

                for symbol in &symbol_index.symbols[start..end] {
                    if self.only_types && !is_type(symbol.kind) {
                        continue;
                    }
                    if self.exact && symbol.name != self.query {
                        continue;
                    }

                    res.push(symbol.clone());
                    if res.len() >= self.limit {
                        return res;
                    }
                }
            }
        }
        res
    }
}

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

/// The actual data that is stored in the index. It should be as compact as
/// possible.
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct FileSymbol {
    pub file_id: FileId,
    pub name: SmolStr,
    pub kind: SyntaxKind,
    pub range: TextRange,
    pub ptr: SyntaxNodePtr,
    pub name_range: Option<TextRange>,
    pub container_name: Option<SmolStr>,
}

fn source_file_to_file_symbols(source_file: &SourceFile, file_id: FileId) -> Vec<FileSymbol> {
    let mut symbols = Vec::new();
    let mut stack = Vec::new();

    for event in source_file.syntax().preorder() {
        match event {
            WalkEvent::Enter(node) => {
                if let Some(mut symbol) = to_file_symbol(&node, file_id) {
                    symbol.container_name = stack.last().cloned();

                    stack.push(symbol.name.clone());
                    symbols.push(symbol);
                }
            }

            WalkEvent::Leave(node) => {
                if to_symbol(&node).is_some() {
                    stack.pop();
                }
            }
        }
    }

    symbols
}

fn to_symbol(node: &SyntaxNode) -> Option<(SmolStr, SyntaxNodePtr, TextRange)> {
    fn decl<N: NameOwner>(node: N) -> Option<(SmolStr, SyntaxNodePtr, TextRange)> {
        let name = node.name()?;
        let name_range = name.syntax().text_range();
        let name = name.text().clone();
        let ptr = SyntaxNodePtr::new(node.syntax());

        Some((name, ptr, name_range))
    }
    match_ast! {
        match node {
            ast::FnDef(it) => decl(it),
            ast::StructDef(it) => decl(it),
            ast::EnumDef(it) => decl(it),
            ast::TraitDef(it) => decl(it),
            ast::Module(it) => decl(it),
            ast::TypeAliasDef(it) => decl(it),
            ast::ConstDef(it) => decl(it),
            ast::StaticDef(it) => decl(it),
            ast::MacroCall(it) => {
                if it.is_macro_rules().is_some() {
                    decl(it)
                } else {
                    None
                }
            },
            _ => None,
        }
    }
}

fn to_file_symbol(node: &SyntaxNode, file_id: FileId) -> Option<FileSymbol> {
    to_symbol(node).map(move |(name, ptr, name_range)| FileSymbol {
        name,
        kind: node.kind(),
        range: node.text_range(),
        ptr,
        file_id,
        name_range: Some(name_range),
        container_name: None,
    })
}
<|endoftext|>


<commit_before>//! Command like parsing for rust-analyzer.
//!
//! If run started args, we run the LSP server loop. With a subcommand, we do a
//! one-time batch processing.

use anyhow::{bail, Result};
use pico_args::Arguments;
use rust_analyzer::cli::{BenchWhat, Position, Verbosity};

use std::{fmt::Write, path::PathBuf};

pub(crate) struct Args {
    pub(crate) verbosity: Verbosity,
    pub(crate) command: Command,
}

pub(crate) enum Command {
    Parse {
        no_dump: bool,
    },
    Symbols,
    Highlight {
        rainbow: bool,
    },
    Stats {
        randomize: bool,
        memory_usage: bool,
        only: Option<String>,
        with_deps: bool,
        path: PathBuf,
    },
    Bench {
        path: PathBuf,
        what: BenchWhat,
    },
    RunServer,
    Version,
}

impl Args {
    pub(crate) fn parse() -> Result<Result<Args, HelpPrinted>> {
        let mut matches = Arguments::from_env();

        if matches.contains("--version") {
            matches.finish().or_else(handle_extra_flags)?;
            return Ok(Ok(Args { verbosity: Verbosity::Normal, command: Command::Version }));
        }

        let verbosity = match (
            matches.contains(["-vv", "--spammy"]),
            matches.contains(["-v", "--verbose"]),
            matches.contains(["-q", "--quiet"]),
        ) {
            (true, _, true) => bail!("Invalid flags: -q conflicts with -vv"),
            (true, _, false) => Verbosity::Spammy,
            (false, false, false) => Verbosity::Normal,
            (false, false, true) => Verbosity::Quiet,
            (false, true, false) => Verbosity::Verbose,
            (false, true, true) => bail!("Invalid flags: -q conflicts with -v"),
        };

        let subcommand = match matches.subcommand()? {
            Some(it) => it,
            None => {
                matches.finish().or_else(handle_extra_flags)?;
                return Ok(Ok(Args { verbosity, command: Command::RunServer }));
            }
        };
        let command = match subcommand.as_str() {
            "parse" => {
                if matches.contains(["-h", "--help"]) {
                    eprintln!(
                        "\
ra-cli-parse

USAGE:
    rust-analyzer parse [FLAGS]

FLAGS:
    -h, --help       Prints help inforamtion
        --no-dump"
                    );
                    return Ok(Err(HelpPrinted));
                }

                let no_dump = matches.contains("--no-dump");
                matches.finish().or_else(handle_extra_flags)?;
                Command::Parse { no_dump }
            }
            "symbols" => {
                if matches.contains(["-h", "--help"]) {
                    eprintln!(
                        "\
ra-cli-symbols

USAGE:
    rust-analyzer highlight [FLAGS]

FLAGS:
    -h, --help    Prints help inforamtion"
                    );
                    return Ok(Err(HelpPrinted));
                }

                matches.finish().or_else(handle_extra_flags)?;

                Command::Symbols
            }
            "highlight" => {
                if matches.contains(["-h", "--help"]) {
                    eprintln!(
                        "\
ra-cli-highlight

USAGE:
    rust-analyzer highlight [FLAGS]

FLAGS:
    -h, --help       Prints help information
    -r, --rainbow"
                    );
                    return Ok(Err(HelpPrinted));
                }

                let rainbow = matches.contains(["-r", "--rainbow"]);
                matches.finish().or_else(handle_extra_flags)?;
                Command::Highlight { rainbow }
            }
            "analysis-stats" => {
                if matches.contains(["-h", "--help"]) {
                    eprintln!(
                        "\
ra-cli-analysis-stats

USAGE:
    rust-analyzer analysis-stats [FLAGS] [OPTIONS] [PATH]

FLAGS:
    -h, --help            Prints help information
        --memory-usage
    -v, --verbose
    -q, --quiet

OPTIONS:
    -o <ONLY>

ARGS:
    <PATH>"
                    );
                    return Ok(Err(HelpPrinted));
                }

                let randomize = matches.contains("--randomize");
                let memory_usage = matches.contains("--memory-usage");
                let only: Option<String> = matches.opt_value_from_str(["-o", "--only"])?;
                let with_deps: bool = matches.contains("--with-deps");
                let path = {
                    let mut trailing = matches.free()?;
                    if trailing.len() != 1 {
                        bail!("Invalid flags");
                    }
                    trailing.pop().unwrap().into()
                };

                Command::Stats { randomize, memory_usage, only, with_deps, path }
            }
            "analysis-bench" => {
                if matches.contains(["-h", "--help"]) {
                    eprintln!(
                        "\
rust-analyzer-analysis-bench

USAGE:
    rust-analyzer analysis-bench [FLAGS] [OPTIONS] [PATH]

FLAGS:
    -h, --help        Prints help information
    -v, --verbose

OPTIONS:
    --complete <PATH:LINE:COLUMN>    Compute completions at this location
    --highlight <PATH>               Hightlight this file

ARGS:
    <PATH>    Project to analyse"
                    );
                    return Ok(Err(HelpPrinted));
                }

                let path: PathBuf = matches.opt_value_from_str("--path")?.unwrap_or_default();
                let highlight_path: Option<String> = matches.opt_value_from_str("--highlight")?;
                let complete_path: Option<Position> = matches.opt_value_from_str("--complete")?;
                let goto_def_path: Option<Position> = matches.opt_value_from_str("--goto-def")?;
                let what = match (highlight_path, complete_path, goto_def_path) {
                    (Some(path), None, None) => BenchWhat::Highlight { path: path.into() },
                    (None, Some(position), None) => BenchWhat::Complete(position),
                    (None, None, Some(position)) => BenchWhat::GotoDef(position),
                    _ => panic!(
                        "exactly one of  `--highlight`, `--complete` or `--goto-def` must be set"
                    ),
                };
                Command::Bench { path, what }
            }
            _ => {
                eprintln!(
                    "\
ra-cli

USAGE:
    rust-analyzer <SUBCOMMAND>

FLAGS:
    -h, --help        Prints help information

SUBCOMMANDS:
    analysis-bench
    analysis-stats
    highlight
    parse
    symbols"
                );
                return Ok(Err(HelpPrinted));
            }
        };
        Ok(Ok(Args { verbosity, command }))
    }
}

pub(crate) struct HelpPrinted;

fn handle_extra_flags(e: pico_args::Error) -> Result<()> {
    if let pico_args::Error::UnusedArgsLeft(flags) = e {
        let mut invalid_flags = String::new();
        for flag in flags {
            write!(&mut invalid_flags, "{}, ", flag)?;
        }
        let (invalid_flags, _) = invalid_flags.split_at(invalid_flags.len() - 2);
        bail!("Invalid flags: {}", invalid_flags);
    } else {
        bail!(e);
    }
}
<commit_msg>Fix options for analysis-bench<commit_after>//! Command like parsing for rust-analyzer.
//!
//! If run started args, we run the LSP server loop. With a subcommand, we do a
//! one-time batch processing.

use anyhow::{bail, Result};
use pico_args::Arguments;
use rust_analyzer::cli::{BenchWhat, Position, Verbosity};

use std::{fmt::Write, path::PathBuf};

pub(crate) struct Args {
    pub(crate) verbosity: Verbosity,
    pub(crate) command: Command,
}

pub(crate) enum Command {
    Parse {
        no_dump: bool,
    },
    Symbols,
    Highlight {
        rainbow: bool,
    },
    Stats {
        randomize: bool,
        memory_usage: bool,
        only: Option<String>,
        with_deps: bool,
        path: PathBuf,
    },
    Bench {
        path: PathBuf,
        what: BenchWhat,
    },
    RunServer,
    Version,
}

impl Args {
    pub(crate) fn parse() -> Result<Result<Args, HelpPrinted>> {
        let mut matches = Arguments::from_env();

        if matches.contains("--version") {
            matches.finish().or_else(handle_extra_flags)?;
            return Ok(Ok(Args { verbosity: Verbosity::Normal, command: Command::Version }));
        }

        let verbosity = match (
            matches.contains(["-vv", "--spammy"]),
            matches.contains(["-v", "--verbose"]),
            matches.contains(["-q", "--quiet"]),
        ) {
            (true, _, true) => bail!("Invalid flags: -q conflicts with -vv"),
            (true, _, false) => Verbosity::Spammy,
            (false, false, false) => Verbosity::Normal,
            (false, false, true) => Verbosity::Quiet,
            (false, true, false) => Verbosity::Verbose,
            (false, true, true) => bail!("Invalid flags: -q conflicts with -v"),
        };

        let subcommand = match matches.subcommand()? {
            Some(it) => it,
            None => {
                matches.finish().or_else(handle_extra_flags)?;
                return Ok(Ok(Args { verbosity, command: Command::RunServer }));
            }
        };
        let command = match subcommand.as_str() {
            "parse" => {
                if matches.contains(["-h", "--help"]) {
                    eprintln!(
                        "\
ra-cli-parse

USAGE:
    rust-analyzer parse [FLAGS]

FLAGS:
    -h, --help       Prints help inforamtion
        --no-dump"
                    );
                    return Ok(Err(HelpPrinted));
                }

                let no_dump = matches.contains("--no-dump");
                matches.finish().or_else(handle_extra_flags)?;
                Command::Parse { no_dump }
            }
            "symbols" => {
                if matches.contains(["-h", "--help"]) {
                    eprintln!(
                        "\
ra-cli-symbols

USAGE:
    rust-analyzer highlight [FLAGS]

FLAGS:
    -h, --help    Prints help inforamtion"
                    );
                    return Ok(Err(HelpPrinted));
                }

                matches.finish().or_else(handle_extra_flags)?;

                Command::Symbols
            }
            "highlight" => {
                if matches.contains(["-h", "--help"]) {
                    eprintln!(
                        "\
ra-cli-highlight

USAGE:
    rust-analyzer highlight [FLAGS]

FLAGS:
    -h, --help       Prints help information
    -r, --rainbow"
                    );
                    return Ok(Err(HelpPrinted));
                }

                let rainbow = matches.contains(["-r", "--rainbow"]);
                matches.finish().or_else(handle_extra_flags)?;
                Command::Highlight { rainbow }
            }
            "analysis-stats" => {
                if matches.contains(["-h", "--help"]) {
                    eprintln!(
                        "\
ra-cli-analysis-stats

USAGE:
    rust-analyzer analysis-stats [FLAGS] [OPTIONS] [PATH]

FLAGS:
    -h, --help            Prints help information
        --memory-usage
    -v, --verbose
    -q, --quiet

OPTIONS:
    -o <ONLY>

ARGS:
    <PATH>"
                    );
                    return Ok(Err(HelpPrinted));
                }

                let randomize = matches.contains("--randomize");
                let memory_usage = matches.contains("--memory-usage");
                let only: Option<String> = matches.opt_value_from_str(["-o", "--only"])?;
                let with_deps: bool = matches.contains("--with-deps");
                let path = {
                    let mut trailing = matches.free()?;
                    if trailing.len() != 1 {
                        bail!("Invalid flags");
                    }
                    trailing.pop().unwrap().into()
                };

                Command::Stats { randomize, memory_usage, only, with_deps, path }
            }
            "analysis-bench" => {
                if matches.contains(["-h", "--help"]) {
                    eprintln!(
                        "\
rust-analyzer-analysis-bench

USAGE:
    rust-analyzer analysis-bench [FLAGS] [OPTIONS]

FLAGS:
    -h, --help        Prints help information
    -v, --verbose

OPTIONS:
    --project <PATH>                 Path to directory with Cargo.toml
    --complete <PATH:LINE:COLUMN>    Compute completions at this location
    --goto-def <PATH:LINE:COLUMN>    Compute goto definition at this location
    --highlight <PATH>               Hightlight this file

ARGS:
    <PATH>    Project to analyse"
                    );
                    return Ok(Err(HelpPrinted));
                }

                let path: PathBuf = matches.opt_value_from_str("--project")?.unwrap_or_default();
                let highlight_path: Option<String> = matches.opt_value_from_str("--highlight")?;
                let complete_path: Option<Position> = matches.opt_value_from_str("--complete")?;
                let goto_def_path: Option<Position> = matches.opt_value_from_str("--goto-def")?;
                let what = match (highlight_path, complete_path, goto_def_path) {
                    (Some(path), None, None) => BenchWhat::Highlight { path: path.into() },
                    (None, Some(position), None) => BenchWhat::Complete(position),
                    (None, None, Some(position)) => BenchWhat::GotoDef(position),
                    _ => panic!(
                        "exactly one of  `--highlight`, `--complete` or `--goto-def` must be set"
                    ),
                };
                Command::Bench { path, what }
            }
            _ => {
                eprintln!(
                    "\
ra-cli

USAGE:
    rust-analyzer <SUBCOMMAND>

FLAGS:
    -h, --help        Prints help information

SUBCOMMANDS:
    analysis-bench
    analysis-stats
    highlight
    parse
    symbols"
                );
                return Ok(Err(HelpPrinted));
            }
        };
        Ok(Ok(Args { verbosity, command }))
    }
}

pub(crate) struct HelpPrinted;

fn handle_extra_flags(e: pico_args::Error) -> Result<()> {
    if let pico_args::Error::UnusedArgsLeft(flags) = e {
        let mut invalid_flags = String::new();
        for flag in flags {
            write!(&mut invalid_flags, "{}, ", flag)?;
        }
        let (invalid_flags, _) = invalid_flags.split_at(invalid_flags.len() - 2);
        bail!("Invalid flags: {}", invalid_flags);
    } else {
        bail!(e);
    }
}
<|endoftext|>


<commit_before>use helpers::{HelperDef};
use registry::{Registry};
use context::{Context};
use render::{Renderable, RenderContext, RenderError, render_error, Helper};

#[derive(Clone, Copy)]
pub struct IncludeHelper;

#[derive(Clone, Copy)]
pub struct BlockHelper;

#[derive(Clone, Copy)]
pub struct PartialHelper;

impl HelperDef for IncludeHelper {
    fn call(&self, c: &Context, h: &Helper, r: &Registry, rc: &mut RenderContext) -> Result<(), RenderError> {
        let param = h.params().get(0);

        if param.is_none() {
            return Err(render_error("Param not found for helper"));
        }

        let template = r.get_template(param.unwrap());

        match template {
            Some(t) => {
                (*t).render(c, r, rc)
            },
            None => {
                Err(render_error("Template not found."))
            }
        }
    }
}

impl HelperDef for BlockHelper {
    fn call(&self, c: &Context, h: &Helper, r: &Registry, rc: &mut RenderContext) -> Result<(), RenderError> {
        let param = h.params().get(0);

        if param.is_none() {
            return Err(render_error("Param not found for helper"));
        }

        let partial_template = rc.get_partial(param.unwrap());

        match partial_template {
            Some(partial_template) => {
                partial_template.render(c, r, rc)
            },
            None => {
                h.template().unwrap().render(c, r, rc)
            }
        }
    }
}

impl HelperDef for PartialHelper {
    fn call(&self, _: &Context, h: &Helper, _: &Registry, rc: &mut RenderContext) -> Result<(), RenderError> {
        let param = h.params().get(0);

        if param.is_none() {
            return Err(render_error("Param not found for helper"));
        }

        rc.set_partial(param.unwrap().clone(), h.template().unwrap().clone());
        Ok(())
    }
}

pub static INCLUDE_HELPER: IncludeHelper = IncludeHelper;
pub static BLOCK_HELPER: BlockHelper = BlockHelper;
pub static PARTIAL_HELPER: PartialHelper = PartialHelper;

#[cfg(test)]
mod test {
    use template::{Template};
    use registry::{Registry};

    #[test]
    fn test() {
        let t0 = Template::compile("
{{#block title}}default{{/block}}
".to_string()).ok().unwrap();
        let t1 = Template::compile("{{#partial title}}{{this}}{{/partial}}{{> t0}}".to_string()).ok().unwrap();
        let t2 = Template::compile("{{> t0}}
{{this}}
".to_string()).ok().unwrap();

        let mut handlebars = Registry::new();
        handlebars.register_template("t0", t0);
        handlebars.register_template("t1", t1);
        handlebars.register_template("t2", t2);

        let r0 = handlebars.render("t1", &true);
        assert_eq!(r0.ok().unwrap(), "
true
".to_string());

        let r1 = handlebars.render("t2", &true);
        assert_eq!(r1.ok().unwrap(), "
default

true
".to_string());
    }
}
<commit_msg>(feat) Add ability to set partial context<commit_after>use helpers::{HelperDef};
use registry::{Registry};
use context::{Context};
use render::{Renderable, RenderContext, RenderError, render_error, Helper};

#[derive(Clone, Copy)]
pub struct IncludeHelper;

#[derive(Clone, Copy)]
pub struct BlockHelper;

#[derive(Clone, Copy)]
pub struct PartialHelper;

impl HelperDef for IncludeHelper {
    fn call(&self, c: &Context, h: &Helper, r: &Registry, rc: &mut RenderContext) -> Result<(), RenderError> {
        let template = match h.params().get(0) {
            Some(ref t) => r.get_template(t),
            None => return Err(render_error("Param not found for helper")),
        };

        let context_param = h.params().get(1);
        let old_path = match context_param {
            Some(p) => {
                let old_path = rc.get_path().clone();
                rc.promote_local_vars();
                let new_path = format!("{}/{}", old_path, p);
                rc.set_path(new_path);
                Some(old_path)
            },
            None => None,
        };

        let result = match template {
            Some(t) => (*t).render(c, r, rc),
            None => Err(render_error("Template not found.")),
        };

        if let Some(path) = old_path {
            rc.set_path(path);
            rc.demote_local_vars();
        }

        result
    }
}

impl HelperDef for BlockHelper {
    fn call(&self, c: &Context, h: &Helper, r: &Registry, rc: &mut RenderContext) -> Result<(), RenderError> {
        let param = h.params().get(0);

        if param.is_none() {
            return Err(render_error("Param not found for helper"));
        }

        let partial_template = rc.get_partial(param.unwrap());

        match partial_template {
            Some(partial_template) => {
                partial_template.render(c, r, rc)
            },
            None => {
                h.template().unwrap().render(c, r, rc)
            }
        }
    }
}

impl HelperDef for PartialHelper {
    fn call(&self, _: &Context, h: &Helper, _: &Registry, rc: &mut RenderContext) -> Result<(), RenderError> {
        let param = h.params().get(0);

        if param.is_none() {
            return Err(render_error("Param not found for helper"));
        }

        rc.set_partial(param.unwrap().clone(), h.template().unwrap().clone());
        Ok(())
    }
}

pub static INCLUDE_HELPER: IncludeHelper = IncludeHelper;
pub static BLOCK_HELPER: BlockHelper = BlockHelper;
pub static PARTIAL_HELPER: PartialHelper = PartialHelper;

#[cfg(test)]
mod test {
    use template::{Template};
    use registry::{Registry};
    use std::collections::BTreeMap;

    #[test]
    fn test() {
        let t0 = Template::compile("
{{#block title}}default{{/block}}
".to_string()).ok().unwrap();
        let t1 = Template::compile("{{#partial title}}{{this}}{{/partial}}{{> t0}}".to_string()).ok().unwrap();
        let t2 = Template::compile("{{> t0}}
{{this}}
".to_string()).ok().unwrap();

        let mut handlebars = Registry::new();
        handlebars.register_template("t0", t0);
        handlebars.register_template("t1", t1);
        handlebars.register_template("t2", t2);

        let r0 = handlebars.render("t1", &true);
        assert_eq!(r0.ok().unwrap(), "
true
".to_string());

        let r1 = handlebars.render("t2", &true);
        assert_eq!(r1.ok().unwrap(), "
default

true
".to_string());
    }

    #[test]
    fn test_context() {
        let t0 = Template::compile("
{{> (body) data}}
".to_string()).ok().unwrap();
        let t1 = Template::compile("
{{this}}
".to_string()).ok().unwrap();

        let mut handlebars = Registry::new();
        handlebars.register_template("t0", t0);
        handlebars.register_template("t1", t1);

        let mut map: BTreeMap<String, String> = BTreeMap::new();
        map.insert("body".into(), "t1".into());
        map.insert("data".into(), "hello".into());

        let r0 = handlebars.render("t0", &map);
        assert_eq!(r0.ok().unwrap(), "

hello

".to_string());
    }
}
<|endoftext|>


<commit_before>// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

extern crate gcc;
extern crate build_helper;

use std::process::Command;
use std::env;
use std::path::{PathBuf, Path};

use build_helper::output;

fn detect_llvm_link(llvm_config: &Path) -> (&'static str, Option<&'static str>) {
    let mut version_cmd = Command::new(llvm_config);
    version_cmd.arg("--version");
    let version_output = output(&mut version_cmd);
    let mut parts = version_output.split('.').take(2)
        .filter_map(|s| s.parse::<u32>().ok());
    if let (Some(major), Some(minor)) = (parts.next(), parts.next()) {
        if major > 3 || (major == 3 && minor >= 9) {
            // Force the link mode we want, preferring static by default, but
            // possibly overridden by `configure --enable-llvm-link-shared`.
            if env::var_os("LLVM_LINK_SHARED").is_some() {
                return ("dylib", Some("--link-shared"));
            } else {
                return ("static", Some("--link-static"));
            }
        } else if major == 3 && minor == 8 {
            // Find out LLVM's default linking mode.
            let mut mode_cmd = Command::new(llvm_config);
            mode_cmd.arg("--shared-mode");
            if output(&mut mode_cmd).trim() == "shared" {
                return ("dylib", None);
            } else {
                return ("static", None);
            }
        }
    }
    ("static", None)
}

fn main() {
    println!("cargo:rustc-cfg=cargobuild");

    let target = env::var("TARGET").expect("TARGET was not set");
    let llvm_config = env::var_os("LLVM_CONFIG")
        .map(PathBuf::from)
        .unwrap_or_else(|| {
            if let Some(dir) = env::var_os("CARGO_TARGET_DIR").map(PathBuf::from) {
                let to_test = dir.parent()
                    .unwrap()
                    .parent()
                    .unwrap()
                    .join(&target)
                    .join("llvm/bin/llvm-config");
                if Command::new(&to_test).output().is_ok() {
                    return to_test;
                }
            }
            PathBuf::from("llvm-config")
        });

    println!("cargo:rerun-if-changed={}", llvm_config.display());

    // Test whether we're cross-compiling LLVM. This is a pretty rare case
    // currently where we're producing an LLVM for a different platform than
    // what this build script is currently running on.
    //
    // In that case, there's no guarantee that we can actually run the target,
    // so the build system works around this by giving us the LLVM_CONFIG for
    // the host platform. This only really works if the host LLVM and target
    // LLVM are compiled the same way, but for us that's typically the case.
    //
    // We *want* detect this cross compiling situation by asking llvm-config
    // what it's host-target is. If that's not the TARGET, then we're cross
    // compiling. Unfortunately `llvm-config` seems either be buggy, or we're
    // misconfiguring it, because the `i686-pc-windows-gnu` build of LLVM will
    // report itself with a `--host-target` of `x86_64-pc-windows-gnu`. This
    // tricks us into thinking we're doing a cross build when we aren't, so
    // havoc ensues.
    //
    // In any case, if we're cross compiling, this generally just means that we
    // can't trust all the output of llvm-config becaues it might be targeted
    // for the host rather than the target. As a result a bunch of blocks below
    // are gated on `if !is_crossed`
    let target = env::var("TARGET").expect("TARGET was not set");
    let host = env::var("HOST").expect("HOST was not set");
    let is_crossed = target != host;

    let optional_components =
        ["x86", "arm", "aarch64", "mips", "powerpc", "pnacl", "systemz", "jsbackend", "msp430"];

    // FIXME: surely we don't need all these components, right? Stuff like mcjit
    //        or interpreter the compiler itself never uses.
    let required_components = &["ipo",
                                "bitreader",
                                "bitwriter",
                                "linker",
                                "asmparser",
                                "mcjit",
                                "interpreter",
                                "instrumentation"];

    let components = output(Command::new(&llvm_config).arg("--components"));
    let mut components = components.split_whitespace().collect::<Vec<_>>();
    components.retain(|c| optional_components.contains(c) || required_components.contains(c));

    for component in required_components {
        if !components.contains(component) {
            panic!("require llvm component {} but wasn't found", component);
        }
    }

    for component in components.iter() {
        println!("cargo:rustc-cfg=llvm_component=\"{}\"", component);
    }

    // Link in our own LLVM shims, compiled with the same flags as LLVM
    let mut cmd = Command::new(&llvm_config);
    cmd.arg("--cxxflags");
    let cxxflags = output(&mut cmd);
    let mut cfg = gcc::Config::new();
    for flag in cxxflags.split_whitespace() {
        // Ignore flags like `-m64` when we're doing a cross build
        if is_crossed && flag.starts_with("-m") {
            continue;
        }
        cfg.flag(flag);
    }

    for component in &components[..] {
        let mut flag = String::from("-DLLVM_COMPONENT_");
        flag.push_str(&component.to_uppercase());
        cfg.flag(&flag);
    }

    if env::var_os("LLVM_RUSTLLVM").is_some() {
        cfg.flag("-DLLVM_RUSTLLVM");
    }

    println!("cargo:rerun-if-changed=../rustllvm/PassWrapper.cpp");
    println!("cargo:rerun-if-changed=../rustllvm/RustWrapper.cpp");
    println!("cargo:rerun-if-changed=../rustllvm/ArchiveWrapper.cpp");
    cfg.file("../rustllvm/PassWrapper.cpp")
       .file("../rustllvm/RustWrapper.cpp")
       .file("../rustllvm/ArchiveWrapper.cpp")
       .cpp(true)
       .cpp_link_stdlib(None) // we handle this below
       .compile("librustllvm.a");

    let (llvm_kind, llvm_link_arg) = detect_llvm_link(&llvm_config);

    // Link in all LLVM libraries, if we're uwring the "wrong" llvm-config then
    // we don't pick up system libs because unfortunately they're for the host
    // of llvm-config, not the target that we're attempting to link.
    let mut cmd = Command::new(&llvm_config);
    cmd.arg("--libs");

    if let Some(link_arg) = llvm_link_arg {
        cmd.arg(link_arg);
    }

    if !is_crossed {
        cmd.arg("--system-libs");
    }
    cmd.args(&components[..]);

    for lib in output(&mut cmd).split_whitespace() {
        let name = if lib.starts_with("-l") {
            &lib[2..]
        } else if lib.starts_with("-") {
            &lib[1..]
        } else if Path::new(lib).exists() {
            // On MSVC llvm-config will print the full name to libraries, but
            // we're only interested in the name part
            let name = Path::new(lib).file_name().unwrap().to_str().unwrap();
            name.trim_right_matches(".lib")
        } else if lib.ends_with(".lib") {
            // Some MSVC libraries just come up with `.lib` tacked on, so chop
            // that off
            lib.trim_right_matches(".lib")
        } else {
            continue;
        };

        // Don't need or want this library, but LLVM's CMake build system
        // doesn't provide a way to disable it, so filter it here even though we
        // may or may not have built it. We don't reference anything from this
        // library and it otherwise may just pull in extra dependencies on
        // libedit which we don't want
        if name == "LLVMLineEditor" {
            continue;
        }

        let kind = if name.starts_with("LLVM") {
            llvm_kind
        } else {
            "dylib"
        };
        println!("cargo:rustc-link-lib={}={}", kind, name);
    }

    // LLVM ldflags
    //
    // If we're a cross-compile of LLVM then unfortunately we can't trust these
    // ldflags (largely where all the LLVM libs are located). Currently just
    // hack around this by replacing the host triple with the target and pray
    // that those -L directories are the same!
    let mut cmd = Command::new(&llvm_config);
    cmd.arg("--ldflags");
    for lib in output(&mut cmd).split_whitespace() {
        if lib.starts_with("-LIBPATH:") {
            println!("cargo:rustc-link-search=native={}", &lib[9..]);
        } else if is_crossed {
            if lib.starts_with("-L") {
                println!("cargo:rustc-link-search=native={}",
                         lib[2..].replace(&host, &target));
            }
        } else if lib.starts_with("-l") {
            println!("cargo:rustc-link-lib={}", &lib[2..]);
        } else if lib.starts_with("-L") {
            println!("cargo:rustc-link-search=native={}", &lib[2..]);
        }
    }

    // C++ runtime library
    if !target.contains("msvc") {
        if let Some(s) = env::var_os("LLVM_STATIC_STDCPP") {
            assert!(!cxxflags.contains("stdlib=libc++"));
            let path = PathBuf::from(s);
            println!("cargo:rustc-link-search=native={}",
                     path.parent().unwrap().display());
            println!("cargo:rustc-link-lib=static=stdc++");
        } else if cxxflags.contains("stdlib=libc++") {
            println!("cargo:rustc-link-lib=c++");
        } else {
            println!("cargo:rustc-link-lib=stdc++");
        }
    }
}
<commit_msg>OpenBSD has two stdc++ libraries: use the newer<commit_after>// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

extern crate gcc;
extern crate build_helper;

use std::process::Command;
use std::env;
use std::path::{PathBuf, Path};

use build_helper::output;

fn detect_llvm_link(llvm_config: &Path) -> (&'static str, Option<&'static str>) {
    let mut version_cmd = Command::new(llvm_config);
    version_cmd.arg("--version");
    let version_output = output(&mut version_cmd);
    let mut parts = version_output.split('.').take(2)
        .filter_map(|s| s.parse::<u32>().ok());
    if let (Some(major), Some(minor)) = (parts.next(), parts.next()) {
        if major > 3 || (major == 3 && minor >= 9) {
            // Force the link mode we want, preferring static by default, but
            // possibly overridden by `configure --enable-llvm-link-shared`.
            if env::var_os("LLVM_LINK_SHARED").is_some() {
                return ("dylib", Some("--link-shared"));
            } else {
                return ("static", Some("--link-static"));
            }
        } else if major == 3 && minor == 8 {
            // Find out LLVM's default linking mode.
            let mut mode_cmd = Command::new(llvm_config);
            mode_cmd.arg("--shared-mode");
            if output(&mut mode_cmd).trim() == "shared" {
                return ("dylib", None);
            } else {
                return ("static", None);
            }
        }
    }
    ("static", None)
}

fn main() {
    println!("cargo:rustc-cfg=cargobuild");

    let target = env::var("TARGET").expect("TARGET was not set");
    let llvm_config = env::var_os("LLVM_CONFIG")
        .map(PathBuf::from)
        .unwrap_or_else(|| {
            if let Some(dir) = env::var_os("CARGO_TARGET_DIR").map(PathBuf::from) {
                let to_test = dir.parent()
                    .unwrap()
                    .parent()
                    .unwrap()
                    .join(&target)
                    .join("llvm/bin/llvm-config");
                if Command::new(&to_test).output().is_ok() {
                    return to_test;
                }
            }
            PathBuf::from("llvm-config")
        });

    println!("cargo:rerun-if-changed={}", llvm_config.display());

    // Test whether we're cross-compiling LLVM. This is a pretty rare case
    // currently where we're producing an LLVM for a different platform than
    // what this build script is currently running on.
    //
    // In that case, there's no guarantee that we can actually run the target,
    // so the build system works around this by giving us the LLVM_CONFIG for
    // the host platform. This only really works if the host LLVM and target
    // LLVM are compiled the same way, but for us that's typically the case.
    //
    // We *want* detect this cross compiling situation by asking llvm-config
    // what it's host-target is. If that's not the TARGET, then we're cross
    // compiling. Unfortunately `llvm-config` seems either be buggy, or we're
    // misconfiguring it, because the `i686-pc-windows-gnu` build of LLVM will
    // report itself with a `--host-target` of `x86_64-pc-windows-gnu`. This
    // tricks us into thinking we're doing a cross build when we aren't, so
    // havoc ensues.
    //
    // In any case, if we're cross compiling, this generally just means that we
    // can't trust all the output of llvm-config becaues it might be targeted
    // for the host rather than the target. As a result a bunch of blocks below
    // are gated on `if !is_crossed`
    let target = env::var("TARGET").expect("TARGET was not set");
    let host = env::var("HOST").expect("HOST was not set");
    let is_crossed = target != host;

    let optional_components =
        ["x86", "arm", "aarch64", "mips", "powerpc", "pnacl", "systemz", "jsbackend", "msp430"];

    // FIXME: surely we don't need all these components, right? Stuff like mcjit
    //        or interpreter the compiler itself never uses.
    let required_components = &["ipo",
                                "bitreader",
                                "bitwriter",
                                "linker",
                                "asmparser",
                                "mcjit",
                                "interpreter",
                                "instrumentation"];

    let components = output(Command::new(&llvm_config).arg("--components"));
    let mut components = components.split_whitespace().collect::<Vec<_>>();
    components.retain(|c| optional_components.contains(c) || required_components.contains(c));

    for component in required_components {
        if !components.contains(component) {
            panic!("require llvm component {} but wasn't found", component);
        }
    }

    for component in components.iter() {
        println!("cargo:rustc-cfg=llvm_component=\"{}\"", component);
    }

    // Link in our own LLVM shims, compiled with the same flags as LLVM
    let mut cmd = Command::new(&llvm_config);
    cmd.arg("--cxxflags");
    let cxxflags = output(&mut cmd);
    let mut cfg = gcc::Config::new();
    for flag in cxxflags.split_whitespace() {
        // Ignore flags like `-m64` when we're doing a cross build
        if is_crossed && flag.starts_with("-m") {
            continue;
        }
        cfg.flag(flag);
    }

    for component in &components[..] {
        let mut flag = String::from("-DLLVM_COMPONENT_");
        flag.push_str(&component.to_uppercase());
        cfg.flag(&flag);
    }

    if env::var_os("LLVM_RUSTLLVM").is_some() {
        cfg.flag("-DLLVM_RUSTLLVM");
    }

    println!("cargo:rerun-if-changed=../rustllvm/PassWrapper.cpp");
    println!("cargo:rerun-if-changed=../rustllvm/RustWrapper.cpp");
    println!("cargo:rerun-if-changed=../rustllvm/ArchiveWrapper.cpp");
    cfg.file("../rustllvm/PassWrapper.cpp")
       .file("../rustllvm/RustWrapper.cpp")
       .file("../rustllvm/ArchiveWrapper.cpp")
       .cpp(true)
       .cpp_link_stdlib(None) // we handle this below
       .compile("librustllvm.a");

    let (llvm_kind, llvm_link_arg) = detect_llvm_link(&llvm_config);

    // Link in all LLVM libraries, if we're uwring the "wrong" llvm-config then
    // we don't pick up system libs because unfortunately they're for the host
    // of llvm-config, not the target that we're attempting to link.
    let mut cmd = Command::new(&llvm_config);
    cmd.arg("--libs");

    if let Some(link_arg) = llvm_link_arg {
        cmd.arg(link_arg);
    }

    if !is_crossed {
        cmd.arg("--system-libs");
    }
    cmd.args(&components[..]);

    for lib in output(&mut cmd).split_whitespace() {
        let name = if lib.starts_with("-l") {
            &lib[2..]
        } else if lib.starts_with("-") {
            &lib[1..]
        } else if Path::new(lib).exists() {
            // On MSVC llvm-config will print the full name to libraries, but
            // we're only interested in the name part
            let name = Path::new(lib).file_name().unwrap().to_str().unwrap();
            name.trim_right_matches(".lib")
        } else if lib.ends_with(".lib") {
            // Some MSVC libraries just come up with `.lib` tacked on, so chop
            // that off
            lib.trim_right_matches(".lib")
        } else {
            continue;
        };

        // Don't need or want this library, but LLVM's CMake build system
        // doesn't provide a way to disable it, so filter it here even though we
        // may or may not have built it. We don't reference anything from this
        // library and it otherwise may just pull in extra dependencies on
        // libedit which we don't want
        if name == "LLVMLineEditor" {
            continue;
        }

        let kind = if name.starts_with("LLVM") {
            llvm_kind
        } else {
            "dylib"
        };
        println!("cargo:rustc-link-lib={}={}", kind, name);
    }

    // LLVM ldflags
    //
    // If we're a cross-compile of LLVM then unfortunately we can't trust these
    // ldflags (largely where all the LLVM libs are located). Currently just
    // hack around this by replacing the host triple with the target and pray
    // that those -L directories are the same!
    let mut cmd = Command::new(&llvm_config);
    cmd.arg("--ldflags");
    for lib in output(&mut cmd).split_whitespace() {
        if lib.starts_with("-LIBPATH:") {
            println!("cargo:rustc-link-search=native={}", &lib[9..]);
        } else if is_crossed {
            if lib.starts_with("-L") {
                println!("cargo:rustc-link-search=native={}",
                         lib[2..].replace(&host, &target));
            }
        } else if lib.starts_with("-l") {
            println!("cargo:rustc-link-lib={}", &lib[2..]);
        } else if lib.starts_with("-L") {
            println!("cargo:rustc-link-search=native={}", &lib[2..]);
        }
    }

    // OpenBSD has a particular C++ runtime library name
    let stdcppname = if target.contains("openbsd") {
        "estdc++"
    } else {
        "stdc++"
    };

    // C++ runtime library
    if !target.contains("msvc") {
        if let Some(s) = env::var_os("LLVM_STATIC_STDCPP") {
            assert!(!cxxflags.contains("stdlib=libc++"));
            let path = PathBuf::from(s);
            println!("cargo:rustc-link-search=native={}",
                     path.parent().unwrap().display());
            println!("cargo:rustc-link-lib=static={}", stdcppname);
        } else if cxxflags.contains("stdlib=libc++") {
            println!("cargo:rustc-link-lib=c++");
        } else {
            println!("cargo:rustc-link-lib={}", stdcppname);
        }
    }
}
<|endoftext|>


<commit_before>// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

/*!

Signal handling

This modules provides bindings to receive signals safely, built on top of the
local I/O factory. There are a number of defined signals which can be caught,
but not all signals will work across all platforms (windows doesn't have
definitions for a number of signals.

*/

use container::{Map, MutableMap};
use comm::{Port, SharedChan, stream};
use hashmap;
use option::{Some, None};
use result::{Err, Ok};
use rt::io::io_error;
use rt::rtio::{IoFactory, RtioSignal, with_local_io};

#[repr(int)]
#[deriving(Eq, IterBytes)]
pub enum Signum {
    /// Equivalent to SIGBREAK, delivered when the user presses Ctrl-Break.
    Break = 21i,
    /// Equivalent to SIGHUP, delivered when the user closes the terminal
    /// window. On delivery of HangUp, the program is given approximately
    /// 10 seconds to perfom any cleanup. After that, Windows will
    /// unconditionally terminate it.
    HangUp = 1i,
    /// Equivalent to SIGINT, delivered when the user presses Ctrl-c.
    Interrupt = 2i,
    /// Equivalent to SIGQUIT, delivered when the user presses Ctrl-\.
    Quit = 3i,
    /// Equivalent to SIGTSTP, delivered when the user presses Ctrl-z.
    StopTemporarily = 20i,
    /// Equivalent to SIGUSR1.
    User1 = 10i,
    /// Equivalent to SIGUSR2.
    User2 = 12i,
    /// Equivalent to SIGWINCH, delivered when the console has been resized.
    /// WindowSizeChange may not be delivered in a timely manner; size change
    /// will only be detected when the cursor is being moved.
    WindowSizeChange = 28i,
}

/// Listener provides a port to listen for registered signals.
///
/// Listener automatically unregisters its handles once it is out of scope.
/// However, clients can still unregister signums manually.
///
/// # Example
///
/// ```rust
/// use std::rt::io::signal::{Listener, Interrupt};
///
/// let mut listener = Listener::new();
/// listener.register(signal::Interrupt);
///
/// do spawn {
///     loop {
///         match listener.port.recv() {
///             Interrupt => println("Got Interrupt'ed"),
///             _ => (),
///         }
///     }
/// }
///
/// ```
pub struct Listener {
    /// A map from signums to handles to keep the handles in memory
    priv handles: hashmap::HashMap<Signum, ~RtioSignal>,
    /// chan is where all the handles send signums, which are received by
    /// the clients from port.
    priv chan: SharedChan<Signum>,

    /// Clients of Listener can `recv()` from this port. This is exposed to
    /// allow selection over this port as well as manipulation of the port
    /// directly.
    port: Port<Signum>,
}

impl Listener {
    /// Creates a new listener for signals. Once created, signals are bound via
    /// the `register` method (otherwise nothing will ever be received)
    pub fn new() -> Listener {
        let (port, chan) = stream();
        Listener {
            chan: SharedChan::new(chan),
            port: port,
            handles: hashmap::HashMap::new(),
        }
    }

    /// Listen for a signal, returning true when successfully registered for
    /// signum. Signals can be received using `recv()`.
    ///
    /// Once a signal is registered, this listener will continue to receive
    /// notifications of signals until it is unregistered. This occurs
    /// regardless of the number of other listeners registered in other tasks
    /// (or on this task).
    ///
    /// Signals are still received if there is no task actively waiting for
    /// a signal, and a later call to `recv` will return the signal that was
    /// received while no task was waiting on it.
    ///
    /// # Failure
    ///
    /// If this function fails to register a signal handler, then an error will
    /// be raised on the `io_error` condition and the function will return
    /// false.
    pub fn register(&mut self, signum: Signum) -> bool {
        if self.handles.contains_key(&signum) {
            return true; // self is already listening to signum, so succeed
        }
        do with_local_io |io| {
            match io.signal(signum, self.chan.clone()) {
                Ok(w) => {
                    self.handles.insert(signum, w);
                    Some(())
                },
                Err(ioerr) => {
                    io_error::cond.raise(ioerr);
                    None
                }
            }
        }.is_some()
    }

    /// Unregisters a signal. If this listener currently had a handler
    /// registered for the signal, then it will stop receiving any more
    /// notification about the signal. If the signal has already been received,
    /// it may still be returned by `recv`.
    pub fn unregister(&mut self, signum: Signum) {
        self.handles.pop(&signum);
    }
}

#[cfg(test)]
mod test {
    use libc;
    use rt::io::timer;
    use super::{Listener, Interrupt};
    use comm::{GenericPort, Peekable};

    // kill is only available on Unixes
    #[cfg(unix)]
    #[fixed_stack_segment]
    fn sigint() {
        unsafe {
            libc::funcs::posix88::signal::kill(libc::getpid(), libc::SIGINT);
        }
    }

    #[test] #[cfg(unix)]
    fn test_io_signal_smoketest() {
        let mut signal = Listener::new();
        signal.register(Interrupt);
        sigint();
        timer::sleep(10);
        match signal.port.recv() {
            Interrupt => (),
            s => fail!("Expected Interrupt, got {:?}", s),
        }
    }

    #[test] #[cfg(unix)]
    fn test_io_signal_two_signal_one_signum() {
        let mut s1 = Listener::new();
        let mut s2 = Listener::new();
        s1.register(Interrupt);
        s2.register(Interrupt);
        sigint();
        timer::sleep(10);
        match s1.port.recv() {
            Interrupt => (),
            s => fail!("Expected Interrupt, got {:?}", s),
        }
        match s2.port.recv() {
            Interrupt => (),
            s => fail!("Expected Interrupt, got {:?}", s),
        }
    }

    #[test] #[cfg(unix)]
    fn test_io_signal_unregister() {
        let mut s1 = Listener::new();
        let mut s2 = Listener::new();
        s1.register(Interrupt);
        s2.register(Interrupt);
        s2.unregister(Interrupt);
        sigint();
        timer::sleep(10);
        if s2.port.peek() {
            fail!("Unexpected {:?}", s2.port.recv());
        }
    }

    #[cfg(windows)]
    #[test]
    fn test_io_signal_invalid_signum() {
        use rt::io;
        use super::User1;
        let mut s = Listener::new();
        let mut called = false;
        do io::io_error::cond.trap(|_| {
            called = true;
        }).inside {
            if s.register(User1) {
                fail!("Unexpected successful registry of signum {:?}", User1);
            }
        }
        assert!(called);
    }
}
<commit_msg>temporarily disable tests on android and tagging issue number #10378<commit_after>// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

/*!

Signal handling

This modules provides bindings to receive signals safely, built on top of the
local I/O factory. There are a number of defined signals which can be caught,
but not all signals will work across all platforms (windows doesn't have
definitions for a number of signals.

*/

use container::{Map, MutableMap};
use comm::{Port, SharedChan, stream};
use hashmap;
use option::{Some, None};
use result::{Err, Ok};
use rt::io::io_error;
use rt::rtio::{IoFactory, RtioSignal, with_local_io};

#[repr(int)]
#[deriving(Eq, IterBytes)]
pub enum Signum {
    /// Equivalent to SIGBREAK, delivered when the user presses Ctrl-Break.
    Break = 21i,
    /// Equivalent to SIGHUP, delivered when the user closes the terminal
    /// window. On delivery of HangUp, the program is given approximately
    /// 10 seconds to perfom any cleanup. After that, Windows will
    /// unconditionally terminate it.
    HangUp = 1i,
    /// Equivalent to SIGINT, delivered when the user presses Ctrl-c.
    Interrupt = 2i,
    /// Equivalent to SIGQUIT, delivered when the user presses Ctrl-\.
    Quit = 3i,
    /// Equivalent to SIGTSTP, delivered when the user presses Ctrl-z.
    StopTemporarily = 20i,
    /// Equivalent to SIGUSR1.
    User1 = 10i,
    /// Equivalent to SIGUSR2.
    User2 = 12i,
    /// Equivalent to SIGWINCH, delivered when the console has been resized.
    /// WindowSizeChange may not be delivered in a timely manner; size change
    /// will only be detected when the cursor is being moved.
    WindowSizeChange = 28i,
}

/// Listener provides a port to listen for registered signals.
///
/// Listener automatically unregisters its handles once it is out of scope.
/// However, clients can still unregister signums manually.
///
/// # Example
///
/// ```rust
/// use std::rt::io::signal::{Listener, Interrupt};
///
/// let mut listener = Listener::new();
/// listener.register(signal::Interrupt);
///
/// do spawn {
///     loop {
///         match listener.port.recv() {
///             Interrupt => println("Got Interrupt'ed"),
///             _ => (),
///         }
///     }
/// }
///
/// ```
pub struct Listener {
    /// A map from signums to handles to keep the handles in memory
    priv handles: hashmap::HashMap<Signum, ~RtioSignal>,
    /// chan is where all the handles send signums, which are received by
    /// the clients from port.
    priv chan: SharedChan<Signum>,

    /// Clients of Listener can `recv()` from this port. This is exposed to
    /// allow selection over this port as well as manipulation of the port
    /// directly.
    port: Port<Signum>,
}

impl Listener {
    /// Creates a new listener for signals. Once created, signals are bound via
    /// the `register` method (otherwise nothing will ever be received)
    pub fn new() -> Listener {
        let (port, chan) = stream();
        Listener {
            chan: SharedChan::new(chan),
            port: port,
            handles: hashmap::HashMap::new(),
        }
    }

    /// Listen for a signal, returning true when successfully registered for
    /// signum. Signals can be received using `recv()`.
    ///
    /// Once a signal is registered, this listener will continue to receive
    /// notifications of signals until it is unregistered. This occurs
    /// regardless of the number of other listeners registered in other tasks
    /// (or on this task).
    ///
    /// Signals are still received if there is no task actively waiting for
    /// a signal, and a later call to `recv` will return the signal that was
    /// received while no task was waiting on it.
    ///
    /// # Failure
    ///
    /// If this function fails to register a signal handler, then an error will
    /// be raised on the `io_error` condition and the function will return
    /// false.
    pub fn register(&mut self, signum: Signum) -> bool {
        if self.handles.contains_key(&signum) {
            return true; // self is already listening to signum, so succeed
        }
        do with_local_io |io| {
            match io.signal(signum, self.chan.clone()) {
                Ok(w) => {
                    self.handles.insert(signum, w);
                    Some(())
                },
                Err(ioerr) => {
                    io_error::cond.raise(ioerr);
                    None
                }
            }
        }.is_some()
    }

    /// Unregisters a signal. If this listener currently had a handler
    /// registered for the signal, then it will stop receiving any more
    /// notification about the signal. If the signal has already been received,
    /// it may still be returned by `recv`.
    pub fn unregister(&mut self, signum: Signum) {
        self.handles.pop(&signum);
    }
}

#[cfg(test)]
mod test {
    use libc;
    use rt::io::timer;
    use super::{Listener, Interrupt};
    use comm::{GenericPort, Peekable};

    // kill is only available on Unixes
    #[cfg(unix)]
    #[fixed_stack_segment]
    fn sigint() {
        unsafe {
            libc::funcs::posix88::signal::kill(libc::getpid(), libc::SIGINT);
        }
    }

    #[test] #[cfg(unix, not(target_os="android"))] // FIXME(#10378)
    fn test_io_signal_smoketest() {
        let mut signal = Listener::new();
        signal.register(Interrupt);
        sigint();
        timer::sleep(10);
        match signal.port.recv() {
            Interrupt => (),
            s => fail!("Expected Interrupt, got {:?}", s),
        }
    }

    #[test] #[cfg(unix, not(target_os="android"))] // FIXME(#10378)
    fn test_io_signal_two_signal_one_signum() {
        let mut s1 = Listener::new();
        let mut s2 = Listener::new();
        s1.register(Interrupt);
        s2.register(Interrupt);
        sigint();
        timer::sleep(10);
        match s1.port.recv() {
            Interrupt => (),
            s => fail!("Expected Interrupt, got {:?}", s),
        }
        match s2.port.recv() {
            Interrupt => (),
            s => fail!("Expected Interrupt, got {:?}", s),
        }
    }

    #[test] #[cfg(unix, not(target_os="android"))] // FIXME(#10378)
    fn test_io_signal_unregister() {
        let mut s1 = Listener::new();
        let mut s2 = Listener::new();
        s1.register(Interrupt);
        s2.register(Interrupt);
        s2.unregister(Interrupt);
        sigint();
        timer::sleep(10);
        if s2.port.peek() {
            fail!("Unexpected {:?}", s2.port.recv());
        }
    }

    #[cfg(windows)]
    #[test]
    fn test_io_signal_invalid_signum() {
        use rt::io;
        use super::User1;
        let mut s = Listener::new();
        let mut called = false;
        do io::io_error::cond.trap(|_| {
            called = true;
        }).inside {
            if s.register(User1) {
                fail!("Unexpected successful registry of signum {:?}", User1);
            }
        }
        assert!(called);
    }
}
<|endoftext|>


<commit_before>// https://leetcode.com/problems/circle-and-rectangle-overlapping/
pub fn check_overlap(
    radius: i32,
    x_center: i32,
    y_center: i32,
    x1: i32,
    y1: i32,
    x2: i32,
    y2: i32,
) -> bool {
    todo!()
}

fn main() {
    println!("{}", check_overlap(1, 0, 0, 1, -1, 3, 1)); // true
    println!("{}", check_overlap(1, 1, 1, 1, -3, 2, -1)); // false
    println!("{}", check_overlap(1, 0, 0, -1, 0, 0, 1)); // true
}
<commit_msg>Solve problem 1401<commit_after>// https://leetcode.com/problems/circle-and-rectangle-overlapping/
pub fn check_overlap(
    radius: i32,
    x_center: i32,
    y_center: i32,
    x1: i32,
    y1: i32,
    x2: i32,
    y2: i32,
) -> bool {
    fn find(a1: i32, a2: i32, a_center: i32) -> i32 {
        if a1 <= a_center && a_center <= a2 {
            0
        } else if a1 > a_center {
            a1 - a_center
        } else {
            // a2 > a_center
            a_center - a2
        }
    }
    find(x1, x2, x_center).pow(2) + find(y1, y2, y_center).pow(2) <= radius.pow(2)
}

fn main() {
    println!("{}", check_overlap(1, 0, 0, 1, -1, 3, 1)); // true
    println!("{}", check_overlap(1, 1, 1, 1, -3, 2, -1)); // false
    println!("{}", check_overlap(1, 0, 0, -1, 0, 0, 1)); // true
}
<|endoftext|>


<commit_before>// https://leetcode.com/problems/number-of-sub-arrays-with-odd-sum/
pub fn num_of_subarrays(arr: Vec<i32>) -> i32 {
    let mut answer: i64 = 0;
    for i in 0..arr.len() {
        let mut sum = 0;
        for j in i..arr.len() {
            sum += arr[j];
            if sum % 2 != 0 {
                answer += 1;
            }
        }
    }
    (answer % 1_000_000_007) as i32
}

fn main() {
    println!("{}", num_of_subarrays(vec![1, 3, 5])); // 4
    println!("{}", num_of_subarrays(vec![2, 4, 6])); // 0
    println!("{}", num_of_subarrays(vec![1, 2, 3, 4, 5, 6, 7])); // 16
}
<commit_msg>Solve problem 1524<commit_after>// https://leetcode.com/problems/number-of-sub-arrays-with-odd-sum/
pub fn num_of_subarrays(arr: Vec<i32>) -> i32 {
    let mut answer: i64 = 0;
    let mut odd_sum: i64 = 0;
    let mut even_sum: i64 = 1;
    let mut current_sum: i64 = 0;
    for i in 0..arr.len() {
        current_sum += arr[i] as i64;
        if current_sum % 2 == 0 {
            even_sum += 1;
            answer += odd_sum;
        } else {
            odd_sum += 1;
            answer += even_sum;
        }
    }
    (answer % 1_000_000_007) as i32
}

fn main() {
    println!("{}", num_of_subarrays(vec![1, 3, 5])); // 4
    println!("{}", num_of_subarrays(vec![2, 4, 6])); // 0
    println!("{}", num_of_subarrays(vec![1, 2, 3, 4, 5, 6, 7])); // 16
}
<|endoftext|>


<commit_before>use lapin::{
    message::DeliveryResult,
    options::*,
    publisher_confirm::Confirmation,
    tcp::{AMQPUriTcpExt, NativeTlsConnector},
    types::FieldTable,
    uri::AMQPUri,
    BasicProperties, Connection, ConnectionProperties, ConsumerDelegate, Result,
};
use std::{future::Future, pin::Pin};
use tracing::info;

#[derive(Clone, Debug, PartialEq)]
struct Subscriber;

impl ConsumerDelegate for Subscriber {
    fn on_new_delivery(
        &self,
        delivery: DeliveryResult,
    ) -> Pin<Box<dyn Future<Output = ()> + Send>> {
        Box::pin(async move {
            info!(message=?delivery, "received message");
        })
    }
}

async fn connect() -> Result<Connection> {
    // You need to use amqp:// scheme here to handle the TLS part manually as it's automatic when you use amqps://
    let uri = std::env::var("AMQP_ADDR")
        .unwrap_or_else(|_| "amqp://127.0.0.1:5672/%2f".into())
        .parse::<AMQPUri>()
        .unwrap();
    let connect = move |uri: &AMQPUri| {
        uri.connect().and_then(|stream| {
            let tls_builder = NativeTlsConnector::builder();
            // Perform here your custom TLS setup, with tls_builder.identity or whatever else you need
            stream.into_native_tls(
                tls_builder.build().expect("TLS configuration failed"),
                &uri.authority.host,
            )
        })
    };
    Connection::connector(uri, Box::new(connect), ConnectionProperties::default()).await
}

fn main() {
    if std::env::var("RUST_LOG").is_err() {
        std::env::set_var("RUST_LOG", "info");
    }

    tracing_subscriber::fmt::init();

    async_global_executor::block_on(async {
        let conn = connect().await.expect("connection error");

        info!("CONNECTED");

        //send channel
        let channel_a = conn.create_channel().await.expect("create_channel");
        //receive channel
        let channel_b = conn.create_channel().await.expect("create_channel");
        info!(state=?conn.status().state());

        //create the hello queue
        let queue = channel_a
            .queue_declare(
                "hello",
                QueueDeclareOptions::default(),
                FieldTable::default(),
            )
            .await
            .expect("queue_declare");
        info!(state=?conn.status().state());
        info!(?queue, "Declared queue");

        info!("will consume");
        channel_b
            .basic_consume(
                "hello",
                "my_consumer",
                BasicConsumeOptions::default(),
                FieldTable::default(),
            )
            .await
            .expect("basic_consume")
            .set_delegate(Subscriber);
        info!(state=?conn.status().state());

        info!("will publish");
        let payload = b"Hello world!";
        let confirm = channel_a
            .basic_publish(
                "",
                "hello",
                BasicPublishOptions::default(),
                payload.to_vec(),
                BasicProperties::default(),
            )
            .await
            .expect("basic_publish")
            .await
            .expect("publisher-confirms");
        assert_eq!(confirm, Confirmation::NotRequested);
        info!(state=?conn.status().state());
    })
}
<commit_msg>fix example build<commit_after>use lapin::{
    message::DeliveryResult,
    options::*,
    publisher_confirm::Confirmation,
    tcp::{AMQPUriTcpExt, NativeTlsConnector},
    types::FieldTable,
    uri::AMQPUri,
    BasicProperties, Connection, ConnectionProperties, ConsumerDelegate, Result,
};
use std::{future::Future, pin::Pin};
use tracing::info;

#[derive(Clone, Debug, PartialEq)]
struct Subscriber;

impl ConsumerDelegate for Subscriber {
    fn on_new_delivery(
        &self,
        delivery: DeliveryResult,
    ) -> Pin<Box<dyn Future<Output = ()> + Send>> {
        Box::pin(async move {
            info!(message=?delivery, "received message");
        })
    }
}

async fn connect() -> Result<Connection> {
    // You need to use amqp:// scheme here to handle the TLS part manually as it's automatic when you use amqps://
    let uri = std::env::var("AMQP_ADDR")
        .unwrap_or_else(|_| "amqp://127.0.0.1:5672/%2f".into())
        .parse::<AMQPUri>()
        .unwrap();
    let connect = move |uri: &AMQPUri| {
        uri.connect().and_then(|stream| {
            let tls_builder = NativeTlsConnector::builder();
            // Perform here your custom TLS setup, with tls_builder.identity or whatever else you need
            stream.into_native_tls(
                &tls_builder.build().expect("TLS configuration failed"),
                &uri.authority.host,
            )
        })
    };
    Connection::connector(uri, Box::new(connect), ConnectionProperties::default()).await
}

fn main() {
    if std::env::var("RUST_LOG").is_err() {
        std::env::set_var("RUST_LOG", "info");
    }

    tracing_subscriber::fmt::init();

    async_global_executor::block_on(async {
        let conn = connect().await.expect("connection error");

        info!("CONNECTED");

        //send channel
        let channel_a = conn.create_channel().await.expect("create_channel");
        //receive channel
        let channel_b = conn.create_channel().await.expect("create_channel");
        info!(state=?conn.status().state());

        //create the hello queue
        let queue = channel_a
            .queue_declare(
                "hello",
                QueueDeclareOptions::default(),
                FieldTable::default(),
            )
            .await
            .expect("queue_declare");
        info!(state=?conn.status().state());
        info!(?queue, "Declared queue");

        info!("will consume");
        channel_b
            .basic_consume(
                "hello",
                "my_consumer",
                BasicConsumeOptions::default(),
                FieldTable::default(),
            )
            .await
            .expect("basic_consume")
            .set_delegate(Subscriber);
        info!(state=?conn.status().state());

        info!("will publish");
        let payload = b"Hello world!";
        let confirm = channel_a
            .basic_publish(
                "",
                "hello",
                BasicPublishOptions::default(),
                payload.to_vec(),
                BasicProperties::default(),
            )
            .await
            .expect("basic_publish")
            .await
            .expect("publisher-confirms");
        assert_eq!(confirm, Confirmation::NotRequested);
        info!(state=?conn.status().state());
    })
}
<|endoftext|>