<commit_before>use std::io::Read;
use std::collections::HashMap;
use std::str::FromStr;

use chrono::{DateTime, Utc};
use reqwest;
use serde_json::{self, Value};

use errors::*;

#[derive(Debug, Deserialize)]
pub struct Errors {
    pub detail: String,
}

#[derive(Debug, Deserialize)]
pub struct ErrorResponse {
    pub errors: Vec<Errors>,
}

impl ErrorResponse {
    pub fn detail(&self) -> &str {
        self.errors.get(0).map(|x| x.detail.as_str()).unwrap_or("")
    }
}

impl FromStr for ErrorResponse {
    type Err = Error;
    fn from_str(s: &str) -> ::std::result::Result<Self, Self::Err> {
        serde_json::from_str(s).chain_err(|| "Failed to parse JSON")
    }
}

#[derive(Debug, Deserialize)]
pub struct BadgeData {
    pub attributes: HashMap<String, Option<String>>,
    pub badge_type: String,
}

#[derive(Debug, Deserialize)]
pub struct CategoryData {
    pub category: String,
    pub crates_cnt: i32,
    pub created_at: DateTime<Utc>,
    pub description: String,
    pub id: String,
    pub slug: String,
}

#[derive(Debug, Deserialize)]
pub struct CrateLinks {
    pub version_downloads: String,
    pub versions: Option<String>,
    pub owners: Option<String>,
    pub reverse_dependencies: String,
    pub owner_team: Option<String>,
    pub owner_user: Option<String>,
}

#[derive(Debug, Deserialize)]
pub struct KeywordData {
    pub crates_cnt: i32,
    pub created_at: DateTime<Utc>,
    pub id: String,
    pub keyword: String,
}

#[derive(Debug, Deserialize)]
pub struct VersionLinks {
    pub authors: String,
    pub dependencies: String,
    pub version_downloads: String,
}

#[derive(Debug, Deserialize)]
pub struct VersionData {
    #[serde(rename(deserialize = "crate"))]
    pub krate: String,
    pub created_at: DateTime<Utc>,
    pub dl_path: String,
    pub downloads: i32,
    pub features: HashMap<String, Vec<String>>,
    pub id: i32,
    pub links: VersionLinks,
    pub num: String, // XXX should be semver::Version
    pub updated_at: DateTime<Utc>,
    pub yanked: bool,
    pub license: Option<String>,
}

#[derive(Debug, Deserialize)]
pub struct CrateData {
    pub badges: Option<Vec<BadgeData>>,
    pub categories: Option<Vec<String>>,
    pub created_at: DateTime<Utc>,
    pub description: Option<String>,
    pub documentation: Option<String>,
    pub downloads: i32,
    pub exact_match: bool,
    pub homepage: Option<String>,
    pub id: String,
    pub keywords: Option<Vec<String>>,
    pub license: Option<String>,
    pub links: CrateLinks,
    pub max_version: String,
    pub name: String,
    pub repository: Option<String>,
    pub updated_at: DateTime<Utc>,
    pub versions: Option<Vec<i32>>,
}

#[derive(Debug, Deserialize)]
pub struct ApiResponse {
    pub categories: Vec<CategoryData>,
    #[serde(rename(deserialize = "crate"))]
    pub krate: CrateData,
    pub keywords: Vec<KeywordData>,
    pub versions: Vec<VersionData>,
}

impl FromStr for ApiResponse {
    type Err = Error;
    fn from_str(s: &str) -> ::std::result::Result<Self, Self::Err> {
        serde_json::from_str(s).chain_err(|| "Failed to parse JSON")
    }
}

#[derive(Debug)]
pub struct CratesIO {
    response: reqwest::Response,
    body: String,
}

impl CratesIO {
    pub fn query(krate: &str) -> Result<Self> {
        let url = format!("https://crates.io/api/v1/crates/{}", krate);
        let mut response = reqwest::get(&url)?;
        let mut body = String::with_capacity(20480);
        response.read_to_string(&mut body)?;
        Ok(CratesIO {
            response: response,
            body: body,
        })
    }

    pub fn raw_data(&self) -> &str {
        &self.body
    }

    pub fn as_json(&self) -> Result<Value> {
        serde_json::from_str(&self.body).chain_err(|| "Failed to parse JSON")
        // serde_json::to_string_pretty(&json).chain_err(|| "Failed to prettify")
    }

    pub fn as_data(&self) -> Result<ApiResponse> {
        if *self.response.status() == reqwest::StatusCode::Ok {
            self.body.parse::<ApiResponse>()
        } else {
            self.body
                .parse::<ErrorResponse>()
                .and_then(|er| Err(ErrorKind::CratesIOError(er).into()))
        }
    }
}
<commit_msg>Update fields order to better match that of the crates.io<commit_after>use std::io::Read;
use std::collections::HashMap;
use std::str::FromStr;

use chrono::{DateTime, Utc};
use reqwest;
use serde_json::{self, Value};

use errors::*;

#[derive(Debug, Deserialize)]
pub struct Errors {
    pub detail: String,
}

#[derive(Debug, Deserialize)]
pub struct ErrorResponse {
    pub errors: Vec<Errors>,
}

impl ErrorResponse {
    pub fn detail(&self) -> &str {
        self.errors.get(0).map(|x| x.detail.as_str()).unwrap_or("")
    }
}

impl FromStr for ErrorResponse {
    type Err = Error;
    fn from_str(s: &str) -> ::std::result::Result<Self, Self::Err> {
        serde_json::from_str(s).chain_err(|| "Failed to parse JSON")
    }
}

#[derive(Debug, Deserialize)]
pub struct BadgeData {
    pub badge_type: String,
    pub attributes: HashMap<String, Option<String>>,
}

#[derive(Debug, Deserialize)]
pub struct CategoryData {
    pub category: String,
    pub crates_cnt: i32,
    pub created_at: DateTime<Utc>,
    pub description: String,
    pub id: String,
    pub slug: String,
}

#[derive(Debug, Deserialize)]
pub struct CrateLinks {
    pub version_downloads: String,
    pub versions: Option<String>,
    pub owners: Option<String>,
    pub reverse_dependencies: String,
    pub owner_team: Option<String>,
    pub owner_user: Option<String>,
}

#[derive(Debug, Deserialize)]
pub struct KeywordData {
    pub id: String,
    pub keyword: String,
    pub created_at: DateTime<Utc>,
    pub crates_cnt: i32,
}

#[derive(Debug, Deserialize)]
pub struct VersionLinks {
    pub dependencies: String,
    pub version_downloads: String,
    pub authors: String,
}

#[derive(Debug, Deserialize)]
pub struct VersionData {
    pub id: i32,
    #[serde(rename(deserialize = "crate"))]
    pub krate: String,
    pub num: String, // XXX should be semver::Version
    pub dl_path: String,
    pub updated_at: DateTime<Utc>,
    pub created_at: DateTime<Utc>,
    pub downloads: i32,
    pub features: HashMap<String, Vec<String>>,
    pub yanked: bool,
    pub license: Option<String>,
    pub links: VersionLinks,
}

#[derive(Debug, Deserialize)]
pub struct CrateData {
    pub id: String,
    pub name: String,
    pub updated_at: DateTime<Utc>,
    pub versions: Option<Vec<i32>>,
    pub keywords: Option<Vec<String>>,
    pub categories: Option<Vec<String>>,
    pub badges: Option<Vec<BadgeData>>,
    pub created_at: DateTime<Utc>,
    pub downloads: i32,
    pub max_version: String,
    pub description: Option<String>,
    pub homepage: Option<String>,
    pub documentation: Option<String>,
    pub license: Option<String>,
    pub repository: Option<String>,
    pub links: CrateLinks,
    pub exact_match: bool,
}

#[derive(Debug, Deserialize)]
pub struct ApiResponse {
    pub categories: Vec<CategoryData>,
    #[serde(rename(deserialize = "crate"))]
    pub krate: CrateData,
    pub keywords: Vec<KeywordData>,
    pub versions: Vec<VersionData>,
}

impl FromStr for ApiResponse {
    type Err = Error;
    fn from_str(s: &str) -> ::std::result::Result<Self, Self::Err> {
        serde_json::from_str(s).chain_err(|| "Failed to parse JSON")
    }
}

#[derive(Debug)]
pub struct CratesIO {
    response: reqwest::Response,
    body: String,
}

impl CratesIO {
    pub fn query(krate: &str) -> Result<Self> {
        let url = format!("https://crates.io/api/v1/crates/{}", krate);
        let mut response = reqwest::get(&url)?;
        let mut body = String::with_capacity(20480);
        response.read_to_string(&mut body)?;
        Ok(CratesIO {
            response: response,
            body: body,
        })
    }

    pub fn raw_data(&self) -> &str {
        &self.body
    }

    pub fn as_json(&self) -> Result<Value> {
        serde_json::from_str(&self.body).chain_err(|| "Failed to parse JSON")
        // serde_json::to_string_pretty(&json).chain_err(|| "Failed to prettify")
    }

    pub fn as_data(&self) -> Result<ApiResponse> {
        if *self.response.status() == reqwest::StatusCode::Ok {
            self.body.parse::<ApiResponse>()
        } else {
            self.body
                .parse::<ErrorResponse>()
                .and_then(|er| Err(ErrorKind::CratesIOError(er).into()))
        }
    }
}
<|endoftext|>


<commit_before>//! SemanticEngine implementation for [the racer library](https://github.com/phildawes/racer)
//!
use crate::engine::{Completion, Context, CursorPosition, Definition, SemanticEngine};

use racer::{self, Coordinate, FileCache, Match, Session};

use std::sync::Mutex;

use regex::Regex;

pub struct Racer {
    cache: Mutex<FileCache>,
}

impl Racer {
    pub fn new() -> Racer {
        Racer {
            cache: Mutex::new(FileCache::default()),
        }
    }
}

// Racer's FileCache uses Rc and RefCell internally. We wrap everything in a
// Mutex, and the Rcs aren't possible to leak, so these should be ok. Correct
// solution is to make racer threadsafe.
unsafe impl Sync for Racer {}
unsafe impl Send for Racer {}

use super::Result;
use crate::Config;

impl SemanticEngine for Racer {
    fn initialize(&self, config: &Config) -> Result<()> {
        if let Some(ref src_path) = config.rust_src_path {
            std::env::set_var("RUST_SRC_PATH", src_path);
        }

        Ok(())
    }

    fn find_definition(&self, ctx: &Context) -> Result<Option<Definition>> {
        let cache = match self.cache.lock() {
            Ok(guard) => guard,
            Err(poisoned) => poisoned.into_inner(),
        };

        let session = Session::new(&cache);
        for buffer in &ctx.buffers {
            session.cache_file_contents(buffer.path(), &*buffer.contents)
        }

        // TODO catch_panic: apparently this can panic! in a string operation. Something about pos
        // not landing on a character boundary.
        Ok(
            racer::find_definition(ctx.query_path(), ctx.query_cursor, &session).and_then(|m| {
                m.coords.map(|Coordinate { row, col }| Definition {
                    position: CursorPosition {
                        line: row.0 as usize,
                        col: col.0 as usize,
                    },
                    dtype: format!("{:?}", m.mtype),
                    file_path: m.filepath.to_str().unwrap().to_string(),
                    text: m.matchstr.clone(),
                    text_context: m.contextstr.clone(),
                    docs: m.docs.clone(),
                })
            }),
        )
    }

    fn list_completions(&self, ctx: &Context) -> Result<Option<Vec<Completion>>> {
        let cache = match self.cache.lock() {
            Ok(guard) => guard,
            Err(poisoned) => poisoned.into_inner(),
        };

        let session = Session::new(&cache);
        for buffer in &ctx.buffers {
            session.cache_file_contents(buffer.path(), &*buffer.contents)
        }

        let completions = racer::complete_from_file(ctx.query_path(), ctx.query_cursor, &session)
            .filter_map(
                |Match {
                     matchstr,
                     contextstr,
                     mtype,
                     filepath,
                     coords,
                     ..
                 }| {
                    coords.map(|Coordinate { row, col }| Completion {
                        position: CursorPosition {
                            line: row.0 as usize,
                            col: col.0 as usize,
                        },
                        text: matchstr,
                        context: collapse_whitespace(&contextstr),
                        kind: format!("{:?}", mtype),
                        file_path: filepath.to_str().unwrap().to_string(),
                    })
                },
            )
            .collect::<Vec<_>>();

        if !completions.is_empty() {
            Ok(Some(completions))
        } else {
            Ok(None)
        }
    }
}

pub fn collapse_whitespace(text: &str) -> String {
    Regex::new(r"\s+")
        .unwrap()
        .replace_all(text, " ")
        .to_string()
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::engine::{Buffer, Context, CursorPosition, SemanticEngine};

    use crate::util::fs::TmpFile;

    #[test]
    #[allow(unused_variables)]
    fn find_definition() {
        // FIXME this is just here for side effects.
        let src2 = TmpFile::with_name(
            "src2.rs",
            "
            /// myfn docs
            pub fn myfn() {}
            pub fn foo() {}
            ",
        );

        let src = "
            use src2::*;
            mod src2;
            fn main() {
                myfn();
            }";

        let buffers = vec![Buffer {
            contents: src.to_string(),
            file_path: "src.rs".to_string(),
        }];

        let ctx = Context::new(buffers, CursorPosition { line: 5, col: 17 }, "src.rs");
        let racer = Racer::new();
        let def = racer.find_definition(&ctx).unwrap().unwrap();

        assert_eq!(def.text, "myfn");
        assert_eq!(def.docs, "myfn docs");
    }

    #[test]
    #[allow(unused_variables)]
    fn find_completion() {
        let src = "
            mod mymod {
                /// myfn is a thing
                pub fn myfn<T>(reader: T) where T: ::std::io::Read {}
            }

            fn main() {
                mymod::my
            }";

        let buffers = vec![Buffer {
            contents: src.to_string(),
            file_path: "src.rs".to_string(),
        }];

        let ctx = Context::new(buffers, CursorPosition { line: 8, col: 25 }, "src.rs");
        let racer = Racer::new();

        let completions = racer.list_completions(&ctx).unwrap().unwrap();
        assert_eq!(completions.len(), 1);

        let completion = &completions[0];
        assert_eq!(completion.text, "myfn");
        assert_eq!(completion.position.line, 4);
        assert_eq!(completion.file_path, "src.rs");
    }

    #[test]
    fn find_completion_collapsing_whitespace() {
        let src = "
            struct foo {}

            impl foo {
              fn format( &self )
                -> u32 {
              }
            }

            fn main() {
              let x = foo{};
              x.
            }";

        let buffers = vec![Buffer {
            contents: src.to_string(),
            file_path: "src.rs".to_string(),
        }];

        let ctx = Context::new(buffers, CursorPosition { line: 12, col: 16 }, "src.rs");
        let racer = Racer::new();

        let completions = racer.list_completions(&ctx).unwrap().unwrap();
        assert_eq!(completions.len(), 1);

        let completion = &completions[0];
        assert_eq!(completion.context, "fn format( &self ) -> u32");
    }

    #[test]
    fn collapse_whitespace_test() {
        assert_eq!(collapse_whitespace("foo  foo"), "foo foo");
        assert_eq!(collapse_whitespace("  "), " ");
        assert_eq!(collapse_whitespace("\n\t  \n"), " ");
        assert_eq!(collapse_whitespace("foo\nbar"), "foo bar");
        assert_eq!(
            collapse_whitespace("fn foo( &self )\n   -> u32"),
            "fn foo( &self ) -> u32"
        );
    }
}
<commit_msg>Fix build error: racer Session::new(&cache, None)<commit_after>//! SemanticEngine implementation for [the racer library](https://github.com/phildawes/racer)
//!
use crate::engine::{Completion, Context, CursorPosition, Definition, SemanticEngine};

use racer::{self, Coordinate, FileCache, Match, Session};

use std::sync::Mutex;

use regex::Regex;

pub struct Racer {
    cache: Mutex<FileCache>,
}

impl Racer {
    pub fn new() -> Racer {
        Racer {
            cache: Mutex::new(FileCache::default()),
        }
    }
}

// Racer's FileCache uses Rc and RefCell internally. We wrap everything in a
// Mutex, and the Rcs aren't possible to leak, so these should be ok. Correct
// solution is to make racer threadsafe.
unsafe impl Sync for Racer {}
unsafe impl Send for Racer {}

use super::Result;
use crate::Config;

impl SemanticEngine for Racer {
    fn initialize(&self, config: &Config) -> Result<()> {
        if let Some(ref src_path) = config.rust_src_path {
            std::env::set_var("RUST_SRC_PATH", src_path);
        }

        Ok(())
    }

    fn find_definition(&self, ctx: &Context) -> Result<Option<Definition>> {
        let cache = match self.cache.lock() {
            Ok(guard) => guard,
            Err(poisoned) => poisoned.into_inner(),
        };

        let session = Session::new(&cache, None);
        for buffer in &ctx.buffers {
            session.cache_file_contents(buffer.path(), &*buffer.contents)
        }

        // TODO catch_panic: apparently this can panic! in a string operation. Something about pos
        // not landing on a character boundary.
        Ok(
            racer::find_definition(ctx.query_path(), ctx.query_cursor, &session).and_then(|m| {
                m.coords.map(|Coordinate { row, col }| Definition {
                    position: CursorPosition {
                        line: row.0 as usize,
                        col: col.0 as usize,
                    },
                    dtype: format!("{:?}", m.mtype),
                    file_path: m.filepath.to_str().unwrap().to_string(),
                    text: m.matchstr.clone(),
                    text_context: m.contextstr.clone(),
                    docs: m.docs.clone(),
                })
            }),
        )
    }

    fn list_completions(&self, ctx: &Context) -> Result<Option<Vec<Completion>>> {
        let cache = match self.cache.lock() {
            Ok(guard) => guard,
            Err(poisoned) => poisoned.into_inner(),
        };

        let session = Session::new(&cache, None);
        for buffer in &ctx.buffers {
            session.cache_file_contents(buffer.path(), &*buffer.contents)
        }

        let completions = racer::complete_from_file(ctx.query_path(), ctx.query_cursor, &session)
            .filter_map(
                |Match {
                     matchstr,
                     contextstr,
                     mtype,
                     filepath,
                     coords,
                     ..
                 }| {
                    coords.map(|Coordinate { row, col }| Completion {
                        position: CursorPosition {
                            line: row.0 as usize,
                            col: col.0 as usize,
                        },
                        text: matchstr,
                        context: collapse_whitespace(&contextstr),
                        kind: format!("{:?}", mtype),
                        file_path: filepath.to_str().unwrap().to_string(),
                    })
                },
            )
            .collect::<Vec<_>>();

        if !completions.is_empty() {
            Ok(Some(completions))
        } else {
            Ok(None)
        }
    }
}

pub fn collapse_whitespace(text: &str) -> String {
    Regex::new(r"\s+")
        .unwrap()
        .replace_all(text, " ")
        .to_string()
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::engine::{Buffer, Context, CursorPosition, SemanticEngine};

    use crate::util::fs::TmpFile;

    #[test]
    #[allow(unused_variables)]
    fn find_definition() {
        // FIXME this is just here for side effects.
        let src2 = TmpFile::with_name(
            "src2.rs",
            "
            /// myfn docs
            pub fn myfn() {}
            pub fn foo() {}
            ",
        );

        let src = "
            use src2::*;
            mod src2;
            fn main() {
                myfn();
            }";

        let buffers = vec![Buffer {
            contents: src.to_string(),
            file_path: "src.rs".to_string(),
        }];

        let ctx = Context::new(buffers, CursorPosition { line: 5, col: 17 }, "src.rs");
        let racer = Racer::new();
        let def = racer.find_definition(&ctx).unwrap().unwrap();

        assert_eq!(def.text, "myfn");
        assert_eq!(def.docs, "myfn docs");
    }

    #[test]
    #[allow(unused_variables)]
    fn find_completion() {
        let src = "
            mod mymod {
                /// myfn is a thing
                pub fn myfn<T>(reader: T) where T: ::std::io::Read {}
            }

            fn main() {
                mymod::my
            }";

        let buffers = vec![Buffer {
            contents: src.to_string(),
            file_path: "src.rs".to_string(),
        }];

        let ctx = Context::new(buffers, CursorPosition { line: 8, col: 25 }, "src.rs");
        let racer = Racer::new();

        let completions = racer.list_completions(&ctx).unwrap().unwrap();
        assert_eq!(completions.len(), 1);

        let completion = &completions[0];
        assert_eq!(completion.text, "myfn");
        assert_eq!(completion.position.line, 4);
        assert_eq!(completion.file_path, "src.rs");
    }

    #[test]
    fn find_completion_collapsing_whitespace() {
        let src = "
            struct foo {}

            impl foo {
              fn format( &self )
                -> u32 {
              }
            }

            fn main() {
              let x = foo{};
              x.
            }";

        let buffers = vec![Buffer {
            contents: src.to_string(),
            file_path: "src.rs".to_string(),
        }];

        let ctx = Context::new(buffers, CursorPosition { line: 12, col: 16 }, "src.rs");
        let racer = Racer::new();

        let completions = racer.list_completions(&ctx).unwrap().unwrap();
        assert_eq!(completions.len(), 1);

        let completion = &completions[0];
        assert_eq!(completion.context, "fn format( &self ) -> u32");
    }

    #[test]
    fn collapse_whitespace_test() {
        assert_eq!(collapse_whitespace("foo  foo"), "foo foo");
        assert_eq!(collapse_whitespace("  "), " ");
        assert_eq!(collapse_whitespace("\n\t  \n"), " ");
        assert_eq!(collapse_whitespace("foo\nbar"), "foo bar");
        assert_eq!(
            collapse_whitespace("fn foo( &self )\n   -> u32"),
            "fn foo( &self ) -> u32"
        );
    }
}
<|endoftext|>


<commit_before>extern crate secret;

use secret::{SecretService, Lock};

fn main() {
    let secret_service = SecretService::get().ok().unwrap();
    println!("Session algorithms: {}", secret_service.get_session_algorithms().unwrap());
    println!("Number of collections: {}\n", secret_service.get_collections().unwrap().len());
    for secret_collection in secret_service.get_collections().unwrap() {
        println!("Label for collection: {}\n", secret_collection.get_label());
        if secret_collection.get_locked() {
            println!("Collection is locked");
            secret_collection.unlock().ok().unwrap();
        }
        let all_items = secret_collection.get_items();

        for secret_item in all_items {
            println!("Label for item: {}", secret_item.get_label());
            secret_item.load_secret().ok().unwrap();
            let secret_value = secret_item.get_secret().unwrap();
            println!("ContentType for item: {}", secret_value.get_content_type());
            println!("SecretValue for item: {}\n", secret_value.get().unwrap());
        }
    }
}
<commit_msg>Fix example<commit_after>extern crate secret;

use secret::{SecretService, Lock};

fn main() {
    let secret_service = SecretService::get().ok().unwrap();
    println!("Session algorithms: {}", secret_service.get_session_algorithms());
    println!("Number of collections: {}\n", secret_service.get_collections().len());
    for secret_collection in secret_service.get_collections() {
        println!("Label for collection: {}\n", secret_collection.get_label());
        if secret_collection.get_locked() {
            println!("Collection is locked");
            secret_collection.unlock().ok().unwrap();
        }
        let all_items = secret_collection.get_items();

        for secret_item in all_items {
            println!("Label for item: {}", secret_item.get_label());
            secret_item.load_secret().ok().unwrap();
            let secret_value = secret_item.get_secret().unwrap();
            println!("ContentType for item: {}", secret_value.get_content_type());
            println!("SecretValue for item: {}\n", secret_value.get().unwrap());
        }
    }
}
<|endoftext|>


<commit_before>//! Name resolution algorithm. The end result of the algorithm is an `ItemMap`:
//! a map which maps each module to its scope: the set of items visible in the
//! module. That is, we only resolve imports here, name resolution of item
//! bodies will be done in a separate step.
//!
//! Like Rustc, we use an interactive per-crate algorithm: we start with scopes
//! containing only directly defined items, and then iteratively resolve
//! imports.
//!
//! To make this work nicely in the IDE scenario, we place `InputModuleItems`
//! in between raw syntax and name resolution. `InputModuleItems` are computed
//! using only the module's syntax, and it is all directly defined items plus
//! imports. The plan is to make `InputModuleItems` independent of local
//! modifications (that is, typing inside a function should not change IMIs),
//! so that the results of name resolution can be preserved unless the module
//! structure itself is modified.
pub(crate) mod lower;

use std::sync::Arc;

use ra_db::CrateId;
use ra_arena::map::ArenaMap;
use test_utils::tested_by;
use rustc_hash::{FxHashMap, FxHashSet};

use crate::{
    Module, ModuleDef,
    Path, PathKind,
    HirDatabase, Crate,
    Name,
    module_tree::{ModuleId, ModuleTree},
    nameres::lower::{ImportId, LoweredModule, ImportData},
};

/// `ItemMap` is the result of name resolution. It contains, for each
/// module, the set of visible items.
// FIXME: currenty we compute item map per source-root. We should do it per crate instead.
#[derive(Default, Debug, PartialEq, Eq)]
pub struct ItemMap {
    per_module: ArenaMap<ModuleId, ModuleScope>,
}

impl std::ops::Index<ModuleId> for ItemMap {
    type Output = ModuleScope;
    fn index(&self, id: ModuleId) -> &ModuleScope {
        &self.per_module[id]
    }
}

#[derive(Debug, Default, PartialEq, Eq, Clone)]
pub struct ModuleScope {
    items: FxHashMap<Name, Resolution>,
}

impl ModuleScope {
    pub fn entries<'a>(&'a self) -> impl Iterator<Item = (&'a Name, &'a Resolution)> + 'a {
        self.items.iter()
    }
    pub fn get(&self, name: &Name) -> Option<&Resolution> {
        self.items.get(name)
    }
}

/// `Resolution` is basically `DefId` atm, but it should account for stuff like
/// multiple namespaces, ambiguity and errors.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Resolution {
    /// None for unresolved
    pub def: PerNs<ModuleDef>,
    /// ident by which this is imported into local scope.
    pub import: Option<ImportId>,
}

#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum Namespace {
    Types,
    Values,
}

#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub struct PerNs<T> {
    pub types: Option<T>,
    pub values: Option<T>,
}

impl<T> PerNs<T> {
    pub fn none() -> PerNs<T> {
        PerNs {
            types: None,
            values: None,
        }
    }

    pub fn values(t: T) -> PerNs<T> {
        PerNs {
            types: None,
            values: Some(t),
        }
    }

    pub fn types(t: T) -> PerNs<T> {
        PerNs {
            types: Some(t),
            values: None,
        }
    }

    pub fn both(types: T, values: T) -> PerNs<T> {
        PerNs {
            types: Some(types),
            values: Some(values),
        }
    }

    pub fn is_none(&self) -> bool {
        self.types.is_none() && self.values.is_none()
    }

    pub fn take(self, namespace: Namespace) -> Option<T> {
        match namespace {
            Namespace::Types => self.types,
            Namespace::Values => self.values,
        }
    }

    pub fn take_types(self) -> Option<T> {
        self.take(Namespace::Types)
    }

    pub fn take_values(self) -> Option<T> {
        self.take(Namespace::Values)
    }

    pub fn get(&self, namespace: Namespace) -> Option<&T> {
        self.as_ref().take(namespace)
    }

    pub fn as_ref(&self) -> PerNs<&T> {
        PerNs {
            types: self.types.as_ref(),
            values: self.values.as_ref(),
        }
    }

    pub fn and_then<U>(self, f: impl Fn(T) -> Option<U>) -> PerNs<U> {
        PerNs {
            types: self.types.and_then(&f),
            values: self.values.and_then(&f),
        }
    }

    pub fn map<U>(self, f: impl Fn(T) -> U) -> PerNs<U> {
        PerNs {
            types: self.types.map(&f),
            values: self.values.map(&f),
        }
    }
}

pub(crate) struct Resolver<'a, DB> {
    db: &'a DB,
    input: &'a FxHashMap<ModuleId, Arc<LoweredModule>>,
    krate: CrateId,
    module_tree: Arc<ModuleTree>,
    processed_imports: FxHashSet<(ModuleId, ImportId)>,
    result: ItemMap,
}

impl<'a, DB> Resolver<'a, DB>
where
    DB: HirDatabase,
{
    pub(crate) fn new(
        db: &'a DB,
        input: &'a FxHashMap<ModuleId, Arc<LoweredModule>>,
        krate: CrateId,
    ) -> Resolver<'a, DB> {
        let module_tree = db.module_tree(krate);
        Resolver {
            db,
            input,
            krate,
            module_tree,
            processed_imports: FxHashSet::default(),
            result: ItemMap::default(),
        }
    }

    pub(crate) fn resolve(mut self) -> ItemMap {
        for (&module_id, items) in self.input.iter() {
            self.populate_module(module_id, Arc::clone(items));
        }

        loop {
            let processed_imports_count = self.processed_imports.len();
            for &module_id in self.input.keys() {
                self.db.check_canceled();
                self.resolve_imports(module_id);
            }
            if processed_imports_count == self.processed_imports.len() {
                // no new imports resolved
                break;
            }
        }
        self.result
    }

    fn populate_module(&mut self, module_id: ModuleId, input: Arc<LoweredModule>) {
        let mut module_items = ModuleScope::default();

        // Populate extern crates prelude
        {
            let root_id = module_id.crate_root(&self.module_tree);
            let file_id = root_id.source(&self.module_tree).file_id;
            let crate_graph = self.db.crate_graph();
            if let Some(crate_id) = crate_graph.crate_id_for_crate_root(file_id.as_original_file())
            {
                let krate = Crate::new(crate_id);
                for dep in krate.dependencies(self.db) {
                    if let Some(module) = dep.krate.root_module(self.db) {
                        let def = module.into();
                        self.add_module_item(
                            &mut module_items,
                            dep.name.clone(),
                            PerNs::types(def),
                        );
                    }
                }
            };
        }
        for (import_id, import_data) in input.imports.iter() {
            if let Some(segment) = import_data.path.segments.iter().last() {
                if !import_data.is_glob {
                    module_items.items.insert(
                        segment.name.clone(),
                        Resolution {
                            def: PerNs::none(),
                            import: Some(import_id),
                        },
                    );
                }
            }
        }
        // Populate explicitly declared items, except modules
        for (name, &def) in input.declarations.iter() {
            let resolution = Resolution { def, import: None };
            module_items.items.insert(name.clone(), resolution);
        }

        // Populate modules
        for (name, module_id) in module_id.children(&self.module_tree) {
            let module = Module {
                module_id,
                krate: self.krate,
            };
            self.add_module_item(&mut module_items, name, PerNs::types(module.into()));
        }

        self.result.per_module.insert(module_id, module_items);
    }

    fn add_module_item(&self, module_items: &mut ModuleScope, name: Name, def: PerNs<ModuleDef>) {
        let resolution = Resolution { def, import: None };
        module_items.items.insert(name, resolution);
    }

    fn resolve_imports(&mut self, module_id: ModuleId) {
        for (import_id, import_data) in self.input[&module_id].imports.iter() {
            if self.processed_imports.contains(&(module_id, import_id)) {
                // already done
                continue;
            }
            if self.resolve_import(module_id, import_id, import_data) == ReachedFixedPoint::Yes {
                log::debug!("import {:?} resolved (or definite error)", import_id);
                self.processed_imports.insert((module_id, import_id));
            }
        }
    }

    fn resolve_import(
        &mut self,
        module_id: ModuleId,
        import_id: ImportId,
        import: &ImportData,
    ) -> ReachedFixedPoint {
        log::debug!("resolving import: {:?}", import);
        if import.is_glob {
            return ReachedFixedPoint::Yes;
        };
        let original_module = Module {
            krate: self.krate,
            module_id,
        };
        let (def, reached_fixedpoint) =
            self.result
                .resolve_path_fp(self.db, original_module, &import.path);

        if reached_fixedpoint == ReachedFixedPoint::Yes {
            let last_segment = import.path.segments.last().unwrap();
            self.update(module_id, |items| {
                let res = Resolution {
                    def,
                    import: Some(import_id),
                };
                items.items.insert(last_segment.name.clone(), res);
            });
            log::debug!(
                "resolved import {:?} ({:?}) cross-source root to {:?}",
                last_segment.name,
                import,
                def,
            );
        }
        reached_fixedpoint
    }

    fn update(&mut self, module_id: ModuleId, f: impl FnOnce(&mut ModuleScope)) {
        let module_items = self.result.per_module.get_mut(module_id).unwrap();
        f(module_items)
    }
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum ReachedFixedPoint {
    Yes,
    No,
}

impl ItemMap {
    pub(crate) fn resolve_path(
        &self,
        db: &impl HirDatabase,
        original_module: Module,
        path: &Path,
    ) -> PerNs<ModuleDef> {
        self.resolve_path_fp(db, original_module, path).0
    }

    // Returns Yes if we are sure that additions to `ItemMap` wouldn't change
    // the result.
    fn resolve_path_fp(
        &self,
        db: &impl HirDatabase,
        original_module: Module,
        path: &Path,
    ) -> (PerNs<ModuleDef>, ReachedFixedPoint) {
        let mut curr_per_ns: PerNs<ModuleDef> = PerNs::types(match path.kind {
            PathKind::Crate => original_module.crate_root(db).into(),
            PathKind::Self_ | PathKind::Plain => original_module.into(),
            PathKind::Super => {
                if let Some(p) = original_module.parent(db) {
                    p.into()
                } else {
                    log::debug!("super path in root module");
                    return (PerNs::none(), ReachedFixedPoint::Yes);
                }
            }
            PathKind::Abs => {
                // TODO: absolute use is not supported
                return (PerNs::none(), ReachedFixedPoint::Yes);
            }
        });

        for (i, segment) in path.segments.iter().enumerate() {
            let curr = match curr_per_ns.as_ref().take_types() {
                Some(r) => r,
                None => {
                    // we still have path segments left, but the path so far
                    // didn't resolve in the types namespace => no resolution
                    // (don't break here because curr_per_ns might contain
                    // something in the value namespace, and it would be wrong
                    // to return that)
                    return (PerNs::none(), ReachedFixedPoint::No);
                }
            };
            // resolve segment in curr

            curr_per_ns = match curr {
                ModuleDef::Module(module) => {
                    if module.krate != original_module.krate {
                        let path = Path {
                            segments: path.segments[i..].iter().cloned().collect(),
                            kind: PathKind::Crate,
                        };
                        log::debug!("resolving {:?} in other crate", path);
                        let def = module.resolve_path(db, &path);
                        return (def, ReachedFixedPoint::Yes);
                    }

                    match self[module.module_id].items.get(&segment.name) {
                        Some(res) if !res.def.is_none() => res.def,
                        _ => {
                            log::debug!("path segment {:?} not found", segment.name);
                            return (PerNs::none(), ReachedFixedPoint::No);
                        }
                    }
                }
                ModuleDef::Enum(e) => {
                    // enum variant
                    tested_by!(item_map_enum_importing);
                    let matching_variant = e
                        .variants(db)
                        .into_iter()
                        .find(|(n, _variant)| n == &segment.name);

                    match matching_variant {
                        Some((_n, variant)) => PerNs::both(variant.into(), (*e).into()),
                        None => PerNs::none(),
                    }
                }
                _ => {
                    // could be an inherent method call in UFCS form
                    // (`Struct::method`), or some other kind of associated
                    // item... Which we currently don't handle (TODO)
                    log::debug!(
                        "path segment {:?} resolved to non-module {:?}, but is not last",
                        segment.name,
                        curr,
                    );
                    return (PerNs::none(), ReachedFixedPoint::Yes);
                }
            };
        }
        (curr_per_ns, ReachedFixedPoint::Yes)
    }
}

#[cfg(test)]
mod tests;
<commit_msg>drop obsolete fixme<commit_after>//! Name resolution algorithm. The end result of the algorithm is an `ItemMap`:
//! a map which maps each module to its scope: the set of items visible in the
//! module. That is, we only resolve imports here, name resolution of item
//! bodies will be done in a separate step.
//!
//! Like Rustc, we use an interactive per-crate algorithm: we start with scopes
//! containing only directly defined items, and then iteratively resolve
//! imports.
//!
//! To make this work nicely in the IDE scenario, we place `InputModuleItems`
//! in between raw syntax and name resolution. `InputModuleItems` are computed
//! using only the module's syntax, and it is all directly defined items plus
//! imports. The plan is to make `InputModuleItems` independent of local
//! modifications (that is, typing inside a function should not change IMIs),
//! so that the results of name resolution can be preserved unless the module
//! structure itself is modified.
pub(crate) mod lower;

use std::sync::Arc;

use ra_db::CrateId;
use ra_arena::map::ArenaMap;
use test_utils::tested_by;
use rustc_hash::{FxHashMap, FxHashSet};

use crate::{
    Module, ModuleDef,
    Path, PathKind,
    HirDatabase, Crate,
    Name,
    module_tree::{ModuleId, ModuleTree},
    nameres::lower::{ImportId, LoweredModule, ImportData},
};

/// `ItemMap` is the result of name resolution. It contains, for each
/// module, the set of visible items.
#[derive(Default, Debug, PartialEq, Eq)]
pub struct ItemMap {
    per_module: ArenaMap<ModuleId, ModuleScope>,
}

impl std::ops::Index<ModuleId> for ItemMap {
    type Output = ModuleScope;
    fn index(&self, id: ModuleId) -> &ModuleScope {
        &self.per_module[id]
    }
}

#[derive(Debug, Default, PartialEq, Eq, Clone)]
pub struct ModuleScope {
    items: FxHashMap<Name, Resolution>,
}

impl ModuleScope {
    pub fn entries<'a>(&'a self) -> impl Iterator<Item = (&'a Name, &'a Resolution)> + 'a {
        self.items.iter()
    }
    pub fn get(&self, name: &Name) -> Option<&Resolution> {
        self.items.get(name)
    }
}

/// `Resolution` is basically `DefId` atm, but it should account for stuff like
/// multiple namespaces, ambiguity and errors.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Resolution {
    /// None for unresolved
    pub def: PerNs<ModuleDef>,
    /// ident by which this is imported into local scope.
    pub import: Option<ImportId>,
}

#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum Namespace {
    Types,
    Values,
}

#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub struct PerNs<T> {
    pub types: Option<T>,
    pub values: Option<T>,
}

impl<T> PerNs<T> {
    pub fn none() -> PerNs<T> {
        PerNs {
            types: None,
            values: None,
        }
    }

    pub fn values(t: T) -> PerNs<T> {
        PerNs {
            types: None,
            values: Some(t),
        }
    }

    pub fn types(t: T) -> PerNs<T> {
        PerNs {
            types: Some(t),
            values: None,
        }
    }

    pub fn both(types: T, values: T) -> PerNs<T> {
        PerNs {
            types: Some(types),
            values: Some(values),
        }
    }

    pub fn is_none(&self) -> bool {
        self.types.is_none() && self.values.is_none()
    }

    pub fn take(self, namespace: Namespace) -> Option<T> {
        match namespace {
            Namespace::Types => self.types,
            Namespace::Values => self.values,
        }
    }

    pub fn take_types(self) -> Option<T> {
        self.take(Namespace::Types)
    }

    pub fn take_values(self) -> Option<T> {
        self.take(Namespace::Values)
    }

    pub fn get(&self, namespace: Namespace) -> Option<&T> {
        self.as_ref().take(namespace)
    }

    pub fn as_ref(&self) -> PerNs<&T> {
        PerNs {
            types: self.types.as_ref(),
            values: self.values.as_ref(),
        }
    }

    pub fn and_then<U>(self, f: impl Fn(T) -> Option<U>) -> PerNs<U> {
        PerNs {
            types: self.types.and_then(&f),
            values: self.values.and_then(&f),
        }
    }

    pub fn map<U>(self, f: impl Fn(T) -> U) -> PerNs<U> {
        PerNs {
            types: self.types.map(&f),
            values: self.values.map(&f),
        }
    }
}

pub(crate) struct Resolver<'a, DB> {
    db: &'a DB,
    input: &'a FxHashMap<ModuleId, Arc<LoweredModule>>,
    krate: CrateId,
    module_tree: Arc<ModuleTree>,
    processed_imports: FxHashSet<(ModuleId, ImportId)>,
    result: ItemMap,
}

impl<'a, DB> Resolver<'a, DB>
where
    DB: HirDatabase,
{
    pub(crate) fn new(
        db: &'a DB,
        input: &'a FxHashMap<ModuleId, Arc<LoweredModule>>,
        krate: CrateId,
    ) -> Resolver<'a, DB> {
        let module_tree = db.module_tree(krate);
        Resolver {
            db,
            input,
            krate,
            module_tree,
            processed_imports: FxHashSet::default(),
            result: ItemMap::default(),
        }
    }

    pub(crate) fn resolve(mut self) -> ItemMap {
        for (&module_id, items) in self.input.iter() {
            self.populate_module(module_id, Arc::clone(items));
        }

        loop {
            let processed_imports_count = self.processed_imports.len();
            for &module_id in self.input.keys() {
                self.db.check_canceled();
                self.resolve_imports(module_id);
            }
            if processed_imports_count == self.processed_imports.len() {
                // no new imports resolved
                break;
            }
        }
        self.result
    }

    fn populate_module(&mut self, module_id: ModuleId, input: Arc<LoweredModule>) {
        let mut module_items = ModuleScope::default();

        // Populate extern crates prelude
        {
            let root_id = module_id.crate_root(&self.module_tree);
            let file_id = root_id.source(&self.module_tree).file_id;
            let crate_graph = self.db.crate_graph();
            if let Some(crate_id) = crate_graph.crate_id_for_crate_root(file_id.as_original_file())
            {
                let krate = Crate::new(crate_id);
                for dep in krate.dependencies(self.db) {
                    if let Some(module) = dep.krate.root_module(self.db) {
                        let def = module.into();
                        self.add_module_item(
                            &mut module_items,
                            dep.name.clone(),
                            PerNs::types(def),
                        );
                    }
                }
            };
        }
        for (import_id, import_data) in input.imports.iter() {
            if let Some(segment) = import_data.path.segments.iter().last() {
                if !import_data.is_glob {
                    module_items.items.insert(
                        segment.name.clone(),
                        Resolution {
                            def: PerNs::none(),
                            import: Some(import_id),
                        },
                    );
                }
            }
        }
        // Populate explicitly declared items, except modules
        for (name, &def) in input.declarations.iter() {
            let resolution = Resolution { def, import: None };
            module_items.items.insert(name.clone(), resolution);
        }

        // Populate modules
        for (name, module_id) in module_id.children(&self.module_tree) {
            let module = Module {
                module_id,
                krate: self.krate,
            };
            self.add_module_item(&mut module_items, name, PerNs::types(module.into()));
        }

        self.result.per_module.insert(module_id, module_items);
    }

    fn add_module_item(&self, module_items: &mut ModuleScope, name: Name, def: PerNs<ModuleDef>) {
        let resolution = Resolution { def, import: None };
        module_items.items.insert(name, resolution);
    }

    fn resolve_imports(&mut self, module_id: ModuleId) {
        for (import_id, import_data) in self.input[&module_id].imports.iter() {
            if self.processed_imports.contains(&(module_id, import_id)) {
                // already done
                continue;
            }
            if self.resolve_import(module_id, import_id, import_data) == ReachedFixedPoint::Yes {
                log::debug!("import {:?} resolved (or definite error)", import_id);
                self.processed_imports.insert((module_id, import_id));
            }
        }
    }

    fn resolve_import(
        &mut self,
        module_id: ModuleId,
        import_id: ImportId,
        import: &ImportData,
    ) -> ReachedFixedPoint {
        log::debug!("resolving import: {:?}", import);
        if import.is_glob {
            return ReachedFixedPoint::Yes;
        };
        let original_module = Module {
            krate: self.krate,
            module_id,
        };
        let (def, reached_fixedpoint) =
            self.result
                .resolve_path_fp(self.db, original_module, &import.path);

        if reached_fixedpoint == ReachedFixedPoint::Yes {
            let last_segment = import.path.segments.last().unwrap();
            self.update(module_id, |items| {
                let res = Resolution {
                    def,
                    import: Some(import_id),
                };
                items.items.insert(last_segment.name.clone(), res);
            });
            log::debug!(
                "resolved import {:?} ({:?}) cross-source root to {:?}",
                last_segment.name,
                import,
                def,
            );
        }
        reached_fixedpoint
    }

    fn update(&mut self, module_id: ModuleId, f: impl FnOnce(&mut ModuleScope)) {
        let module_items = self.result.per_module.get_mut(module_id).unwrap();
        f(module_items)
    }
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum ReachedFixedPoint {
    Yes,
    No,
}

impl ItemMap {
    pub(crate) fn resolve_path(
        &self,
        db: &impl HirDatabase,
        original_module: Module,
        path: &Path,
    ) -> PerNs<ModuleDef> {
        self.resolve_path_fp(db, original_module, path).0
    }

    // Returns Yes if we are sure that additions to `ItemMap` wouldn't change
    // the result.
    fn resolve_path_fp(
        &self,
        db: &impl HirDatabase,
        original_module: Module,
        path: &Path,
    ) -> (PerNs<ModuleDef>, ReachedFixedPoint) {
        let mut curr_per_ns: PerNs<ModuleDef> = PerNs::types(match path.kind {
            PathKind::Crate => original_module.crate_root(db).into(),
            PathKind::Self_ | PathKind::Plain => original_module.into(),
            PathKind::Super => {
                if let Some(p) = original_module.parent(db) {
                    p.into()
                } else {
                    log::debug!("super path in root module");
                    return (PerNs::none(), ReachedFixedPoint::Yes);
                }
            }
            PathKind::Abs => {
                // TODO: absolute use is not supported
                return (PerNs::none(), ReachedFixedPoint::Yes);
            }
        });

        for (i, segment) in path.segments.iter().enumerate() {
            let curr = match curr_per_ns.as_ref().take_types() {
                Some(r) => r,
                None => {
                    // we still have path segments left, but the path so far
                    // didn't resolve in the types namespace => no resolution
                    // (don't break here because curr_per_ns might contain
                    // something in the value namespace, and it would be wrong
                    // to return that)
                    return (PerNs::none(), ReachedFixedPoint::No);
                }
            };
            // resolve segment in curr

            curr_per_ns = match curr {
                ModuleDef::Module(module) => {
                    if module.krate != original_module.krate {
                        let path = Path {
                            segments: path.segments[i..].iter().cloned().collect(),
                            kind: PathKind::Crate,
                        };
                        log::debug!("resolving {:?} in other crate", path);
                        let def = module.resolve_path(db, &path);
                        return (def, ReachedFixedPoint::Yes);
                    }

                    match self[module.module_id].items.get(&segment.name) {
                        Some(res) if !res.def.is_none() => res.def,
                        _ => {
                            log::debug!("path segment {:?} not found", segment.name);
                            return (PerNs::none(), ReachedFixedPoint::No);
                        }
                    }
                }
                ModuleDef::Enum(e) => {
                    // enum variant
                    tested_by!(item_map_enum_importing);
                    let matching_variant = e
                        .variants(db)
                        .into_iter()
                        .find(|(n, _variant)| n == &segment.name);

                    match matching_variant {
                        Some((_n, variant)) => PerNs::both(variant.into(), (*e).into()),
                        None => PerNs::none(),
                    }
                }
                _ => {
                    // could be an inherent method call in UFCS form
                    // (`Struct::method`), or some other kind of associated
                    // item... Which we currently don't handle (TODO)
                    log::debug!(
                        "path segment {:?} resolved to non-module {:?}, but is not last",
                        segment.name,
                        curr,
                    );
                    return (PerNs::none(), ReachedFixedPoint::Yes);
                }
            };
        }
        (curr_per_ns, ReachedFixedPoint::Yes)
    }
}

#[cfg(test)]
mod tests;
<|endoftext|>


<commit_before>#[cfg(test)]
mod tests {
    #[test]
    fn it_works() {
    }
}
<commit_msg>Add module-documentation for the documentation crate<commit_after>//! # imag
//!
//! This is the _developer_ documentation for the imag personal information management suite for the
//! commandline.
//!
//! For the user documentation, have a look
//! [at the 'doc' subtree in the repository](http://git.imag-pim.org/imag/tree/doc)
//! which can be compiled to HTML or PDF using [pandoc](pandoc.org) (and might be a bit outdated as
//! imag is not yet released for use).
//!
//! ## General
//!
//! _Some_ things from the user documentation might be helpful for developers as well, so make sure
//! to at least skim over it if you want to contribute to the imag source.
//!
//! Also make sure you had a look at
//! [the CONTRIBUTING](http://git.imag-pim.org/imag/tree/CONTRIBUTING.md)
//! file and [the developers certificate of origin](http://developercertificate.org/), which we also
//! have in the `CONTRIBUTING` file, by the way.
//!
//! ## Contributing
//!
//! All things relevant for contributing are descripbed in
//! [the CONTRIBUTING file](http://git.imag-pim.org/imag/tree/CONTRIBUTING.md),
//! but here are some additional notes:
//!
//! * We have a `editorconfig` file in the repository. Would be nice if you'd
//!   [use it](http://editorconfig.org/)
//! * We have [default.nix](http://git.imag-pim.org/imag/tree/default.nix) file, which can be used
//!   to install dependencies in `nix-shell` environments. If you have a installation of the nix
//!   package manager, feel free to use this opportunity to be _pure_.
//! * If you want to play around with imag, use the
//!   [imagrc.toml](http://git.imag-pim.org/imag/tree/imagrc.toml)
//!   file from the repository, we try to keep it up to date.
//! * You can use [the Makefile](http://git.imag-pim.org/imag/tree/Makefile) to build things (if you
//!   have all dependencies and cargo/rustc installed, of course).
//! * It is a real advantage to use `cargo-check` when developing things - it speeds you up, beleive
//!   me!
//!

<|endoftext|>


<commit_before>pub const RUNTIME: &'static str = r#"
#include "ruby.h"
#include <stdbool.h>
#include <string.h>

static const char *DOT = ".";

static VALUE escape_html(VALUE value) {
    VALUE cgi = rb_const_get(rb_cObject, rb_intern("CGI"));
    return rb_funcall(cgi, rb_intern("escapeHTML"), 1, value);
}

static VALUE fetch(VALUE context, VALUE key) {
    if (RSTRING_LEN(key) == 1 && strncmp(StringValuePtr(key), DOT, 1) == 0) {
        return context;
    }

    switch (rb_type(context)) {
        case T_HASH:
            if (rb_funcall(context, rb_intern("key?"), 1, key) == Qtrue) {
                return rb_hash_aref(context, key);
            } else {
                return Qundef;
            }
        case T_STRUCT:
            // TODO Check rb_struct_members to avoid name error.
            return rb_struct_aref(context, key);
        case T_OBJECT: {
            ID method = rb_to_id(key);
            if (rb_respond_to(context, method) && rb_obj_method_arity(context, method) == 0) {
                return rb_funcall(context, method, 0);
            } else {
                return Qundef;
            }
        }
        default:
            return Qundef;
    }
}

static VALUE context_fetch(VALUE stack, VALUE key) {
    for (long i = RARRAY_LEN(stack) - 1; i >= 0; i--) {
        VALUE context = rb_ary_entry(stack, i);
        VALUE value = fetch(context, key);
        if (value != Qundef) {
            return value;
        }
    }
    return Qnil;
}

static VALUE fetch_path(VALUE stack, VALUE path) {
    VALUE value = context_fetch(stack, rb_ary_entry(path, 0));
    if (value == Qnil) {
        return Qnil;
    }
    for (long i = 1; i < RARRAY_LEN(path); i++) {
        value = fetch(value, rb_ary_entry(path, i));
        if (value == Qundef) {
            return Qnil;
        }
    }
    return value;
}

static void append_value(VALUE buf, VALUE stack, VALUE path, bool escape) {
    VALUE value = fetch_path(stack, path);
    switch (rb_type(value)) {
        case T_NIL:
        case T_UNDEF:
            return;
        case T_STRING:
            break;
        default:
            value = rb_funcall(value, rb_intern("to_s"), 0);
            break;
    }
    rb_str_buf_append(buf, escape ? escape_html(value) : value);
}

static void section(VALUE buf, VALUE stack, VALUE path, void (*block)(VALUE, VALUE)) {
    VALUE value = fetch_path(stack, path);
    switch (rb_type(value)) {
        case T_ARRAY:
            for (long i = 0; i < RARRAY_LEN(value); i++) {
                rb_ary_push(stack, rb_ary_entry(value, i));
                block(buf, stack);
                rb_ary_pop(stack);
            }
            break;
        case T_HASH:
        case T_OBJECT:
        case T_STRUCT:
            rb_ary_push(stack, value);
            block(buf, stack);
            rb_ary_pop(stack);
            break;
        case T_NIL:
        case T_UNDEF:
        case T_FALSE:
            break;
        default:
            block(buf, stack);
            break;
    }
}

static void inverted(VALUE buf, VALUE stack, VALUE path, void (*block)(VALUE, VALUE)) {
    VALUE value = fetch_path(stack, path);
    switch (rb_type(value)) {
        case T_ARRAY:
            if (RARRAY_LEN(value) == 0) {
                block(buf, stack);
            }
            break;
        case T_NIL:
        case T_UNDEF:
        case T_FALSE:
            block(buf, stack);
            break;
    }
}
"#;
<commit_msg>Use RTEST for boolean comparison<commit_after>pub const RUNTIME: &'static str = r#"
#include "ruby.h"
#include <stdbool.h>
#include <string.h>

static const char *DOT = ".";

static VALUE escape_html(VALUE value) {
    VALUE cgi = rb_const_get(rb_cObject, rb_intern("CGI"));
    return rb_funcall(cgi, rb_intern("escapeHTML"), 1, value);
}

static VALUE fetch(VALUE context, VALUE key) {
    if (RSTRING_LEN(key) == 1 && strncmp(StringValuePtr(key), DOT, 1) == 0) {
        return context;
    }

    switch (rb_type(context)) {
        case T_HASH:
            if (RTEST(rb_funcall(context, id_key_p, 1, key))) {
                return rb_hash_aref(context, key);
            } else {
                return Qundef;
            }
        case T_STRUCT:
            // TODO Check rb_struct_members to avoid name error.
            return rb_struct_aref(context, key);
        case T_OBJECT: {
            ID method = rb_to_id(key);
            if (rb_respond_to(context, method) && rb_obj_method_arity(context, method) == 0) {
                return rb_funcall(context, method, 0);
            } else {
                return Qundef;
            }
        }
        default:
            return Qundef;
    }
}

static VALUE context_fetch(VALUE stack, VALUE key) {
    for (long i = RARRAY_LEN(stack) - 1; i >= 0; i--) {
        VALUE context = rb_ary_entry(stack, i);
        VALUE value = fetch(context, key);
        if (value != Qundef) {
            return value;
        }
    }
    return Qnil;
}

static VALUE fetch_path(VALUE stack, VALUE path) {
    VALUE value = context_fetch(stack, rb_ary_entry(path, 0));
    if (value == Qnil) {
        return Qnil;
    }
    for (long i = 1; i < RARRAY_LEN(path); i++) {
        value = fetch(value, rb_ary_entry(path, i));
        if (value == Qundef) {
            return Qnil;
        }
    }
    return value;
}

static void append_value(VALUE buf, VALUE stack, VALUE path, bool escape) {
    VALUE value = fetch_path(stack, path);
    switch (rb_type(value)) {
        case T_NIL:
        case T_UNDEF:
            return;
        case T_STRING:
            break;
        default:
            value = rb_funcall(value, rb_intern("to_s"), 0);
            break;
    }
    rb_str_buf_append(buf, escape ? escape_html(value) : value);
}

static void section(VALUE buf, VALUE stack, VALUE path, void (*block)(VALUE, VALUE)) {
    VALUE value = fetch_path(stack, path);
    switch (rb_type(value)) {
        case T_ARRAY:
            for (long i = 0; i < RARRAY_LEN(value); i++) {
                rb_ary_push(stack, rb_ary_entry(value, i));
                block(buf, stack);
                rb_ary_pop(stack);
            }
            break;
        case T_HASH:
        case T_OBJECT:
        case T_STRUCT:
            rb_ary_push(stack, value);
            block(buf, stack);
            rb_ary_pop(stack);
            break;
        case T_NIL:
        case T_UNDEF:
        case T_FALSE:
            break;
        default:
            block(buf, stack);
            break;
    }
}

static void inverted(VALUE buf, VALUE stack, VALUE path, void (*block)(VALUE, VALUE)) {
    VALUE value = fetch_path(stack, path);
    switch (rb_type(value)) {
        case T_ARRAY:
            if (RARRAY_LEN(value) == 0) {
                block(buf, stack);
            }
            break;
        case T_NIL:
        case T_UNDEF:
        case T_FALSE:
            block(buf, stack);
            break;
    }
}
"#;
<|endoftext|>


<commit_before>#![feature(core)]
//!
//! # Usage
//!
//! ```test_harness
//! extern crate "youtube3-dev" as youtube3;
//! extern crate hyper;
//!
//! # #[test]
//! # fn test() {
//! # // TODO - generate !
//! # }
//! ```
use std::marker::PhantomData;
use std::cell::RefCell;
use std::borrow::BorrowMut;
use std::default::Default;

use hyper;
use oauth2;

mod common;
pub mod videos;

/// Central instance to access all youtube related services
pub struct YouTube<C, NC, A> {
    client: RefCell<C>,
    auth: RefCell<A>,
    _m: PhantomData<NC>
}

impl<'a, C, NC, A> YouTube<C, NC, A>
    where  NC: hyper::net::NetworkConnector,
            C: BorrowMut<hyper::Client<NC>> + 'a,
            A: oauth2::GetToken {

    pub fn new(client: C, authenticator: A) -> YouTube<C, NC, A> {
        YouTube {
            client: RefCell::new(client),
            auth: RefCell::new(authenticator),
            _m: PhantomData,
        }
    }

    pub fn videos(&'a self) -> videos::Service<'a, C, NC, A> {
        videos::Service::new(&self)
    }

    pub fn channel_sections(&'a self) -> ChannelSectionMethodsBuilder<'a, C, NC, A> {
        ChannelSectionMethodsBuilder { hub: &self }
    }
}

pub fn new<C, NC, A>(client: C, authenticator: A) -> YouTube<C, NC, A>
    where  NC: hyper::net::NetworkConnector,
            C: BorrowMut<hyper::Client<NC>>,
            A: oauth2::GetToken {
    YouTube::new(client, authenticator)
}

#[cfg(test)]
mod tests {
    use super::*;
    use hyper;
    use oauth2;

    use std::default::Default;

    #[test]
    fn instantiate() {
        let secret = <oauth2::ApplicationSecret as Default>::default();
        let auth = oauth2::Authenticator::new(
                        &secret,
                        oauth2::DefaultAuthenticatorDelegate,
                        hyper::Client::new(),
                        <oauth2::MemoryStorage as Default>::default(),
                        None);
        let yt = YouTube::new(hyper::Client::new(), auth);

        let v = yt.videos().insert("snippet", &Default::default());
    }

    #[test] fn helper_test() {
        use std::default::Default;
        use oauth2::{Authenticator, DefaultAuthenticatorDelegate, ApplicationSecret, MemoryStorage};

        let secret: ApplicationSecret = Default::default();
        let auth = Authenticator::new(&secret, DefaultAuthenticatorDelegate,
                                      hyper::Client::new(),
                                      <MemoryStorage as Default>::default(), None);
        let mut hub = YouTube::new(hyper::Client::new(), auth);
        let result = hub.channel_sections().insert()
                     .delegate(&mut DefaultDelegate)
                     .doit();
    }
}

pub struct ChannelSectionMethodsBuilder<'a, C, NC, A>
    where NC: 'a,
           C: 'a,
           A: 'a, {

    hub: &'a YouTube<C, NC, A>,
}

impl<'a, C, NC, A> ChannelSectionMethodsBuilder<'a, C, NC, A> {

    /// Create a builder to help you perform the following task:
    ///
    /// Adds a channelSection for the authenticated user's channel.
    pub fn insert(&self) -> ChannelSectionInsertMethodBuilder<'a, C, NC, A> {
        ChannelSectionInsertMethodBuilder {
            hub: self.hub,
            _delegate: Default::default(),
            _part: None,
        }
    }
}

pub struct ChannelSectionInsertMethodBuilder<'a, C, NC, A>
    where NC: 'a,
           C: 'a,
           A: 'a, {

    hub: &'a YouTube<C, NC, A>,
    _delegate: Option<&'a mut Delegate>,
    _part: Option<String>,
}

impl<'a, C, NC, A> ChannelSectionInsertMethodBuilder<'a, C, NC, A> where NC: hyper::net::NetworkConnector, C: BorrowMut<hyper::Client<NC>> + 'a, A: oauth2::GetToken {

    /// Perform the operation you have build so far.
    /// TODO: Build actual call
    pub fn doit(mut self) -> () {
        let mut params: Vec<(&str, String)> = Vec::with_capacity(1);
        if self._part.is_none() {
            self._parts = "parts from request value".to_string();
        }
        if self._delegate.is_some() {
            self._delegate.as_mut().unwrap().connection_error(hyper::HttpError::HttpStatusError);
        }
    }

    pub fn delegate(mut self, new_value: &'a mut Delegate) -> ChannelSectionInsertMethodBuilder<'a, C, NC, A> {
        self._delegate = Some(new_value);
        self
    }

}

pub trait Delegate {

    /// Called whenever there is an HttpError, usually if there are network problems.
    ///
    /// Return retry information.
    fn connection_error(&mut self, hyper::HttpError) -> oauth2::Retry {
        oauth2::Retry::Abort
    }
}

#[derive(Default)]
pub struct DefaultDelegate;

impl Delegate for DefaultDelegate {}
<commit_msg>fix(dev): typo<commit_after>#![feature(core)]
//!
//! # Usage
//!
//! ```test_harness
//! extern crate "youtube3-dev" as youtube3;
//! extern crate hyper;
//!
//! # #[test]
//! # fn test() {
//! # // TODO - generate !
//! # }
//! ```
use std::marker::PhantomData;
use std::cell::RefCell;
use std::borrow::BorrowMut;
use std::default::Default;

use hyper;
use oauth2;

mod common;
pub mod videos;

/// Central instance to access all youtube related services
pub struct YouTube<C, NC, A> {
    client: RefCell<C>,
    auth: RefCell<A>,
    _m: PhantomData<NC>
}

impl<'a, C, NC, A> YouTube<C, NC, A>
    where  NC: hyper::net::NetworkConnector,
            C: BorrowMut<hyper::Client<NC>> + 'a,
            A: oauth2::GetToken {

    pub fn new(client: C, authenticator: A) -> YouTube<C, NC, A> {
        YouTube {
            client: RefCell::new(client),
            auth: RefCell::new(authenticator),
            _m: PhantomData,
        }
    }

    pub fn videos(&'a self) -> videos::Service<'a, C, NC, A> {
        videos::Service::new(&self)
    }

    pub fn channel_sections(&'a self) -> ChannelSectionMethodsBuilder<'a, C, NC, A> {
        ChannelSectionMethodsBuilder { hub: &self }
    }
}

pub fn new<C, NC, A>(client: C, authenticator: A) -> YouTube<C, NC, A>
    where  NC: hyper::net::NetworkConnector,
            C: BorrowMut<hyper::Client<NC>>,
            A: oauth2::GetToken {
    YouTube::new(client, authenticator)
}

#[cfg(test)]
mod tests {
    use super::*;
    use hyper;
    use oauth2;

    use std::default::Default;

    #[test]
    fn instantiate() {
        let secret = <oauth2::ApplicationSecret as Default>::default();
        let auth = oauth2::Authenticator::new(
                        &secret,
                        oauth2::DefaultAuthenticatorDelegate,
                        hyper::Client::new(),
                        <oauth2::MemoryStorage as Default>::default(),
                        None);
        let yt = YouTube::new(hyper::Client::new(), auth);

        let v = yt.videos().insert("snippet", &Default::default());
    }

    #[test] fn helper_test() {
        use std::default::Default;
        use oauth2::{Authenticator, DefaultAuthenticatorDelegate, ApplicationSecret, MemoryStorage};

        let secret: ApplicationSecret = Default::default();
        let auth = Authenticator::new(&secret, DefaultAuthenticatorDelegate,
                                      hyper::Client::new(),
                                      <MemoryStorage as Default>::default(), None);
        let mut hub = YouTube::new(hyper::Client::new(), auth);
        let result = hub.channel_sections().insert()
                     .delegate(&mut DefaultDelegate)
                     .doit();
    }
}

pub struct ChannelSectionMethodsBuilder<'a, C, NC, A>
    where NC: 'a,
           C: 'a,
           A: 'a, {

    hub: &'a YouTube<C, NC, A>,
}

impl<'a, C, NC, A> ChannelSectionMethodsBuilder<'a, C, NC, A> {

    /// Create a builder to help you perform the following task:
    ///
    /// Adds a channelSection for the authenticated user's channel.
    pub fn insert(&self) -> ChannelSectionInsertMethodBuilder<'a, C, NC, A> {
        ChannelSectionInsertMethodBuilder {
            hub: self.hub,
            _delegate: Default::default(),
            _part: None,
        }
    }
}

pub struct ChannelSectionInsertMethodBuilder<'a, C, NC, A>
    where NC: 'a,
           C: 'a,
           A: 'a, {

    hub: &'a YouTube<C, NC, A>,
    _delegate: Option<&'a mut Delegate>,
    _part: Option<String>,
}

impl<'a, C, NC, A> ChannelSectionInsertMethodBuilder<'a, C, NC, A> where NC: hyper::net::NetworkConnector, C: BorrowMut<hyper::Client<NC>> + 'a, A: oauth2::GetToken {

    /// Perform the operation you have build so far.
    /// TODO: Build actual call
    pub fn doit(mut self) -> () {
        let mut params: Vec<(&str, String)> = Vec::with_capacity(1);
        if self._part.is_none() {
            self._part = Some("parts from request value".to_string());
        }
        if self._delegate.is_some() {
            self._delegate.as_mut().unwrap().connection_error(hyper::HttpError::HttpStatusError);
        }
    }

    pub fn delegate(mut self, new_value: &'a mut Delegate) -> ChannelSectionInsertMethodBuilder<'a, C, NC, A> {
        self._delegate = Some(new_value);
        self
    }

}

pub trait Delegate {

    /// Called whenever there is an HttpError, usually if there are network problems.
    ///
    /// Return retry information.
    fn connection_error(&mut self, hyper::HttpError) -> oauth2::Retry {
        oauth2::Retry::Abort
    }
}

#[derive(Default)]
pub struct DefaultDelegate;

impl Delegate for DefaultDelegate {}
<|endoftext|>


<commit_before>/*
 * Copyright (c) Facebook, Inc. and its affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

use crate::build_project::{build_project, build_schema, check_project, WrittenArtifacts};
use crate::compiler_state::{CompilerState, ProjectName};
use crate::config::Config;
use crate::errors::{Error, Result};
use crate::parse_sources::parse_sources;
use crate::watchman::FileSource;
use common::{PerfLogEvent, PerfLogger};
use log::{error, info};
use schema::Schema;
use std::collections::HashMap;

pub struct Compiler<'perf, T>
where
    T: PerfLogger,
{
    config: Config,
    perf_logger: &'perf T,
}

impl<'perf, T: PerfLogger> Compiler<'perf, T> {
    pub fn new(config: Config, perf_logger: &'perf T) -> Self {
        Self {
            config,
            perf_logger,
        }
    }

    pub async fn compile(&self) -> Result<CompilerState> {
        let setup_event = self.perf_logger.create_event("compiler_setup");

        let file_source = FileSource::connect(&self.config, &setup_event).await?;
        let mut compiler_state = file_source.query(&setup_event, self.perf_logger).await?;
        self.build_projects(&mut compiler_state, &setup_event)
            .await?;

        self.perf_logger.complete_event(setup_event);

        Ok(compiler_state)
    }

    pub fn build_schemas(
        &self,
        compiler_state: &CompilerState,
        setup_event: &impl PerfLogEvent,
    ) -> HashMap<ProjectName, Schema> {
        let timer = setup_event.start("build_schemas");
        let mut schemas = HashMap::new();
        match self.config.only_project {
            Some(project_key) => {
                let project_config =
                    self.config.projects.get(&project_key).unwrap_or_else(|| {
                        panic!("Expected the project {} to exist", &project_key)
                    });
                let schema = build_schema(compiler_state, project_config);
                schemas.insert(project_config.name, schema);
            }
            None => {
                for project_config in self.config.projects.values() {
                    let schema = build_schema(compiler_state, project_config);
                    schemas.insert(project_config.name, schema);
                }
            }
        }
        setup_event.stop(timer);
        schemas
    }

    pub async fn watch_with_callback<F>(&self, mut callback: F) -> Result<()>
    where
        F: FnMut(Result<()>),
    {
        let setup_event = self.perf_logger.create_event("compiler_setup");

        let file_source = FileSource::connect(&self.config, &setup_event).await?;
        let (mut compiler_state, mut subscription) = file_source
            .subscribe(&setup_event, self.perf_logger)
            .await?;
        let schemas = self.build_schemas(&compiler_state, &setup_event);
        callback(
            self.check_projects(&mut compiler_state, &schemas, &setup_event)
                .await,
        );

        self.perf_logger.complete_event(setup_event);

        loop {
            if let Some(file_source_changes) = subscription.next_change().await? {
                let incremental_check_event =
                    self.perf_logger.create_event("incremental_check_event");
                let incremental_check_time =
                    incremental_check_event.start("incremental_check_time");

                // TODO Single change to file in VSCode sometimes produces
                // 2 watchman change events for the same file
                let had_new_changes = compiler_state.add_pending_file_source_changes(
                    &self.config,
                    &file_source_changes,
                    &incremental_check_event,
                    self.perf_logger,
                )?;
                if had_new_changes {
                    // Clear out existing errors
                    callback(Ok(()));
                    // Report any new errors
                    callback(
                        self.check_projects(
                            &mut compiler_state,
                            &schemas,
                            &incremental_check_event,
                        )
                        .await,
                    );
                } else {
                    info!("[watch-mode] No re-compilation required");
                }
                incremental_check_event.stop(incremental_check_time);
                self.perf_logger.complete_event(incremental_check_event);
                // We probably don't want the messages queue to grow indefinitely
                // and we need to flush then, as the check/build is completed
                self.perf_logger.flush();
            }
        }
    }

    pub async fn watch(&self) -> Result<()> {
        let setup_event = self.perf_logger.create_event("compiler_setup");

        let file_source = FileSource::connect(&self.config, &setup_event).await?;

        let (mut compiler_state, mut subscription) = file_source
            .subscribe(&setup_event, self.perf_logger)
            .await?;

        if let Err(errors) = self.build_projects(&mut compiler_state, &setup_event).await {
            // TODO correctly print errors
            error!("Errors: {:#?}", errors)
        }
        self.perf_logger.complete_event(setup_event);

        loop {
            if let Some(file_source_changes) = subscription.next_change().await? {
                let incremental_build_event =
                    self.perf_logger.create_event("incremental_build_event");
                let incremental_build_time =
                    incremental_build_event.start("incremental_build_time");

                // TODO Single change to file in VSCode sometimes produces
                // 2 watchman change events for the same file

                info!("\n\n[watch-mode] Change detected");
                let had_new_changes = compiler_state.add_pending_file_source_changes(
                    &self.config,
                    &file_source_changes,
                    &incremental_build_event,
                    self.perf_logger,
                )?;

                if had_new_changes {
                    if let Err(errors) = self
                        .build_projects(&mut compiler_state, &incremental_build_event)
                        .await
                    {
                        // TODO correctly print errors
                        error!("Errors: {:#?}", errors)
                    }
                } else {
                    info!("[watch-mode] No re-compilation required");
                }
                incremental_build_event.stop(incremental_build_time);
                self.perf_logger.complete_event(incremental_build_event);
                // We probably don't want the messages queue to grow indefinitely
                // and we need to flush then, as the check/build is completed
                self.perf_logger.flush();
            }
        }
    }

    async fn check_projects(
        &self,
        compiler_state: &mut CompilerState,
        schemas: &HashMap<ProjectName, Schema>,
        setup_event: &impl PerfLogEvent,
    ) -> Result<()> {
        let graphql_asts =
            setup_event.time("parse_sources_time", || parse_sources(&compiler_state))?;

        let mut build_project_errors = vec![];

        match self.config.only_project {
            Some(project_key) => {
                let project_config =
                    self.config.projects.get(&project_key).unwrap_or_else(|| {
                        panic!("Expected the project {} to exist", &project_key)
                    });
                let schema = schemas.get(&project_config.name).unwrap();
                check_project(
                    project_config,
                    compiler_state,
                    &graphql_asts,
                    schema,
                    self.perf_logger,
                )
                .await
                .map_err(|err| {
                    build_project_errors.push(err);
                })
                .ok();
            }
            None => {
                for project_config in self.config.projects.values() {
                    if compiler_state.project_has_pending_changes(project_config.name) {
                        let schema = schemas.get(&project_config.name).unwrap();
                        // TODO: consider running all projects in parallel
                        check_project(
                            project_config,
                            compiler_state,
                            &graphql_asts,
                            schema,
                            self.perf_logger,
                        )
                        .await
                        .map_err(|err| {
                            build_project_errors.push(err);
                        })
                        .ok();
                    }
                }
            }
        }

        if build_project_errors.is_empty() {
            Ok(())
        } else {
            Err(Error::BuildProjectsErrors {
                errors: build_project_errors,
            })
        }
    }

    async fn build_projects(
        &self,
        compiler_state: &mut CompilerState,
        setup_event: &impl PerfLogEvent,
    ) -> Result<()> {
        let graphql_asts =
            setup_event.time("parse_sources_time", || parse_sources(&compiler_state))?;

        let mut build_project_errors = vec![];
        let mut next_artifacts: HashMap<ProjectName, WrittenArtifacts> = Default::default();

        let mut process_build_result = |result, name| match result {
            Ok(written_artifacts) => {
                next_artifacts.insert(name, written_artifacts);
            }
            Err(err) => build_project_errors.push(err),
        };

        match self.config.only_project {
            Some(project_key) => {
                let project_config =
                    self.config.projects.get(&project_key).unwrap_or_else(|| {
                        panic!("Expected the project {} to exist", &project_key)
                    });
                process_build_result(
                    build_project(
                        &self.config,
                        project_config,
                        compiler_state,
                        &graphql_asts,
                        self.perf_logger,
                    )
                    .await,
                    project_config.name,
                )
            }
            None => {
                for project_config in self.config.projects.values() {
                    if compiler_state.project_has_pending_changes(project_config.name) {
                        // TODO: consider running all projects in parallel
                        process_build_result(
                            build_project(
                                &self.config,
                                project_config,
                                compiler_state,
                                &graphql_asts,
                                self.perf_logger,
                            )
                            .await,
                            project_config.name,
                        )
                    }
                }
            }
        }

        if build_project_errors.is_empty() {
            compiler_state.complete_compilation(next_artifacts);
            Ok(())
        } else {
            Err(Error::BuildProjectsErrors {
                errors: build_project_errors,
            })
        }
    }
}
<commit_msg>small simplification in compiler.rs<commit_after>/*
 * Copyright (c) Facebook, Inc. and its affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

use crate::build_project::{build_project, build_schema, check_project, WrittenArtifacts};
use crate::compiler_state::{CompilerState, ProjectName};
use crate::config::Config;
use crate::errors::{Error, Result};
use crate::parse_sources::parse_sources;
use crate::watchman::FileSource;
use common::{PerfLogEvent, PerfLogger};
use log::{error, info};
use schema::Schema;
use std::collections::HashMap;

pub struct Compiler<'perf, T>
where
    T: PerfLogger,
{
    config: Config,
    perf_logger: &'perf T,
}

impl<'perf, T: PerfLogger> Compiler<'perf, T> {
    pub fn new(config: Config, perf_logger: &'perf T) -> Self {
        Self {
            config,
            perf_logger,
        }
    }

    pub async fn compile(&self) -> Result<CompilerState> {
        let setup_event = self.perf_logger.create_event("compiler_setup");

        let file_source = FileSource::connect(&self.config, &setup_event).await?;
        let mut compiler_state = file_source.query(&setup_event, self.perf_logger).await?;
        self.build_projects(&mut compiler_state, &setup_event)
            .await?;

        self.perf_logger.complete_event(setup_event);

        Ok(compiler_state)
    }

    pub fn build_schemas(
        &self,
        compiler_state: &CompilerState,
        setup_event: &impl PerfLogEvent,
    ) -> HashMap<ProjectName, Schema> {
        let timer = setup_event.start("build_schemas");
        let mut schemas = HashMap::new();
        match self.config.only_project {
            Some(project_key) => {
                let project_config =
                    self.config.projects.get(&project_key).unwrap_or_else(|| {
                        panic!("Expected the project {} to exist", &project_key)
                    });
                let schema = build_schema(compiler_state, project_config);
                schemas.insert(project_config.name, schema);
            }
            None => {
                for project_config in self.config.projects.values() {
                    let schema = build_schema(compiler_state, project_config);
                    schemas.insert(project_config.name, schema);
                }
            }
        }
        setup_event.stop(timer);
        schemas
    }

    pub async fn watch_with_callback<F>(&self, mut callback: F) -> Result<()>
    where
        F: FnMut(Result<()>),
    {
        let setup_event = self.perf_logger.create_event("compiler_setup");

        let file_source = FileSource::connect(&self.config, &setup_event).await?;
        let (mut compiler_state, mut subscription) = file_source
            .subscribe(&setup_event, self.perf_logger)
            .await?;
        let schemas = self.build_schemas(&compiler_state, &setup_event);
        callback(
            self.check_projects(&mut compiler_state, &schemas, &setup_event)
                .await,
        );

        self.perf_logger.complete_event(setup_event);

        loop {
            if let Some(file_source_changes) = subscription.next_change().await? {
                let incremental_check_event =
                    self.perf_logger.create_event("incremental_check_event");
                let incremental_check_time =
                    incremental_check_event.start("incremental_check_time");

                // TODO Single change to file in VSCode sometimes produces
                // 2 watchman change events for the same file
                let had_new_changes = compiler_state.add_pending_file_source_changes(
                    &self.config,
                    &file_source_changes,
                    &incremental_check_event,
                    self.perf_logger,
                )?;
                if had_new_changes {
                    // Clear out existing errors
                    callback(Ok(()));
                    // Report any new errors
                    callback(
                        self.check_projects(
                            &mut compiler_state,
                            &schemas,
                            &incremental_check_event,
                        )
                        .await,
                    );
                } else {
                    info!("[watch-mode] No re-compilation required");
                }
                incremental_check_event.stop(incremental_check_time);
                self.perf_logger.complete_event(incremental_check_event);
                // We probably don't want the messages queue to grow indefinitely
                // and we need to flush then, as the check/build is completed
                self.perf_logger.flush();
            }
        }
    }

    pub async fn watch(&self) -> Result<()> {
        let setup_event = self.perf_logger.create_event("compiler_setup");

        let file_source = FileSource::connect(&self.config, &setup_event).await?;

        let (mut compiler_state, mut subscription) = file_source
            .subscribe(&setup_event, self.perf_logger)
            .await?;

        if let Err(errors) = self.build_projects(&mut compiler_state, &setup_event).await {
            // TODO correctly print errors
            error!("Errors: {:#?}", errors)
        }
        self.perf_logger.complete_event(setup_event);

        loop {
            if let Some(file_source_changes) = subscription.next_change().await? {
                let incremental_build_event =
                    self.perf_logger.create_event("incremental_build_event");
                let incremental_build_time =
                    incremental_build_event.start("incremental_build_time");

                // TODO Single change to file in VSCode sometimes produces
                // 2 watchman change events for the same file

                info!("\n\n[watch-mode] Change detected");
                let had_new_changes = compiler_state.add_pending_file_source_changes(
                    &self.config,
                    &file_source_changes,
                    &incremental_build_event,
                    self.perf_logger,
                )?;

                if had_new_changes {
                    if let Err(errors) = self
                        .build_projects(&mut compiler_state, &incremental_build_event)
                        .await
                    {
                        // TODO correctly print errors
                        error!("Errors: {:#?}", errors)
                    }
                } else {
                    info!("[watch-mode] No re-compilation required");
                }
                incremental_build_event.stop(incremental_build_time);
                self.perf_logger.complete_event(incremental_build_event);
                // We probably don't want the messages queue to grow indefinitely
                // and we need to flush then, as the check/build is completed
                self.perf_logger.flush();
            }
        }
    }

    async fn check_projects(
        &self,
        compiler_state: &mut CompilerState,
        schemas: &HashMap<ProjectName, Schema>,
        setup_event: &impl PerfLogEvent,
    ) -> Result<()> {
        let graphql_asts =
            setup_event.time("parse_sources_time", || parse_sources(&compiler_state))?;

        let mut build_project_errors = vec![];

        match self.config.only_project {
            Some(project_key) => {
                let project_config =
                    self.config.projects.get(&project_key).unwrap_or_else(|| {
                        panic!("Expected the project {} to exist", &project_key)
                    });
                let schema = schemas.get(&project_config.name).unwrap();
                check_project(
                    project_config,
                    compiler_state,
                    &graphql_asts,
                    schema,
                    self.perf_logger,
                )
                .await
                .map_err(|err| {
                    build_project_errors.push(err);
                })
                .ok();
            }
            None => {
                for project_config in self.config.projects.values() {
                    if compiler_state.project_has_pending_changes(project_config.name) {
                        let schema = schemas.get(&project_config.name).unwrap();
                        // TODO: consider running all projects in parallel
                        check_project(
                            project_config,
                            compiler_state,
                            &graphql_asts,
                            schema,
                            self.perf_logger,
                        )
                        .await
                        .map_err(|err| {
                            build_project_errors.push(err);
                        })
                        .ok();
                    }
                }
            }
        }

        if build_project_errors.is_empty() {
            Ok(())
        } else {
            Err(Error::BuildProjectsErrors {
                errors: build_project_errors,
            })
        }
    }

    async fn build_projects(
        &self,
        compiler_state: &mut CompilerState,
        setup_event: &impl PerfLogEvent,
    ) -> Result<()> {
        let graphql_asts =
            setup_event.time("parse_sources_time", || parse_sources(&compiler_state))?;

        let mut build_project_errors = vec![];
        let mut next_artifacts: HashMap<ProjectName, WrittenArtifacts> = Default::default();

        let mut process_build_result = |result, name| match result {
            Ok(written_artifacts) => {
                next_artifacts.insert(name, written_artifacts);
            }
            Err(err) => build_project_errors.push(err),
        };

        if let Some(only_project) = self.config.only_project {
            let project_config = self
                .config
                .projects
                .get(&only_project)
                .unwrap_or_else(|| panic!("Expected the project {} to exist", &only_project));
            process_build_result(
                build_project(
                    &self.config,
                    project_config,
                    compiler_state,
                    &graphql_asts,
                    self.perf_logger,
                )
                .await,
                project_config.name,
            )
        } else {
            for project_config in self.config.projects.values() {
                if compiler_state.project_has_pending_changes(project_config.name) {
                    // TODO: consider running all projects in parallel
                    process_build_result(
                        build_project(
                            &self.config,
                            project_config,
                            compiler_state,
                            &graphql_asts,
                            self.perf_logger,
                        )
                        .await,
                        project_config.name,
                    )
                }
            }
        }

        if build_project_errors.is_empty() {
            compiler_state.complete_compilation(next_artifacts);
            Ok(())
        } else {
            Err(Error::BuildProjectsErrors {
                errors: build_project_errors,
            })
        }
    }
}
<|endoftext|>