<commit_before>use std::env;
use std::default::Default;
use std::collections::BTreeMap;
use std::path::{Path, PathBuf};

use quire::parse_config;
use quire::validate as V;

use config::Settings;
use path_util::{PathExt, Expand};

#[derive(PartialEq, RustcDecodable, Debug)]
struct SecureSettings {
    allowed_dirs: BTreeMap<String, PathBuf>,
    storage_dir: Option<PathBuf>,
    cache_dir: Option<PathBuf>,
    version_check: Option<bool>,
    proxy_env_vars: Option<bool>,
    ubuntu_mirror: Option<String>,
    alpine_mirror: Option<String>,
    site_settings: BTreeMap<PathBuf, SecureSettings>,
}

pub fn secure_settings_validator<'a>(has_children: bool)
    -> V::Structure<'a>
{
    let mut s = V::Structure::new()
        .member("allowed_dirs", V::Mapping::new(
            V::Scalar::new(),
            V::Scalar::new()))
        .member("storage_dir", V::Scalar::new().optional())
        .member("cache_dir", V::Scalar::new().optional())
        .member("version_check", V::Scalar::new().optional())
        .member("proxy_env_vars", V::Scalar::new().optional())
        .member("ubuntu_mirror", V::Scalar::new().optional())
        .member("alpine_mirror", V::Scalar::new().optional());
    if has_children {
        s = s.member("site_settings", V::Mapping::new(
            V::Scalar::new(),
            secure_settings_validator(false)));
    }
    return s;
}

#[derive(PartialEq, RustcDecodable)]
struct InsecureSettings {
    version_check: Option<bool>,
    shared_cache: Option<bool>,
    ubuntu_mirror: Option<String>,
    alpine_mirror: Option<String>,
}

pub fn insecure_settings_validator<'a>() -> Box<V::Validator + 'a> {
    return Box::new(V::Structure { members: vec!(
        ("version_check".to_string(), Box::new(V::Scalar {
            optional: true,
            .. Default::default()}) as Box<V::Validator + 'a>),
        ("shared_cache".to_string(), Box::new(V::Scalar {
            optional: true,
            .. Default::default()}) as Box<V::Validator + 'a>),
        ("ubuntu_mirror".to_string(), Box::new(V::Scalar {
            optional: true,
            .. Default::default()}) as Box<V::Validator>),
        ("alpine_mirror".to_string(), Box::new(V::Scalar {
            optional: true,
            .. Default::default()}) as Box<V::Validator>),
    ), .. Default::default()}) as Box<V::Validator>;
}

#[derive(Debug)]
pub struct MergedSettings {
    pub allowed_dirs: BTreeMap<String, PathBuf>,
    pub allowed_files: BTreeMap<String, PathBuf>,
    pub storage_dir: Option<PathBuf>,
    pub cache_dir: Option<PathBuf>,
    pub shared_cache: bool,
}

pub fn read_settings(project_root: &Path)
    -> Result<(MergedSettings, Settings), String>
{
    let mut ext_settings = MergedSettings {
        allowed_dirs: BTreeMap::new(),
        allowed_files: BTreeMap::new(),
        storage_dir: None,
        cache_dir: None,
        shared_cache: false,
    };
    let mut int_settings = Settings {
        proxy_env_vars: true,
        version_check: true,
        uid_map: None,
        ubuntu_mirror: "mirror://mirrors.ubuntu.com/mirrors.txt".to_string(),
        alpine_mirror: None,
    };
    let mut secure_files = vec!();
    if let Ok(home) = env::var("VAGGA_USER_HOME") {
        let home = Path::new(&home);
        secure_files.push(home.join(".config/vagga/settings.yaml"));
        secure_files.push(home.join(".vagga/settings.yaml"));
        secure_files.push(home.join(".vagga.yaml"));
    };
    for filename in secure_files.iter() {
        if !filename.exists() {
            continue;
        }
        let cfg: SecureSettings = try!(parse_config(filename,
            &secure_settings_validator(true), Default::default()));
        for (k, v) in cfg.allowed_dirs.iter() {
            ext_settings.allowed_dirs.insert(k.clone(), v.clone());
        }
        if let Some(dir) = cfg.storage_dir {
            ext_settings.storage_dir = Some(try!(dir.expand_home()
                .map_err(|()| format!("Can't expand tilde `~` in storage dir \
                    no HOME found"))));
        }
        if let Some(dir) = cfg.cache_dir {
            ext_settings.cache_dir = Some(try!(dir.expand_home()
                .map_err(|()| format!("Can't expand tilde `~` in cache dir \
                    no HOME found"))));
            ext_settings.shared_cache = true;
        }
        if let Some(val) = cfg.version_check {
            int_settings.version_check = val;
        }
        if let Some(val) = cfg.proxy_env_vars {
            int_settings.proxy_env_vars = val;
        }
        if let Some(ref val) = cfg.ubuntu_mirror {
            int_settings.ubuntu_mirror = val.clone();
        }
        if let Some(ref val) = cfg.alpine_mirror {
            int_settings.alpine_mirror = Some(val.clone());
        }
        if let Some(cfg) = cfg.site_settings.get(project_root) {
            for (k, v) in cfg.allowed_dirs.iter() {
                ext_settings.allowed_dirs.insert(k.clone(), v.clone());
            }
            if let Some(ref dir) = cfg.storage_dir {
                ext_settings.storage_dir = Some(dir.clone());
            }
            if let Some(ref dir) = cfg.cache_dir {
                ext_settings.cache_dir = Some(dir.clone());
                ext_settings.shared_cache = true;
            }
            if let Some(val) = cfg.version_check {
                int_settings.version_check = val;
            }
            if let Some(ref val) = cfg.ubuntu_mirror {
                int_settings.ubuntu_mirror = val.clone();
            }
            if let Some(ref val) = cfg.alpine_mirror {
                int_settings.alpine_mirror = Some(val.clone());
            }
        }
    }
    let mut insecure_files = vec!();
    insecure_files.push(project_root.join(".vagga.settings.yaml"));
    insecure_files.push(project_root.join(".vagga/settings.yaml"));
    for filename in insecure_files.iter() {
        if !filename.exists() {
            continue;
        }
        let cfg: InsecureSettings = try!(parse_config(filename,
            &*insecure_settings_validator(), Default::default()));
        if let Some(val) = cfg.version_check {
            int_settings.version_check = val;
        }
        if let Some(ref val) = cfg.ubuntu_mirror {
            int_settings.ubuntu_mirror = val.clone();
        }
        if let Some(ref val) = cfg.alpine_mirror {
            int_settings.alpine_mirror = Some(val.clone());
        }
        if let Some(val) = cfg.shared_cache {
            ext_settings.shared_cache = val;
        }
    }
    return Ok((ext_settings, int_settings));
}
<commit_msg>Remove never used `allowed-dirs` setting<commit_after>use std::env;
use std::default::Default;
use std::collections::BTreeMap;
use std::path::{Path, PathBuf};

use quire::parse_config;
use quire::validate as V;

use config::Settings;
use path_util::{PathExt, Expand};

#[derive(PartialEq, RustcDecodable, Debug)]
struct SecureSettings {
    storage_dir: Option<PathBuf>,
    cache_dir: Option<PathBuf>,
    version_check: Option<bool>,
    proxy_env_vars: Option<bool>,
    ubuntu_mirror: Option<String>,
    alpine_mirror: Option<String>,
    site_settings: BTreeMap<PathBuf, SecureSettings>,
}

pub fn secure_settings_validator<'a>(has_children: bool)
    -> V::Structure<'a>
{
    let mut s = V::Structure::new()
        .member("storage_dir", V::Scalar::new().optional())
        .member("cache_dir", V::Scalar::new().optional())
        .member("version_check", V::Scalar::new().optional())
        .member("proxy_env_vars", V::Scalar::new().optional())
        .member("ubuntu_mirror", V::Scalar::new().optional())
        .member("alpine_mirror", V::Scalar::new().optional());
    if has_children {
        s = s.member("site_settings", V::Mapping::new(
            V::Scalar::new(),
            secure_settings_validator(false)));
    }
    return s;
}

#[derive(PartialEq, RustcDecodable)]
struct InsecureSettings {
    version_check: Option<bool>,
    shared_cache: Option<bool>,
    ubuntu_mirror: Option<String>,
    alpine_mirror: Option<String>,
}

pub fn insecure_settings_validator<'a>() -> Box<V::Validator + 'a> {
    return Box::new(V::Structure { members: vec!(
        ("version_check".to_string(), Box::new(V::Scalar {
            optional: true,
            .. Default::default()}) as Box<V::Validator + 'a>),
        ("shared_cache".to_string(), Box::new(V::Scalar {
            optional: true,
            .. Default::default()}) as Box<V::Validator + 'a>),
        ("ubuntu_mirror".to_string(), Box::new(V::Scalar {
            optional: true,
            .. Default::default()}) as Box<V::Validator>),
        ("alpine_mirror".to_string(), Box::new(V::Scalar {
            optional: true,
            .. Default::default()}) as Box<V::Validator>),
    ), .. Default::default()}) as Box<V::Validator>;
}

#[derive(Debug)]
pub struct MergedSettings {
    pub allowed_files: BTreeMap<String, PathBuf>,
    pub storage_dir: Option<PathBuf>,
    pub cache_dir: Option<PathBuf>,
    pub shared_cache: bool,
}

pub fn read_settings(project_root: &Path)
    -> Result<(MergedSettings, Settings), String>
{
    let mut ext_settings = MergedSettings {
        allowed_files: BTreeMap::new(),
        storage_dir: None,
        cache_dir: None,
        shared_cache: false,
    };
    let mut int_settings = Settings {
        proxy_env_vars: true,
        version_check: true,
        uid_map: None,
        ubuntu_mirror: "mirror://mirrors.ubuntu.com/mirrors.txt".to_string(),
        alpine_mirror: None,
    };
    let mut secure_files = vec!();
    if let Ok(home) = env::var("VAGGA_USER_HOME") {
        let home = Path::new(&home);
        secure_files.push(home.join(".config/vagga/settings.yaml"));
        secure_files.push(home.join(".vagga/settings.yaml"));
        secure_files.push(home.join(".vagga.yaml"));
    };
    for filename in secure_files.iter() {
        if !filename.exists() {
            continue;
        }
        let cfg: SecureSettings = try!(parse_config(filename,
            &secure_settings_validator(true), Default::default()));
        if let Some(dir) = cfg.storage_dir {
            ext_settings.storage_dir = Some(try!(dir.expand_home()
                .map_err(|()| format!("Can't expand tilde `~` in storage dir \
                    no HOME found"))));
        }
        if let Some(dir) = cfg.cache_dir {
            ext_settings.cache_dir = Some(try!(dir.expand_home()
                .map_err(|()| format!("Can't expand tilde `~` in cache dir \
                    no HOME found"))));
            ext_settings.shared_cache = true;
        }
        if let Some(val) = cfg.version_check {
            int_settings.version_check = val;
        }
        if let Some(val) = cfg.proxy_env_vars {
            int_settings.proxy_env_vars = val;
        }
        if let Some(ref val) = cfg.ubuntu_mirror {
            int_settings.ubuntu_mirror = val.clone();
        }
        if let Some(ref val) = cfg.alpine_mirror {
            int_settings.alpine_mirror = Some(val.clone());
        }
        if let Some(cfg) = cfg.site_settings.get(project_root) {
            if let Some(ref dir) = cfg.storage_dir {
                ext_settings.storage_dir = Some(dir.clone());
            }
            if let Some(ref dir) = cfg.cache_dir {
                ext_settings.cache_dir = Some(dir.clone());
                ext_settings.shared_cache = true;
            }
            if let Some(val) = cfg.version_check {
                int_settings.version_check = val;
            }
            if let Some(ref val) = cfg.ubuntu_mirror {
                int_settings.ubuntu_mirror = val.clone();
            }
            if let Some(ref val) = cfg.alpine_mirror {
                int_settings.alpine_mirror = Some(val.clone());
            }
        }
    }
    let mut insecure_files = vec!();
    insecure_files.push(project_root.join(".vagga.settings.yaml"));
    insecure_files.push(project_root.join(".vagga/settings.yaml"));
    for filename in insecure_files.iter() {
        if !filename.exists() {
            continue;
        }
        let cfg: InsecureSettings = try!(parse_config(filename,
            &*insecure_settings_validator(), Default::default()));
        if let Some(val) = cfg.version_check {
            int_settings.version_check = val;
        }
        if let Some(ref val) = cfg.ubuntu_mirror {
            int_settings.ubuntu_mirror = val.clone();
        }
        if let Some(ref val) = cfg.alpine_mirror {
            int_settings.alpine_mirror = Some(val.clone());
        }
        if let Some(val) = cfg.shared_cache {
            ext_settings.shared_cache = val;
        }
    }
    return Ok((ext_settings, int_settings));
}
<|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 std::{
    collections::HashSet,
    sync::{Arc, RwLock},
};

use super::resolve_completion_items;
use common::{SourceLocationKey, Span};
use graphql_syntax::parse_executable_with_error_recovery;
use interner::Intern;
use lsp_types::CompletionItem;
use relay_test_schema::get_test_schema;

fn parse_and_resolve_completion_items(source: &str) -> Option<Vec<CompletionItem>> {
    let pos = source.find('|').unwrap() - 1;
    let next_source = source.replace("|", "");
    let document = parse_executable_with_error_recovery(
        &next_source,
        SourceLocationKey::Standalone {
            path: "/test/file".intern(),
        },
    )
    .item;

    let position_span = Span {
        start: pos as u32,
        end: pos as u32,
    };

    let source_programs = Arc::new(RwLock::new(Default::default()));
    resolve_completion_items(
        document,
        position_span,
        "test_project".intern(),
        &get_test_schema(),
        &source_programs,
    )
}

fn assert_labels(items: Vec<CompletionItem>, labels: Vec<&str>) {
    assert_eq!(items.len(), labels.len());
    let mut current_labels = items
        .into_iter()
        .map(|item| item.label)
        .collect::<HashSet<_>>();
    for label in labels {
        assert!(current_labels.remove(label));
    }
    assert!(current_labels.is_empty());
}

#[test]
fn scalar_field() {
    let items = parse_and_resolve_completion_items(
        r#"
            fragment Test on User {
                profile_picture {
                    uri|
                }
            }
        "#,
    );
    assert_labels(items.unwrap(), vec!["uri", "width", "height", "test_enums"]);
}

#[test]
fn linked_field() {
    let items = parse_and_resolve_completion_items(
        r#"
            fragment Test on Viewer {
                marketplace_settings {
                    locatio| {
                    }
                }
            }
        "#,
    );
    assert_labels(items.unwrap(), vec!["location", "categories"]);
}

#[test]
fn whitespace_in_linked_field() {
    let items = parse_and_resolve_completion_items(
        r#"
            fragment Test on User {
                profile_picture {
                    uri
                    |
                }
            }
        "#,
    );
    assert_labels(items.unwrap(), vec!["uri", "width", "height", "test_enums"]);
}

#[test]
fn whitespace_in_fragment() {
    let items = parse_and_resolve_completion_items(
        r#"
            fragment Test on Image {
                |
            }
        "#,
    );
    assert_labels(items.unwrap(), vec!["uri", "width", "height", "test_enums"]);
}

#[test]
fn whitespace_in_inline_fragment() {
    let items = parse_and_resolve_completion_items(
        r#"
            fragment Test on Node {
                ... on Image {
                    |
                }
            }
        "#,
    );
    assert_labels(items.unwrap(), vec!["uri", "width", "height", "test_enums"]);
}

#[test]
fn inline_fragment() {
    let items = parse_and_resolve_completion_items(
        r#"
            fragment Test on Viewer {
                ... on a|
            }
        "#,
    );
    assert_labels(items.unwrap(), vec!["Viewer"]);
}

#[test]
fn directive() {
    let items = parse_and_resolve_completion_items(
        r#"
        fragment Test on User {
            profile_picture @a| {
                uri
            }
        }
        "#,
    );
    assert_labels(
        items.unwrap(),
        vec![
            "prependEdge",
            "deleteRecord",
            "appendNode",
            "deleteEdge",
            "__clientField",
            "appendEdge",
            "required",
            "stream_connection",
            "match",
            "customDirective",
            "prependNode",
            "fixme_fat_interface",
            "stream",
            "include",
            "connection",
            "skip",
        ],
    );
}

#[test]
fn empty_argument_list() {
    let items = parse_and_resolve_completion_items(
        r#"
            fragment Test on Viewer {
                profile_picture @stream(|) {
                    uri
                }
            }
        "#,
    );
    assert_labels(
        items.unwrap(),
        vec!["label", "initial_count", "if", "use_customized_batch"],
    );
}

#[test]
fn argument_name_without_value() {
    let items = parse_and_resolve_completion_items(
        r#"
            fragment Test on Viewer {
                profile_picture @stream(i|) {
                    uri
                }
            }
        "#,
    );
    assert_labels(
        items.unwrap(),
        vec!["label", "initial_count", "if", "use_customized_batch"],
    );
}

#[test]
fn argument_name_with_existing_name() {
    let items = parse_and_resolve_completion_items(
        r#"
            fragment Test on Viewer {
                profile_picture @stream(
                    if: false
                    |
                ) {
                    uri
                }
            }
        "#,
    );
    assert_labels(
        items.unwrap(),
        vec!["label", "initial_count", "use_customized_batch"],
    );
}
<commit_msg>Add auto complete unit tests for arguemnt values and fragment srpead<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 std::{
    collections::{HashMap, HashSet},
    sync::{Arc, RwLock},
};

use super::resolve_completion_items;
use common::{SourceLocationKey, Span};
use graphql_ir::{build, Program};
use graphql_syntax::{parse_executable, parse_executable_with_error_recovery};
use interner::{Intern, StringKey};
use lsp_types::CompletionItem;
use relay_test_schema::get_test_schema;

fn parse_and_resolve_completion_items(
    source: &str,
    source_programs: Option<Arc<RwLock<HashMap<StringKey, Program>>>>,
) -> Option<Vec<CompletionItem>> {
    let pos = source.find('|').unwrap() - 1;
    let next_source = source.replace("|", "");
    let document = parse_executable_with_error_recovery(
        &next_source,
        SourceLocationKey::Standalone {
            path: "/test/file".intern(),
        },
    )
    .item;

    let position_span = Span {
        start: pos as u32,
        end: pos as u32,
    };

    resolve_completion_items(
        document,
        position_span,
        "test_project".intern(),
        &get_test_schema(),
        &source_programs.unwrap_or_else(Default::default),
    )
}

fn build_source_programs(source: &str) -> Arc<RwLock<HashMap<StringKey, Program>>> {
    let document = parse_executable(source, SourceLocationKey::Generated).unwrap();
    let ir = build(&get_test_schema(), &document.definitions).unwrap();
    let program = Program::from_definitions(get_test_schema(), ir);
    let mut source_programs_map = HashMap::new();
    source_programs_map.insert("test_project".intern(), program);
    Arc::new(RwLock::new(source_programs_map))
}

fn assert_labels(items: Vec<CompletionItem>, labels: Vec<&str>) {
    assert_eq!(items.len(), labels.len());
    let mut current_labels = items
        .into_iter()
        .map(|item| item.label)
        .collect::<HashSet<_>>();
    for label in labels {
        assert!(current_labels.remove(label));
    }
    assert!(current_labels.is_empty());
}

#[test]
fn scalar_field() {
    let items = parse_and_resolve_completion_items(
        r#"
            fragment Test on User {
                profile_picture {
                    uri|
                }
            }
        "#,
        None,
    );
    assert_labels(items.unwrap(), vec!["uri", "width", "height", "test_enums"]);
}

#[test]
fn linked_field() {
    let items = parse_and_resolve_completion_items(
        r#"
            fragment Test on Viewer {
                marketplace_settings {
                    locatio| {
                    }
                }
            }
        "#,
        None,
    );
    assert_labels(items.unwrap(), vec!["location", "categories"]);
}

#[test]
fn whitespace_in_linked_field() {
    let items = parse_and_resolve_completion_items(
        r#"
            fragment Test on User {
                profile_picture {
                    uri
                    |
                }
            }
        "#,
        None,
    );
    assert_labels(items.unwrap(), vec!["uri", "width", "height", "test_enums"]);
}

#[test]
fn whitespace_in_fragment() {
    let items = parse_and_resolve_completion_items(
        r#"
            fragment Test on Image {
                |
            }
        "#,
        None,
    );
    assert_labels(items.unwrap(), vec!["uri", "width", "height", "test_enums"]);
}

#[test]
fn whitespace_in_inline_fragment() {
    let items = parse_and_resolve_completion_items(
        r#"
            fragment Test on Node {
                ... on Image {
                    |
                }
            }
        "#,
        None,
    );
    assert_labels(items.unwrap(), vec!["uri", "width", "height", "test_enums"]);
}

#[test]
fn inline_fragment() {
    let items = parse_and_resolve_completion_items(
        r#"
            fragment Test on Viewer {
                ... on a|
            }
        "#,
        None,
    );
    assert_labels(items.unwrap(), vec!["Viewer"]);
}

#[test]
fn directive() {
    let items = parse_and_resolve_completion_items(
        r#"
        fragment Test on User {
            profile_picture @a| {
                uri
            }
        }
        "#,
        None,
    );
    assert_labels(
        items.unwrap(),
        vec![
            "prependEdge",
            "deleteRecord",
            "appendNode",
            "deleteEdge",
            "__clientField",
            "appendEdge",
            "required",
            "stream_connection",
            "match",
            "customDirective",
            "prependNode",
            "fixme_fat_interface",
            "stream",
            "include",
            "connection",
            "skip",
        ],
    );
}

#[test]
fn empty_argument_list() {
    let items = parse_and_resolve_completion_items(
        r#"
            fragment Test on Viewer {
                profile_picture @stream(|) {
                    uri
                }
            }
        "#,
        None,
    );
    assert_labels(
        items.unwrap(),
        vec!["label", "initial_count", "if", "use_customized_batch"],
    );
}

#[test]
fn argument_name_without_value() {
    let items = parse_and_resolve_completion_items(
        r#"
            fragment Test on Viewer {
                profile_picture @stream(i|) {
                    uri
                }
            }
        "#,
        None,
    );
    assert_labels(
        items.unwrap(),
        vec!["label", "initial_count", "if", "use_customized_batch"],
    );
}

#[test]
fn argument_name_with_existing_name() {
    let items = parse_and_resolve_completion_items(
        r#"
            fragment Test on Viewer {
                profile_picture @stream(
                    if: false
                    |
                ) {
                    uri
                }
            }
        "#,
        None,
    );
    assert_labels(
        items.unwrap(),
        vec!["label", "initial_count", "use_customized_batch"],
    );
}

#[test]
fn fragment_spread_on_the_same_type() {
    let items = parse_and_resolve_completion_items(
        r#"
            fragment Test on Viewer {
               ...T|
            }
        "#,
        Some(build_source_programs(
            r#"
        fragment TestFragment on Viewer {
           __typename
        }

        fragment TestFragment2 on Viewer {
            __typename
         }
    "#,
        )),
    );
    assert_labels(items.unwrap(), vec!["TestFragment", "TestFragment2"]);
}

#[test]
fn fragment_spread_on_interface() {
    let items = parse_and_resolve_completion_items(
        r#"
            fragment Test on Actor {
               ...T|
            }
        "#,
        Some(build_source_programs(
            r#"
        fragment TestFragment on Page {
           __typename
        }

        fragment TestFragment2 on Node {
            __typename
        }
    "#,
        )),
    );
    assert_labels(items.unwrap(), vec!["TestFragment", "TestFragment2"]);
}

#[test]
fn argument_value() {
    let items = parse_and_resolve_completion_items(
        r#"
            fragment Test on User {
                profilePicture(size:|) {
                    uri
                }
            }
        "#,
        Some(build_source_programs(
            r#"
            fragment Test on User
                @argumentDefinitions(
                    pictureSize: {type: "[Int]"},
                    pictureSize2: {type: "[Int]"}
                ) {
                profilePicture(size: $pictureSize) {
                    uri
                }
            }
        "#,
        )),
    );
    assert_labels(items.unwrap(), vec!["$pictureSize", "$pictureSize2"]);
}

#[test]
fn argument_value_between_names() {
    let items = parse_and_resolve_completion_items(
        r#"
            fragment Test on User {
                profilePicture(size: |, preset: SMALL) {
                    uri
                }
            }
        "#,
        Some(build_source_programs(
            r#"
            fragment Test on User
                @argumentDefinitions(
                    pictureSize: {type: "[Int]"},
                    pictureSize2: {type: "[Int]"}
                ) {
                profilePicture(size: $pictureSize) {
                    uri
                }
            }
        "#,
        )),
    );
    assert_labels(items.unwrap(), vec!["$pictureSize", "$pictureSize2"]);
}
<|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.
 */

//! Utilities for providing the goto definition feature

use crate::{
    location::{read_graphql_file_and_get_range, to_lsp_location_of_graphql_literal},
    lsp_runtime_error::{LSPRuntimeError, LSPRuntimeResult},
    resolution_path::{
        IdentParent, IdentPath, LinkedFieldPath, ResolutionPath, ResolvePosition, ScalarFieldPath,
        SelectionParent, TypeConditionPath,
    },
    server::GlobalState,
    FieldDefinitionSourceInfo, FieldSchemaInfo, LSPExtraDataProvider,
};
use common::NamedItem;

use graphql_ir::Program;
use interner::{Intern, StringKey};
use lsp_types::{
    request::{GotoDefinition, Request},
    GotoDefinitionResponse, Url,
};
use relay_transforms::{RELAY_RESOLVER_DIRECTIVE_NAME, RELAY_RESOLVER_IMPORT_PATH_ARGUMENT_NAME};
use schema::Schema;
use serde::{Deserialize, Serialize};
use std::{
    path::{Path, PathBuf},
    str,
};

fn get_goto_definition_response<'a>(
    node_path: ResolutionPath<'a>,
    project_name: StringKey,
    program: &Program,
    root_dir: &Path,
    extra_data_provider: &dyn LSPExtraDataProvider,
) -> LSPRuntimeResult<GotoDefinitionResponse> {
    match node_path {
        ResolutionPath::Ident(IdentPath {
            inner: fragment_name,
            parent: IdentParent::FragmentSpreadName(_),
        }) => {
            let fragment = program.fragment(fragment_name.value).ok_or_else(|| {
                LSPRuntimeError::UnexpectedError(format!(
                    "Could not find fragment with name {}",
                    fragment_name
                ))
            })?;

            Ok(GotoDefinitionResponse::Scalar(
                to_lsp_location_of_graphql_literal(fragment.name.location, root_dir)?,
            ))
        }
        ResolutionPath::Ident(IdentPath {
            inner: field_name,
            parent:
                IdentParent::LinkedFieldName(LinkedFieldPath {
                    inner: _,
                    parent: selection_path,
                }),
        }) => resolve_field(
            field_name.value.to_string(),
            selection_path.parent,
            project_name,
            program,
            root_dir,
            extra_data_provider,
        ),
        ResolutionPath::Ident(IdentPath {
            inner: field_name,
            parent:
                IdentParent::ScalarFieldName(ScalarFieldPath {
                    inner: _,
                    parent: selection_path,
                }),
        }) => resolve_field(
            field_name.value.to_string(),
            selection_path.parent,
            project_name,
            program,
            root_dir,
            extra_data_provider,
        ),
        ResolutionPath::Ident(IdentPath {
            inner: _,
            parent:
                IdentParent::TypeConditionType(TypeConditionPath {
                    inner: type_condition,
                    parent: _,
                }),
        }) => {
            let provider_response = extra_data_provider.resolve_field_definition(
                project_name.to_string(),
                type_condition.type_.value.to_string(),
                None,
            );
            let FieldDefinitionSourceInfo {
                file_path,
                line_number,
                is_local,
            } = get_field_definition_source_info_result(provider_response)?;
            if is_local {
                Ok(GotoDefinitionResponse::Scalar(get_location(
                    &file_path,
                    line_number,
                )?))
            } else {
                Err(LSPRuntimeError::ExpectedError)
            }
        }
        _ => Err(LSPRuntimeError::ExpectedError),
    }
}

fn resolve_field<'a>(
    field_name: String,
    selection_parent: SelectionParent<'a>,
    project_name: StringKey,
    program: &Program,
    root_dir: &Path,
    extra_data_provider: &dyn LSPExtraDataProvider,
) -> LSPRuntimeResult<GotoDefinitionResponse> {
    let parent_type = selection_parent
        .find_parent_type(&program.schema)
        .ok_or(LSPRuntimeError::ExpectedError)?;

    let field_name_key = (&field_name as &str).intern();

    let field = program.schema.field(
        program
            .schema
            .named_field(parent_type, field_name_key)
            .ok_or_else(|| {
                LSPRuntimeError::UnexpectedError(format!(
                    "Could not find field with name {}",
                    field_name,
                ))
            })?,
    );

    // Step 1: is this field a resolver?
    if let Some(response) = get_relay_resolver_location(field, root_dir)? {
        return Ok(response);
    }

    let parent_name = program.schema.get_type_name(parent_type);
    let provider_response = extra_data_provider.resolve_field_definition(
        project_name.to_string(),
        parent_name.to_string(),
        Some(FieldSchemaInfo {
            name: field.name.item.to_string(),
            is_extension: field.is_extension,
        }),
    );

    if let Ok(Some(source_info)) = provider_response {
        // Step 2: does extra_data_provider know anything about this field?
        if source_info.is_local {
            Ok(GotoDefinitionResponse::Scalar(get_location(
                &source_info.file_path,
                source_info.line_number,
            )?))
        } else {
            Err(LSPRuntimeError::ExpectedError)
        }
    } else if let Ok((_, location)) = read_graphql_file_and_get_range(field.name.location, root_dir)
    {
        // Step 3: is field a standalone graphql file?
        Ok(GotoDefinitionResponse::Scalar(location))
    } else {
        // Give up
        Err(LSPRuntimeError::ExpectedError)
    }
}

fn get_relay_resolver_location(
    field: &schema::Field,
    root_dir: &Path,
) -> LSPRuntimeResult<Option<GotoDefinitionResponse>> {
    let maybe_resolver_directive = field.directives.named(*RELAY_RESOLVER_DIRECTIVE_NAME);

    Ok(if let Some(resolver_directive) = maybe_resolver_directive {
        let resolver_path = resolver_directive
            .arguments
            .named(*RELAY_RESOLVER_IMPORT_PATH_ARGUMENT_NAME)
            .and_then(|argument| argument.value.get_string_literal())
            .ok_or_else(|| {
                LSPRuntimeError::UnexpectedError(
                "Error resolving field definition location. Malformed schema for Relay Resolver."
                    .to_string(),
            )
            })?;

        let absolute_resolver_path = root_dir.join(PathBuf::from(resolver_path.lookup()));
        Some(GotoDefinitionResponse::Scalar(get_location(
            &absolute_resolver_path.to_string_lossy(),
            0,
        )?))
    } else {
        None
    })
}

pub(crate) fn on_goto_definition(
    state: &impl GlobalState,
    params: <GotoDefinition as Request>::Params,
) -> LSPRuntimeResult<<GotoDefinition as Request>::Result> {
    let (document, position_span) =
        state.extract_executable_document_from_text(&params.text_document_position_params, 1)?;
    let path = document.resolve((), position_span);
    let project_name = state
        .extract_project_name_from_url(&params.text_document_position_params.text_document.uri)?;

    let goto_definition_response = get_goto_definition_response(
        path,
        project_name,
        &state.get_program(&project_name)?,
        &state.root_dir(),
        &*state.get_extra_data_provider(),
    )?;

    Ok(Some(goto_definition_response))
}

fn get_location(path: &str, line: u64) -> Result<lsp_types::Location, LSPRuntimeError> {
    let start = lsp_types::Position {
        line: line as u32,
        character: 0,
    };
    let range = lsp_types::Range { start, end: start };

    let uri = Url::parse(&format!("file://{}", path)).map_err(|e| {
        LSPRuntimeError::UnexpectedError(format!("Could not parse path as URL: {}", e))
    })?;

    Ok(lsp_types::Location { uri, range })
}

pub(crate) enum GetSourceLocationOfTypeDefinition {}

#[derive(Deserialize, Serialize)]
pub(crate) struct GetSourceLocationOfTypeDefinitionParams {
    type_name: String,
    field_name: Option<String>,
    schema_name: String,
}

#[derive(Deserialize, Serialize)]
pub(crate) struct GetSourceLocationOfTypeDefinitionResult {
    field_definition_source_info: FieldDefinitionSourceInfo,
}

impl Request for GetSourceLocationOfTypeDefinition {
    type Params = GetSourceLocationOfTypeDefinitionParams;
    type Result = GetSourceLocationOfTypeDefinitionResult;
    const METHOD: &'static str = "relay/getSourceLocationOfTypeDefinition";
}

pub(crate) fn on_get_source_location_of_type_definition(
    state: &impl GlobalState,
    params: <GetSourceLocationOfTypeDefinition as Request>::Params,
) -> LSPRuntimeResult<<GetSourceLocationOfTypeDefinition as Request>::Result> {
    let schema = state.get_schema(&(&params.schema_name as &str).intern())?;

    let type_ = schema
        .get_type((&params.type_name as &str).intern())
        .ok_or_else(|| {
            LSPRuntimeError::UnexpectedError(format!(
                "Could not find type with name {}",
                &params.type_name
            ))
        })?;

    let field_info = params
        .field_name
        .map(|field_name| {
            schema
                .named_field(type_, (&field_name as &str).intern())
                .ok_or_else(|| {
                    LSPRuntimeError::UnexpectedError(format!(
                        "Could not find field with name {}",
                        field_name
                    ))
                })
        })
        .transpose()?
        .map(|field_id| {
            let field = schema.field(field_id);
            FieldSchemaInfo {
                name: field.name.item.to_string(),
                is_extension: field.is_extension,
            }
        });

    // TODO add go-to-definition for client fields
    let field_definition_source_info = get_field_definition_source_info_result(
        state.get_extra_data_provider().resolve_field_definition(
            params.schema_name,
            params.type_name,
            field_info,
        ),
    )?;

    Ok(GetSourceLocationOfTypeDefinitionResult {
        field_definition_source_info,
    })
}

fn get_field_definition_source_info_result(
    result: Result<Option<FieldDefinitionSourceInfo>, String>,
) -> LSPRuntimeResult<FieldDefinitionSourceInfo> {
    result
        .map_err(LSPRuntimeError::UnexpectedError)?
        .ok_or_else(|| {
            LSPRuntimeError::UnexpectedError(
                "Expected result when resolving field definition location".to_string(),
            )
        })
}

#[cfg(test)]
mod test {
    use std::path::PathBuf;

    use common::{SourceLocationKey, Span};
    use graphql_ir::Program;
    use graphql_syntax::{parse_executable_with_features, ParserFeatures};
    use interner::Intern;
    use lsp_types::{GotoDefinitionResponse, Location};
    use relay_test_schema::get_test_schema_with_extensions;

    use crate::{
        resolution_path::ResolvePosition, FieldDefinitionSourceInfo, FieldSchemaInfo,
        LSPExtraDataProvider,
    };

    use super::get_goto_definition_response;

    struct DummyExtraDataProvider {}

    impl LSPExtraDataProvider for DummyExtraDataProvider {
        fn fetch_query_stats(&self, _search_token: &str) -> Vec<String> {
            Vec::new()
        }

        fn resolve_field_definition(
            &self,
            _project_name: String,
            _parent_type: String,
            _field_info: Option<FieldSchemaInfo>,
        ) -> Result<Option<FieldDefinitionSourceInfo>, String> {
            Err("Not implemented".to_string())
        }
    }

    #[test]
    fn resolver_field() {
        let source = r#"
    query Foo {
        me {
            greeting
        }
    }
"#;
        let sub_str = "greeting";
        let document = parse_executable_with_features(
            source,
            SourceLocationKey::standalone("/test/file"),
            ParserFeatures {
                enable_variable_definitions: true,
            },
        )
        .unwrap();

        let pos = source.find(sub_str).unwrap() as u32;

        // Select the `uri` field
        let position_span = Span {
            start: pos,
            end: pos,
        };

        let resolved = document.resolve((), position_span);

        let project_name = "test".intern();
        let program = Program::new(get_test_schema_with_extensions(
            r#"
        extend type User {
            greeting: String @relay_resolver(fragment_name: "resolverFragment", import_path: "path/to/resolver.js")
          }
        "#,
        ));
        let root_dir = PathBuf::from("/config/root/");
        let extra_data_provider = DummyExtraDataProvider {};

        let goto_definition_response = get_goto_definition_response(
            resolved,
            project_name,
            &program,
            &root_dir,
            &extra_data_provider,
        );

        let uri = match goto_definition_response {
            Ok(GotoDefinitionResponse::Scalar(Location { uri, .. })) => uri.path().to_string(),
            _ => panic!("Invalid go to definition response."),
        };

        assert_eq!(uri, "/config/root/path/to/resolver.js");
    }
}
<commit_msg>Always return Vec in GotoDefinitionResponse<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.
 */

//! Utilities for providing the goto definition feature

use crate::{
    location::{read_graphql_file_and_get_range, to_lsp_location_of_graphql_literal},
    lsp_runtime_error::{LSPRuntimeError, LSPRuntimeResult},
    resolution_path::{
        IdentParent, IdentPath, LinkedFieldPath, ResolutionPath, ResolvePosition, ScalarFieldPath,
        SelectionParent, TypeConditionPath,
    },
    server::GlobalState,
    FieldDefinitionSourceInfo, FieldSchemaInfo, LSPExtraDataProvider,
};
use common::NamedItem;

use graphql_ir::Program;
use interner::{Intern, StringKey};
use lsp_types::{
    request::{GotoDefinition, Request},
    GotoDefinitionResponse, Url,
};
use relay_transforms::{RELAY_RESOLVER_DIRECTIVE_NAME, RELAY_RESOLVER_IMPORT_PATH_ARGUMENT_NAME};
use schema::Schema;
use serde::{Deserialize, Serialize};
use std::{
    path::{Path, PathBuf},
    str,
};

fn get_goto_definition_response<'a>(
    node_path: ResolutionPath<'a>,
    project_name: StringKey,
    program: &Program,
    root_dir: &Path,
    extra_data_provider: &dyn LSPExtraDataProvider,
) -> LSPRuntimeResult<GotoDefinitionResponse> {
    match node_path {
        ResolutionPath::Ident(IdentPath {
            inner: fragment_name,
            parent: IdentParent::FragmentSpreadName(_),
        }) => {
            let fragment = program.fragment(fragment_name.value).ok_or_else(|| {
                LSPRuntimeError::UnexpectedError(format!(
                    "Could not find fragment with name {}",
                    fragment_name
                ))
            })?;

            Ok(GotoDefinitionResponse::Scalar(
                to_lsp_location_of_graphql_literal(fragment.name.location, root_dir)?,
            ))
        }
        ResolutionPath::Ident(IdentPath {
            inner: field_name,
            parent:
                IdentParent::LinkedFieldName(LinkedFieldPath {
                    inner: _,
                    parent: selection_path,
                }),
        }) => resolve_field(
            field_name.value.to_string(),
            selection_path.parent,
            project_name,
            program,
            root_dir,
            extra_data_provider,
        ),
        ResolutionPath::Ident(IdentPath {
            inner: field_name,
            parent:
                IdentParent::ScalarFieldName(ScalarFieldPath {
                    inner: _,
                    parent: selection_path,
                }),
        }) => resolve_field(
            field_name.value.to_string(),
            selection_path.parent,
            project_name,
            program,
            root_dir,
            extra_data_provider,
        ),
        ResolutionPath::Ident(IdentPath {
            inner: _,
            parent:
                IdentParent::TypeConditionType(TypeConditionPath {
                    inner: type_condition,
                    parent: _,
                }),
        }) => {
            let provider_response = extra_data_provider.resolve_field_definition(
                project_name.to_string(),
                type_condition.type_.value.to_string(),
                None,
            );
            let FieldDefinitionSourceInfo {
                file_path,
                line_number,
                is_local,
            } = get_field_definition_source_info_result(provider_response)?;
            if is_local {
                Ok(GotoDefinitionResponse::Scalar(get_location(
                    &file_path,
                    line_number,
                )?))
            } else {
                Err(LSPRuntimeError::ExpectedError)
            }
        }
        _ => Err(LSPRuntimeError::ExpectedError),
    }
}

fn resolve_field<'a>(
    field_name: String,
    selection_parent: SelectionParent<'a>,
    project_name: StringKey,
    program: &Program,
    root_dir: &Path,
    extra_data_provider: &dyn LSPExtraDataProvider,
) -> LSPRuntimeResult<GotoDefinitionResponse> {
    let parent_type = selection_parent
        .find_parent_type(&program.schema)
        .ok_or(LSPRuntimeError::ExpectedError)?;

    let field_name_key = (&field_name as &str).intern();

    let field = program.schema.field(
        program
            .schema
            .named_field(parent_type, field_name_key)
            .ok_or_else(|| {
                LSPRuntimeError::UnexpectedError(format!(
                    "Could not find field with name {}",
                    field_name,
                ))
            })?,
    );

    // Step 1: is this field a resolver?
    if let Some(response) = get_relay_resolver_location(field, root_dir)? {
        return Ok(response);
    }

    let parent_name = program.schema.get_type_name(parent_type);
    let provider_response = extra_data_provider.resolve_field_definition(
        project_name.to_string(),
        parent_name.to_string(),
        Some(FieldSchemaInfo {
            name: field.name.item.to_string(),
            is_extension: field.is_extension,
        }),
    );

    if let Ok(Some(source_info)) = provider_response {
        // Step 2: does extra_data_provider know anything about this field?
        if source_info.is_local {
            Ok(GotoDefinitionResponse::Scalar(get_location(
                &source_info.file_path,
                source_info.line_number,
            )?))
        } else {
            Err(LSPRuntimeError::ExpectedError)
        }
    } else if let Ok((_, location)) = read_graphql_file_and_get_range(field.name.location, root_dir)
    {
        // Step 3: is field a standalone graphql file?
        Ok(GotoDefinitionResponse::Scalar(location))
    } else {
        // Give up
        Err(LSPRuntimeError::ExpectedError)
    }
}

fn get_relay_resolver_location(
    field: &schema::Field,
    root_dir: &Path,
) -> LSPRuntimeResult<Option<GotoDefinitionResponse>> {
    let maybe_resolver_directive = field.directives.named(*RELAY_RESOLVER_DIRECTIVE_NAME);

    Ok(if let Some(resolver_directive) = maybe_resolver_directive {
        let resolver_path = resolver_directive
            .arguments
            .named(*RELAY_RESOLVER_IMPORT_PATH_ARGUMENT_NAME)
            .and_then(|argument| argument.value.get_string_literal())
            .ok_or_else(|| {
                LSPRuntimeError::UnexpectedError(
                "Error resolving field definition location. Malformed schema for Relay Resolver."
                    .to_string(),
            )
            })?;

        let absolute_resolver_path = root_dir.join(PathBuf::from(resolver_path.lookup()));
        Some(GotoDefinitionResponse::Scalar(get_location(
            &absolute_resolver_path.to_string_lossy(),
            0,
        )?))
    } else {
        None
    })
}

pub(crate) fn on_goto_definition(
    state: &impl GlobalState,
    params: <GotoDefinition as Request>::Params,
) -> LSPRuntimeResult<<GotoDefinition as Request>::Result> {
    let (document, position_span) =
        state.extract_executable_document_from_text(&params.text_document_position_params, 1)?;
    let path = document.resolve((), position_span);
    let project_name = state
        .extract_project_name_from_url(&params.text_document_position_params.text_document.uri)?;

    let goto_definition_response = get_goto_definition_response(
        path,
        project_name,
        &state.get_program(&project_name)?,
        &state.root_dir(),
        &*state.get_extra_data_provider(),
    )?;

    // For some lsp-clients, such as clients relying on org.eclipse.lsp4j,
    // (see https://javadoc.io/static/org.eclipse.lsp4j/org.eclipse.lsp4j/0.8.1/org/eclipse/lsp4j/services/TextDocumentService.html)
    // the definition response should be vector of location or locationlink.
    // Therefore, let's convert the GotoDefinitionResponse::Scalar into Vector
    if let GotoDefinitionResponse::Scalar(l) = goto_definition_response {
        return Ok(Some(GotoDefinitionResponse::Array(vec![l])));
    }

    Ok(Some(goto_definition_response))
}

fn get_location(path: &str, line: u64) -> Result<lsp_types::Location, LSPRuntimeError> {
    let start = lsp_types::Position {
        line: line as u32,
        character: 0,
    };
    let range = lsp_types::Range { start, end: start };

    let uri = Url::parse(&format!("file://{}", path)).map_err(|e| {
        LSPRuntimeError::UnexpectedError(format!("Could not parse path as URL: {}", e))
    })?;

    Ok(lsp_types::Location { uri, range })
}

pub(crate) enum GetSourceLocationOfTypeDefinition {}

#[derive(Deserialize, Serialize)]
pub(crate) struct GetSourceLocationOfTypeDefinitionParams {
    type_name: String,
    field_name: Option<String>,
    schema_name: String,
}

#[derive(Deserialize, Serialize)]
pub(crate) struct GetSourceLocationOfTypeDefinitionResult {
    field_definition_source_info: FieldDefinitionSourceInfo,
}

impl Request for GetSourceLocationOfTypeDefinition {
    type Params = GetSourceLocationOfTypeDefinitionParams;
    type Result = GetSourceLocationOfTypeDefinitionResult;
    const METHOD: &'static str = "relay/getSourceLocationOfTypeDefinition";
}

pub(crate) fn on_get_source_location_of_type_definition(
    state: &impl GlobalState,
    params: <GetSourceLocationOfTypeDefinition as Request>::Params,
) -> LSPRuntimeResult<<GetSourceLocationOfTypeDefinition as Request>::Result> {
    let schema = state.get_schema(&(&params.schema_name as &str).intern())?;

    let type_ = schema
        .get_type((&params.type_name as &str).intern())
        .ok_or_else(|| {
            LSPRuntimeError::UnexpectedError(format!(
                "Could not find type with name {}",
                &params.type_name
            ))
        })?;

    let field_info = params
        .field_name
        .map(|field_name| {
            schema
                .named_field(type_, (&field_name as &str).intern())
                .ok_or_else(|| {
                    LSPRuntimeError::UnexpectedError(format!(
                        "Could not find field with name {}",
                        field_name
                    ))
                })
        })
        .transpose()?
        .map(|field_id| {
            let field = schema.field(field_id);
            FieldSchemaInfo {
                name: field.name.item.to_string(),
                is_extension: field.is_extension,
            }
        });

    // TODO add go-to-definition for client fields
    let field_definition_source_info = get_field_definition_source_info_result(
        state.get_extra_data_provider().resolve_field_definition(
            params.schema_name,
            params.type_name,
            field_info,
        ),
    )?;

    Ok(GetSourceLocationOfTypeDefinitionResult {
        field_definition_source_info,
    })
}

fn get_field_definition_source_info_result(
    result: Result<Option<FieldDefinitionSourceInfo>, String>,
) -> LSPRuntimeResult<FieldDefinitionSourceInfo> {
    result
        .map_err(LSPRuntimeError::UnexpectedError)?
        .ok_or_else(|| {
            LSPRuntimeError::UnexpectedError(
                "Expected result when resolving field definition location".to_string(),
            )
        })
}

#[cfg(test)]
mod test {
    use std::path::PathBuf;

    use common::{SourceLocationKey, Span};
    use graphql_ir::Program;
    use graphql_syntax::{parse_executable_with_features, ParserFeatures};
    use interner::Intern;
    use lsp_types::{GotoDefinitionResponse, Location};
    use relay_test_schema::get_test_schema_with_extensions;

    use crate::{
        resolution_path::ResolvePosition, FieldDefinitionSourceInfo, FieldSchemaInfo,
        LSPExtraDataProvider,
    };

    use super::get_goto_definition_response;

    struct DummyExtraDataProvider {}

    impl LSPExtraDataProvider for DummyExtraDataProvider {
        fn fetch_query_stats(&self, _search_token: &str) -> Vec<String> {
            Vec::new()
        }

        fn resolve_field_definition(
            &self,
            _project_name: String,
            _parent_type: String,
            _field_info: Option<FieldSchemaInfo>,
        ) -> Result<Option<FieldDefinitionSourceInfo>, String> {
            Err("Not implemented".to_string())
        }
    }

    #[test]
    fn resolver_field() {
        let source = r#"
    query Foo {
        me {
            greeting
        }
    }
"#;
        let sub_str = "greeting";
        let document = parse_executable_with_features(
            source,
            SourceLocationKey::standalone("/test/file"),
            ParserFeatures {
                enable_variable_definitions: true,
            },
        )
        .unwrap();

        let pos = source.find(sub_str).unwrap() as u32;

        // Select the `uri` field
        let position_span = Span {
            start: pos,
            end: pos,
        };

        let resolved = document.resolve((), position_span);

        let project_name = "test".intern();
        let program = Program::new(get_test_schema_with_extensions(
            r#"
        extend type User {
            greeting: String @relay_resolver(fragment_name: "resolverFragment", import_path: "path/to/resolver.js")
          }
        "#,
        ));
        let root_dir = PathBuf::from("/config/root/");
        let extra_data_provider = DummyExtraDataProvider {};

        let goto_definition_response = get_goto_definition_response(
            resolved,
            project_name,
            &program,
            &root_dir,
            &extra_data_provider,
        );

        let uri = match goto_definition_response {
            Ok(GotoDefinitionResponse::Scalar(Location { uri, .. })) => uri.path().to_string(),
            _ => panic!("Invalid go to definition response."),
        };

        assert_eq!(uri, "/config/root/path/to/resolver.js");
    }
}
<|endoftext|>


<commit_before>use std::path::PathBuf;
use std::str::FromStr;
use std::process::exit;

pub enum Depth {
    Unlimited,
    Limited(usize)
}

impl Depth {
    pub fn accepts(&self, level: usize) -> bool {
        match *self {
            Depth::Unlimited => true,
            Depth::Limited(size) => size > level
        }
    }
}

impl FromStr for Depth {
    type Err = String;

    fn from_str(s: &str) -> Result<Self, String> {
        let number = match s.parse::<usize>() {
            Ok(number) => number,
            Err(_) => return Err("Not a positive integer".to_string())
        };

        // number: usize is never negative. :-)
        if number == 0 {
            Ok(Depth::Unlimited)
        } else {
            Ok(Depth::Limited(number))
        }
    }
}

pub struct Options {
    roots: Vec<String>,
    depth: Depth,
}

impl Options {
    pub fn roots(&self) -> Vec<PathBuf> {
        self.roots.iter().map(|root| PathBuf::from(&root)).collect()
    }

    pub fn depth(&self) -> &Depth {
        &self.depth
    }
}

pub fn parse() -> Options {
    let matches = clap_app!(dutop =>
        (version: "0.1")
        (author: "Magnus Bergmark <magnus.bergmark@gmail.com>")
        (about: "Prints the largest entries in a directory")

        (@arg DIR: ... "The directories to look in (defaults to current working directory).")

        (@arg depth:
            -d --depth [INT]
            {|value| {
                let parsed = value.parse::<usize>();
                if parsed.is_ok() {
                    Ok(())
                } else {
                    Err("Not a positive integer".to_string())
                }
            }}
            "The depth to recurse when printing out entries. Defaults to 1. 0 means unlimited depth."
        )

        (@arg recursive: -r --recursive "Show the entire tree instead of just the direct children. This implies unlimited --depth.")
    ).get_matches();

    let roots = matches.values_of("DIR").unwrap_or(vec!["."]);

    let default_depth = if matches.is_present("recursive") {
        Depth::Unlimited
    } else {
        Depth::Limited(1)
    };

    let depth = match matches.is_present("depth") {
        true => {
            // We can unwrap since the argument is required. We'd never get here unless the value
            // exists.
            let value = matches.value_of("depth").unwrap();
            value.parse::<Depth>().unwrap_or_else(|error| {
                println!("Could not determine depth: {}", error);
                exit(2);
            })
        },
        false => default_depth
    };

    Options{
        roots: roots.iter().map(|value| value.to_string()).collect(),
        depth: depth,
    }
}
<commit_msg>Use a better usage line in --help<commit_after>use std::path::PathBuf;
use std::str::FromStr;
use std::process::exit;

pub enum Depth {
    Unlimited,
    Limited(usize)
}

impl Depth {
    pub fn accepts(&self, level: usize) -> bool {
        match *self {
            Depth::Unlimited => true,
            Depth::Limited(size) => size > level
        }
    }
}

impl FromStr for Depth {
    type Err = String;

    fn from_str(s: &str) -> Result<Self, String> {
        let number = match s.parse::<usize>() {
            Ok(number) => number,
            Err(_) => return Err("Not a positive integer".to_string())
        };

        // number: usize is never negative. :-)
        if number == 0 {
            Ok(Depth::Unlimited)
        } else {
            Ok(Depth::Limited(number))
        }
    }
}

pub struct Options {
    roots: Vec<String>,
    depth: Depth,
}

impl Options {
    pub fn roots(&self) -> Vec<PathBuf> {
        self.roots.iter().map(|root| PathBuf::from(&root)).collect()
    }

    pub fn depth(&self) -> &Depth {
        &self.depth
    }
}

pub fn parse() -> Options {
    let matches = clap_app!(dutop =>
        (version: "0.1")
        (author: "Magnus Bergmark <magnus.bergmark@gmail.com>")
        (about: "Prints the largest entries in a directory")
        (usage: "dutop [OPTIONS] [--] [DIR [DIR...]]")

        (@arg DIR: ... "The directories to look in (defaults to current working directory).")

        (@arg depth:
            -d --depth [INT]
            {|value| {
                let parsed = value.parse::<usize>();
                if parsed.is_ok() {
                    Ok(())
                } else {
                    Err("Not a positive integer".to_string())
                }
            }}
            "The depth to recurse when printing out entries. Defaults to 1. 0 means unlimited depth."
        )

        (@arg recursive: -r --recursive "Show the entire tree instead of just the direct children. This implies unlimited --depth.")
    ).get_matches();

    let roots = matches.values_of("DIR").unwrap_or(vec!["."]);

    let default_depth = if matches.is_present("recursive") {
        Depth::Unlimited
    } else {
        Depth::Limited(1)
    };

    let depth = match matches.is_present("depth") {
        true => {
            // We can unwrap since the argument is required. We'd never get here unless the value
            // exists.
            let value = matches.value_of("depth").unwrap();
            value.parse::<Depth>().unwrap_or_else(|error| {
                println!("Could not determine depth: {}", error);
                exit(2);
            })
        },
        false => default_depth
    };

    Options{
        roots: roots.iter().map(|value| value.to_string()).collect(),
        depth: depth,
    }
}
<|endoftext|>


<commit_before>// Copyright (c) 2017-2018, The rav1e contributors. All rights reserved
//
// This source code is subject to the terms of the BSD 2 Clause License and
// the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License
// was not distributed with this source code in the LICENSE file, you can
// obtain it at www.aomedia.org/license/software. If the Alliance for Open
// Media Patent License 1.0 was not distributed with this source code in the
// PATENTS file, you can obtain it at www.aomedia.org/license/patent.

use y4m;

#[macro_use]
extern crate scan_fmt;

mod common;
mod decoder;
mod muxer;
use crate::common::*;
use crate::muxer::*;
use rav1e::*;

use std::io;
use std::io::Write;
use std::io::Read;
use std::path::Path;
use std::sync::Arc;
use crate::decoder::Decoder;
use crate::decoder::VideoDetails;
use std::fs::File;
use std::io::BufWriter;

fn read_frame<T: Pixel, D: Decoder>(ctx: &mut Context<T>, decoder: &mut D, video_info: VideoDetails) {
  match decoder.read_frame(&video_info) {
    Ok(frame) => {
      match video_info.bit_depth {
        8 | 10 | 12 => {}
        _ => panic!("unknown input bit depth!")
      }

      let _ = ctx.send_frame(Some(Arc::new(frame)));
    }
    _ => {
      let frames_to_be_coded = ctx.get_frame_count();
      // This is a hack, instead when EOF is reached simply "close" the encoder to input (flag)
      ctx.set_limit(frames_to_be_coded);
      ctx.flush();
    }
  };
}

// Encode and write a frame.
// Returns frame information in a `Result`.
fn process_frame<T: Pixel>(
  ctx: &mut Context<T>,
  output_file: &mut dyn Write,
  y4m_dec: &mut y4m::Decoder<'_, Box<dyn Read>>,
  mut y4m_enc: Option<&mut y4m::Encoder<'_, Box<dyn Write>>>,
) -> Result<Vec<FrameSummary>, ()> {
  let y4m_details = y4m_dec.get_video_details();
  let mut frame_summaries = Vec::new();
  let pkt_wrapped = ctx.receive_packet();
  match pkt_wrapped {
    Ok(pkt) => {
      write_ivf_frame(output_file, pkt.number as u64, pkt.data.as_ref());
      if let (Some(ref mut y4m_enc_uw), Some(ref rec)) = (y4m_enc.as_mut(), &pkt.rec) {
        write_y4m_frame(y4m_enc_uw, rec, y4m_details);
      }
      frame_summaries.push(pkt.into());
    }
    Err(EncoderStatus::NeedMoreFrames) => {
      read_frame(ctx, y4m_dec, y4m_details);
    }
    Err(EncoderStatus::NeedMoreData) | Err(EncoderStatus::EnoughData) => {
      // Expected statuses, continue without error
    }
    Err(EncoderStatus::Failure) | Err(EncoderStatus::InvalidKey) | Err(EncoderStatus::ParseError) => {
      panic!("Failed to encode video");
    }
  }
  Ok(frame_summaries)
}

fn write_stats_file<T: Pixel>(ctx: &Context<T>, filename: &Path) -> Result<(), io::Error> {
  let file = File::create(filename)?;
  let writer = BufWriter::new(file);
  serde_json::to_writer(writer, &ctx.first_pass_data).expect("Serialization should not fail");
  Ok(())
}

fn main() {
  let mut cli = parse_cli();
  let mut y4m_dec = y4m::decode(&mut cli.io.input).expect("input is not a y4m file");
  let video_info = y4m_dec.get_video_details();
  let mut y4m_enc = match cli.io.rec.as_mut() {
    Some(rec) => Some(
      y4m::encode(
        video_info.width,
        video_info.height,
        y4m::Ratio::new(video_info.time_base.den as usize, video_info.time_base.num as usize)
      ).with_colorspace(y4m_dec.get_colorspace())
        .write_header(rec)
        .unwrap()
    ),
    None => None
  };

  cli.enc.width = video_info.width;
  cli.enc.height = video_info.height;
  cli.enc.bit_depth = video_info.bit_depth;
  cli.enc.chroma_sampling = video_info.chroma_sampling;
  cli.enc.chroma_sample_position = video_info.chroma_sample_position;
  cli.enc.time_base = video_info.time_base;
  let cfg = Config {
    enc: cli.enc
  };

  // FIXME for now, unconditionally create Context<u16>
  let mut ctx: Context<u16> = cfg.new_context();

  let stderr = io::stderr();
  let mut err = stderr.lock();

  let _ = writeln!(
    err,
    "{}x{} @ {}/{} fps",
    video_info.width,
    video_info.height,
    video_info.time_base.den,
    video_info.time_base.num
  );

  write_ivf_header(
    &mut cli.io.output,
    video_info.width,
    video_info.height,
    video_info.time_base.den as usize,
    video_info.time_base.num as usize
  );

  let mut progress = ProgressInfo::new(
    Rational { num: video_info.time_base.den, den: video_info.time_base.num },
    if cli.limit == 0 { None } else { Some(cli.limit) },
      cfg.enc.show_psnr
  );

  ctx.set_limit(cli.limit as u64);
  let mut frame_current_count = 0;

  while frame_current_count <= cli.skip {
       y4m_dec.read_frame().expect("Skipped more frames than in the input");
       frame_current_count+=1;
  }

  while let Ok(frame_info) = process_frame(&mut ctx, &mut cli.io.output, &mut y4m_dec, y4m_enc.as_mut()) {
    for frame in frame_info {
      progress.add_frame(frame);
      let _ = if cli.verbose {
        writeln!(err, "{} - {}", frame, progress)
      } else {
        write!(err, "\r{}                    ", progress)
      };
    }

    if !ctx.needs_more_frames(progress.frames_encoded() as u64) {
      break;
    }

    cli.io.output.flush().unwrap();
  }

  if cfg.enc.pass == Some(1) {
    if let Err(e) = write_stats_file(&ctx, cfg.enc.stats_file.as_ref().unwrap()) {
      let _ = writeln!(err, "\nError: Failed to write stats file! {}\n", e);
    }
  }
  let _ = write!(err, "\n{}\n", progress.print_summary());
}
<commit_msg>Fix an off-by-one with --skip parameter<commit_after>// Copyright (c) 2017-2018, The rav1e contributors. All rights reserved
//
// This source code is subject to the terms of the BSD 2 Clause License and
// the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License
// was not distributed with this source code in the LICENSE file, you can
// obtain it at www.aomedia.org/license/software. If the Alliance for Open
// Media Patent License 1.0 was not distributed with this source code in the
// PATENTS file, you can obtain it at www.aomedia.org/license/patent.

use y4m;

#[macro_use]
extern crate scan_fmt;

mod common;
mod decoder;
mod muxer;
use crate::common::*;
use crate::muxer::*;
use rav1e::*;

use std::io;
use std::io::Write;
use std::io::Read;
use std::path::Path;
use std::sync::Arc;
use crate::decoder::Decoder;
use crate::decoder::VideoDetails;
use std::fs::File;
use std::io::BufWriter;

fn read_frame<T: Pixel, D: Decoder>(ctx: &mut Context<T>, decoder: &mut D, video_info: VideoDetails) {
  match decoder.read_frame(&video_info) {
    Ok(frame) => {
      match video_info.bit_depth {
        8 | 10 | 12 => {}
        _ => panic!("unknown input bit depth!")
      }

      let _ = ctx.send_frame(Some(Arc::new(frame)));
    }
    _ => {
      let frames_to_be_coded = ctx.get_frame_count();
      // This is a hack, instead when EOF is reached simply "close" the encoder to input (flag)
      ctx.set_limit(frames_to_be_coded);
      ctx.flush();
    }
  };
}

// Encode and write a frame.
// Returns frame information in a `Result`.
fn process_frame<T: Pixel>(
  ctx: &mut Context<T>,
  output_file: &mut dyn Write,
  y4m_dec: &mut y4m::Decoder<'_, Box<dyn Read>>,
  mut y4m_enc: Option<&mut y4m::Encoder<'_, Box<dyn Write>>>,
) -> Result<Vec<FrameSummary>, ()> {
  let y4m_details = y4m_dec.get_video_details();
  let mut frame_summaries = Vec::new();
  let pkt_wrapped = ctx.receive_packet();
  match pkt_wrapped {
    Ok(pkt) => {
      write_ivf_frame(output_file, pkt.number as u64, pkt.data.as_ref());
      if let (Some(ref mut y4m_enc_uw), Some(ref rec)) = (y4m_enc.as_mut(), &pkt.rec) {
        write_y4m_frame(y4m_enc_uw, rec, y4m_details);
      }
      frame_summaries.push(pkt.into());
    }
    Err(EncoderStatus::NeedMoreFrames) => {
      read_frame(ctx, y4m_dec, y4m_details);
    }
    Err(EncoderStatus::NeedMoreData) | Err(EncoderStatus::EnoughData) => {
      // Expected statuses, continue without error
    }
    Err(EncoderStatus::Failure) | Err(EncoderStatus::InvalidKey) | Err(EncoderStatus::ParseError) => {
      panic!("Failed to encode video");
    }
  }
  Ok(frame_summaries)
}

fn write_stats_file<T: Pixel>(ctx: &Context<T>, filename: &Path) -> Result<(), io::Error> {
  let file = File::create(filename)?;
  let writer = BufWriter::new(file);
  serde_json::to_writer(writer, &ctx.first_pass_data).expect("Serialization should not fail");
  Ok(())
}

fn main() {
  let mut cli = parse_cli();
  let mut y4m_dec = y4m::decode(&mut cli.io.input).expect("input is not a y4m file");
  let video_info = y4m_dec.get_video_details();
  let mut y4m_enc = match cli.io.rec.as_mut() {
    Some(rec) => Some(
      y4m::encode(
        video_info.width,
        video_info.height,
        y4m::Ratio::new(video_info.time_base.den as usize, video_info.time_base.num as usize)
      ).with_colorspace(y4m_dec.get_colorspace())
        .write_header(rec)
        .unwrap()
    ),
    None => None
  };

  cli.enc.width = video_info.width;
  cli.enc.height = video_info.height;
  cli.enc.bit_depth = video_info.bit_depth;
  cli.enc.chroma_sampling = video_info.chroma_sampling;
  cli.enc.chroma_sample_position = video_info.chroma_sample_position;
  cli.enc.time_base = video_info.time_base;
  let cfg = Config {
    enc: cli.enc
  };

  // FIXME for now, unconditionally create Context<u16>
  let mut ctx: Context<u16> = cfg.new_context();

  let stderr = io::stderr();
  let mut err = stderr.lock();

  let _ = writeln!(
    err,
    "{}x{} @ {}/{} fps",
    video_info.width,
    video_info.height,
    video_info.time_base.den,
    video_info.time_base.num
  );

  write_ivf_header(
    &mut cli.io.output,
    video_info.width,
    video_info.height,
    video_info.time_base.den as usize,
    video_info.time_base.num as usize
  );

  let mut progress = ProgressInfo::new(
    Rational { num: video_info.time_base.den, den: video_info.time_base.num },
    if cli.limit == 0 { None } else { Some(cli.limit) },
      cfg.enc.show_psnr
  );

  ctx.set_limit(cli.limit as u64);

  for _ in 0..cli.skip {
    y4m_dec.read_frame().expect("Skipped more frames than in the input");
  }

  while let Ok(frame_info) = process_frame(&mut ctx, &mut cli.io.output, &mut y4m_dec, y4m_enc.as_mut()) {
    for frame in frame_info {
      progress.add_frame(frame);
      let _ = if cli.verbose {
        writeln!(err, "{} - {}", frame, progress)
      } else {
        write!(err, "\r{}                    ", progress)
      };
    }

    if !ctx.needs_more_frames(progress.frames_encoded() as u64) {
      break;
    }

    cli.io.output.flush().unwrap();
  }

  if cfg.enc.pass == Some(1) {
    if let Err(e) = write_stats_file(&ctx, cfg.enc.stats_file.as_ref().unwrap()) {
      let _ = writeln!(err, "\nError: Failed to write stats file! {}\n", e);
    }
  }
  let _ = write!(err, "\n{}\n", progress.print_summary());
}
<|endoftext|>


<commit_before>//! Defines the BSDF which acts as a container for composing the various BRDFs
//! and BTDFs that describe the surface's properties

use std::vec::Vec;
use collect::enum_set::EnumSet;

use linalg;
use linalg::{Normal, Vector, Point};
use film::Colorf;
use geometry::DifferentialGeometry;
use bxdf::{BxDF, BxDFType};

/// The BSDF contains the various BRDFs and BTDFs that describe the surface's properties
/// at some point. It also transforms incident and outgoing light directions into
/// shading space to make the BxDFs easier to implement.
/// TODO: We really need the memory pool. Each time we get the bsdf from a
/// material we need to allocate a decent amount of stuff since they each need
/// their own tangent, bitangent and differential geometry reference.
pub struct BSDF<'a> {
    /// The hit point
    pub p: Point,
    /// Shading normal, may be perturbed by bump mapping
    pub n: Normal,
    /// The actual geometry normal
    pub ng: Normal,
    /// Tangent vector for the surface
    pub tan: Vector,
    /// Bitangent vector for the surface
    pub bitan: Vector,
    /// Refractive index of the geometry
    pub eta: f32,
    /// TODO: Currently a Vec is safe to use but once in the memory pool it
    /// will leak since it won't be dropped. This would also migrate our BxDFs
    /// from Box<BxDF> to &BxDF. When unboxed traits land we can move to unboxed
    /// BxDFs here though.
    bxdfs: &'a Vec<Box<BxDF + Send + Sync>>,
}

impl<'a> BSDF<'a> {
    /// Create a new BSDF using the BxDFs passed to shade the differential geometry with
    /// refractive index `eta`
    pub fn new(bxdfs: &'a Vec<Box<BxDF + Send + Sync>>, eta: f32,
               dg: &DifferentialGeometry<'a>)
               -> BSDF<'a> {
        let n = dg.n.normalized();
        let bitan = dg.dp_du.normalized();
        let tan = linalg::cross(&n, &bitan);
        BSDF { p: dg.p, n: n, ng: dg.ng, tan: tan, bitan: bitan, bxdfs: bxdfs, eta: eta }
    }
    /// Return the total number of BxDFs
    pub fn num_bxdfs(&self) -> usize { self.bxdfs.len() }
    /// Return the number of BxDFs matching the flags
    pub fn num_matching(&self, flags: EnumSet<BxDFType>) -> usize {
        self.bxdfs.iter().filter(|ref x| x.matches(flags)).count()
    }
    /// Transform the vector from world space to shading space
    pub fn to_shading(&self, v: &Vector) -> Vector {
        Vector::new(linalg::dot(v, &self.bitan), linalg::dot(v, &self.tan),
                    linalg::dot(v, &self.n))
    }
    /// Transform the vectro from shading space to world space
    pub fn from_shading(&self, v: &Vector) -> Vector {
        Vector::new(self.bitan.x * v.x + self.tan.x * v.y + self.n.x * v.z,
                    self.bitan.y * v.x + self.tan.y * v.y + self.n.y * v.z,
                    self.bitan.z * v.x + self.tan.z * v.y + self.n.z * v.z)
    }
    /// Evaluate the BSDF for the outgoing and incident light directions
    /// `w_o` and `w_i` in world space, sampling the desired subset of BxDFs
    /// selected by the flags passed. `wo_world` and `wi_world` should point from
    /// the hit point in the outgoing and incident light directions respectively.
    pub fn eval(&self, wo_world: &Vector, wi_world: &Vector, mut flags: EnumSet<BxDFType>) -> Colorf {
        let w_o = self.to_shading(wo_world);
        let w_i = self.to_shading(wi_world);
        // Determine if we should evaluate reflection or transmission based on the
        // geometry normal and the light directions
        if linalg::dot(wo_world, &self.ng) * linalg::dot(wi_world, &self.ng) > 0.0 {
            flags.remove(&BxDFType::Transmission);
        } else {
            flags.remove(&BxDFType::Reflection);
        }
        // Find all matching BxDFs and add their contribution to the material's color
        self.bxdfs.iter().filter(|ref x| x.matches(flags)).map(|ref x| x.eval(&w_o, &w_i))
            .fold(Colorf::broadcast(0.0), |x, y| x + y)
    }
    /// Sample a component of the BSDF to get an incident light direction for light
    /// leaving the surface along `w_o`. Returns the color, direction and the type
    /// of the BxDF that was sampled. TODO: Need to take random numbers here for proper
    /// sampling. Currently we just find the first that matches which is so incredibly wrong
    /// for proper sampling.
    pub fn sample(&self, wo_world: &Vector, flags: EnumSet<BxDFType>) -> (Colorf, Vector, EnumSet<BxDFType>) {
        let n_matching = self.num_matching(flags);
        if n_matching == 0 {
            return (Colorf::broadcast(0.0), Vector::broadcast(0.0), EnumSet::new());
        }
        // Really bad: just take the first for now. This will be ok for having very basic
        // materials like our single component diffuse and specular but is wrong for
        // more complicated ones
        let bxdf = self.matching_at(0, flags);
        let w_o = self.to_shading(wo_world);
        let (f, w_i) = bxdf.sample(&w_o);
        // TODO: Will fail for later materials - we assume only one matched. For the diffuse
        // and specular metal materials this is correct but not for more complex ones
        (f, self.from_shading(&w_i), bxdf.bxdf_type())
    }
    /// Get the `i`th BxDF that matches the flags passed. There should not be fewer than i
    /// BxDFs that match the flags
    fn matching_at(&self, i: usize, flags: EnumSet<BxDFType>) -> &Box<BxDF + Send + Sync> {
        let mut it = self.bxdfs.iter().filter(|ref x| x.matches(flags)).skip(i);
        match it.next() {
            Some(b) => b,
            None => panic!("Out of bounds index for BxDF type {:?}", flags)
        }
    }
}

<commit_msg>Just noticed `filter_map` in the docs, super cool!<commit_after>//! Defines the BSDF which acts as a container for composing the various BRDFs
//! and BTDFs that describe the surface's properties

use std::vec::Vec;
use collect::enum_set::EnumSet;

use linalg;
use linalg::{Normal, Vector, Point};
use film::Colorf;
use geometry::DifferentialGeometry;
use bxdf::{BxDF, BxDFType};

/// The BSDF contains the various BRDFs and BTDFs that describe the surface's properties
/// at some point. It also transforms incident and outgoing light directions into
/// shading space to make the BxDFs easier to implement.
/// TODO: We really need the memory pool. Each time we get the bsdf from a
/// material we need to allocate a decent amount of stuff since they each need
/// their own tangent, bitangent and differential geometry reference.
pub struct BSDF<'a> {
    /// The hit point
    pub p: Point,
    /// Shading normal, may be perturbed by bump mapping
    pub n: Normal,
    /// The actual geometry normal
    pub ng: Normal,
    /// Tangent vector for the surface
    pub tan: Vector,
    /// Bitangent vector for the surface
    pub bitan: Vector,
    /// Refractive index of the geometry
    pub eta: f32,
    /// TODO: Currently a Vec is safe to use but once in the memory pool it
    /// will leak since it won't be dropped. This would also migrate our BxDFs
    /// from Box<BxDF> to &BxDF. When unboxed traits land we can move to unboxed
    /// BxDFs here though.
    bxdfs: &'a Vec<Box<BxDF + Send + Sync>>,
}

impl<'a> BSDF<'a> {
    /// Create a new BSDF using the BxDFs passed to shade the differential geometry with
    /// refractive index `eta`
    pub fn new(bxdfs: &'a Vec<Box<BxDF + Send + Sync>>, eta: f32,
               dg: &DifferentialGeometry<'a>)
               -> BSDF<'a> {
        let n = dg.n.normalized();
        let bitan = dg.dp_du.normalized();
        let tan = linalg::cross(&n, &bitan);
        BSDF { p: dg.p, n: n, ng: dg.ng, tan: tan, bitan: bitan, bxdfs: bxdfs, eta: eta }
    }
    /// Return the total number of BxDFs
    pub fn num_bxdfs(&self) -> usize { self.bxdfs.len() }
    /// Return the number of BxDFs matching the flags
    pub fn num_matching(&self, flags: EnumSet<BxDFType>) -> usize {
        self.bxdfs.iter().filter(|ref x| x.matches(flags)).count()
    }
    /// Transform the vector from world space to shading space
    pub fn to_shading(&self, v: &Vector) -> Vector {
        Vector::new(linalg::dot(v, &self.bitan), linalg::dot(v, &self.tan),
                    linalg::dot(v, &self.n))
    }
    /// Transform the vectro from shading space to world space
    pub fn from_shading(&self, v: &Vector) -> Vector {
        Vector::new(self.bitan.x * v.x + self.tan.x * v.y + self.n.x * v.z,
                    self.bitan.y * v.x + self.tan.y * v.y + self.n.y * v.z,
                    self.bitan.z * v.x + self.tan.z * v.y + self.n.z * v.z)
    }
    /// Evaluate the BSDF for the outgoing and incident light directions
    /// `w_o` and `w_i` in world space, sampling the desired subset of BxDFs
    /// selected by the flags passed. `wo_world` and `wi_world` should point from
    /// the hit point in the outgoing and incident light directions respectively.
    pub fn eval(&self, wo_world: &Vector, wi_world: &Vector, mut flags: EnumSet<BxDFType>) -> Colorf {
        let w_o = self.to_shading(wo_world);
        let w_i = self.to_shading(wi_world);
        // Determine if we should evaluate reflection or transmission based on the
        // geometry normal and the light directions
        if linalg::dot(wo_world, &self.ng) * linalg::dot(wi_world, &self.ng) > 0.0 {
            flags.remove(&BxDFType::Transmission);
        } else {
            flags.remove(&BxDFType::Reflection);
        }
        // Find all matching BxDFs and add their contribution to the material's color
        self.bxdfs.iter().filter_map(|ref x| if x.matches(flags) { Some(x.eval(&w_o, &w_i)) } else { None })
            .fold(Colorf::broadcast(0.0), |x, y| x + y)
    }
    /// Sample a component of the BSDF to get an incident light direction for light
    /// leaving the surface along `w_o`. Returns the color, direction and the type
    /// of the BxDF that was sampled. TODO: Need to take random numbers here for proper
    /// sampling. Currently we just find the first that matches which is so incredibly wrong
    /// for proper sampling.
    pub fn sample(&self, wo_world: &Vector, flags: EnumSet<BxDFType>) -> (Colorf, Vector, EnumSet<BxDFType>) {
        let n_matching = self.num_matching(flags);
        if n_matching == 0 {
            return (Colorf::broadcast(0.0), Vector::broadcast(0.0), EnumSet::new());
        }
        // Really bad: just take the first for now. This will be ok for having very basic
        // materials like our single component diffuse and specular but is wrong for
        // more complicated ones
        let bxdf = self.matching_at(0, flags);
        let w_o = self.to_shading(wo_world);
        let (f, w_i) = bxdf.sample(&w_o);
        // TODO: Will fail for later materials - we assume only one matched. For the diffuse
        // and specular metal materials this is correct but not for more complex ones
        (f, self.from_shading(&w_i), bxdf.bxdf_type())
    }
    /// Get the `i`th BxDF that matches the flags passed. There should not be fewer than i
    /// BxDFs that match the flags
    fn matching_at(&self, i: usize, flags: EnumSet<BxDFType>) -> &Box<BxDF + Send + Sync> {
        let mut it = self.bxdfs.iter().filter(|ref x| x.matches(flags)).skip(i);
        match it.next() {
            Some(b) => b,
            None => panic!("Out of bounds index for BxDF type {:?}", flags)
        }
    }
}

<|endoftext|>


<commit_before>/// Character level parsers

use internal::{IResult,Needed};

/// matches one of the provided characters
///
/// # Example
/// ```
/// # #[macro_use] extern crate nom;
/// # use nom::IResult;
/// # fn main() {
/// named!(a_or_b<char>, one_of!("ab"));
/// # }
/// ```
#[macro_export]
macro_rules! one_of (
  ($i:expr, $inp: expr) => (
    {
      if $i.is_empty() {
        $crate::IResult::Incomplete::<_, _>($crate::Needed::Size(1))
      } else {
        #[inline(always)]
        fn as_bytes<T: $crate::AsBytes>(b: &T) -> &[u8] {
          b.as_bytes()
        }

        let expected = $inp;
        let bytes = as_bytes(&expected);
        one_of_bytes!($i, bytes)
      }
    }
  );
);

#[doc(hidden)]
#[macro_export]
macro_rules! one_of_bytes (
  ($i:expr, $bytes: expr) => (
    {
      if $i.is_empty() {
        $crate::IResult::Incomplete::<_, _>($crate::Needed::Size(1))
      } else {
        let mut found = false;

        for &i in $bytes {
          if i == $i[0] {
            found = true;
            break;
          }
        }

        if found {
          $crate::IResult::Done(&$i[1..], $i[0] as char)
        } else {
          $crate::IResult::Error(error_position!($crate::ErrorKind::OneOf, $i))
        }
      }
    }
  );
);

/// matches anything but the provided characters
#[macro_export]
macro_rules! none_of (
  ($i:expr, $inp: expr) => (
    {
      if $i.is_empty() {
        $crate::IResult::Incomplete::<_, _>($crate::Needed::Size(1))
      } else {
        #[inline(always)]
        fn as_bytes<T: $crate::AsBytes>(b: &T) -> &[u8] {
          b.as_bytes()
        }

        let expected = $inp;
        let bytes = as_bytes(&expected);
        none_of_bytes!($i, bytes)
      }
    }
  );
);

#[doc(hidden)]
#[macro_export]
macro_rules! none_of_bytes (
  ($i:expr, $bytes: expr) => (
    {
      if $i.is_empty() {
        $crate::IResult::Incomplete::<_, _>($crate::Needed::Size(1))
      } else {
        let mut found = false;

        for &i in $bytes {
          if i == $i[0] {
            found = true;
            break;
          }
        }

        if !found {
          $crate::IResult::Done(&$i[1..], $i[0] as char)
        } else {
          $crate::IResult::Error(error_position!($crate::ErrorKind::NoneOf, $i))
        }
      }
    }
  );
);

/// matches one character: `char!(char) => &[u8] -> IResult<&[u8], char>
#[macro_export]
macro_rules! char (
  ($i:expr, $c: expr) => (
    {
      if $i.is_empty() {
        let res: $crate::IResult<&[u8], char> = $crate::IResult::Incomplete($crate::Needed::Size(1));
        res
      } else {
        if $i[0] == $c as u8 {
          $crate::IResult::Done(&$i[1..], $i[0] as char)
        } else {
          $crate::IResult::Error(error_position!($crate::ErrorKind::Char, $i))
        }
      }
    }
  );
);

named!(#[doc="Matches a newline character '\\n'"], pub newline<char>, char!('\n'));

named!(#[doc="Matches a tab character '\\t'"], pub tab<char>, char!('\t'));

pub fn anychar(input:&[u8]) -> IResult<&[u8], char> {
  if input.is_empty() {
    IResult::Incomplete(Needed::Size(1))
  } else {
    IResult::Done(&input[1..], input[0] as char)
  }
}

#[cfg(test)]
mod tests {
  use internal::IResult::*;
  use util::ErrorKind;

  #[test]
  fn one_of() {
    named!(f<char>, one_of!("ab"));

    let a = &b"abcd"[..];
    assert_eq!(f(a), Done(&b"bcd"[..], 'a'));

    let b = &b"cde"[..];
    assert_eq!(f(b), Error(error_position!(ErrorKind::OneOf, b)));
  }

  #[test]
  fn none_of() {
    named!(f<char>, none_of!("ab"));

    let a = &b"abcd"[..];
    assert_eq!(f(a), Error(error_position!(ErrorKind::NoneOf, a)));

    let b = &b"cde"[..];
    assert_eq!(f(b), Done(&b"de"[..], 'c'));
  }

  #[test]
  fn char() {
    named!(f<char>, char!('c'));

    let a = &b"abcd"[..];
    assert_eq!(f(a), Error(error_position!(ErrorKind::Char, a)));

    let b = &b"cde"[..];
    assert_eq!(f(b), Done(&b"de"[..], 'c'));
  }

}
<commit_msg>Add test - as yet unimplemented<commit_after>/// Character level parsers

use internal::{IResult,Needed};

/// matches one of the provided characters
///
/// # Example
/// ```
/// # #[macro_use] extern crate nom;
/// # use nom::IResult;
/// # fn main() {
/// named!(simple<char>, one_of!(b"abc"));
/// assert_eq!(simple(b"a123"), IResult::Done(&b"123"[..], 'a'));
///
/// named!(a_or_b<&str, char>, one_of!("ab汉"));
/// assert_eq!(a_or_b("汉jiosfe"), IResult::Done("jiosfe", '汉'));
/// # }
/// ```
#[macro_export]
macro_rules! one_of (
  ($i:expr, $inp: expr) => (
    {
      if $i.is_empty() {
        $crate::IResult::Incomplete::<_, _>($crate::Needed::Size(1))
      } else {
        #[inline(always)]
        fn as_bytes<T: $crate::AsBytes>(b: &T) -> &[u8] {
          b.as_bytes()
        }

        let expected = $inp;
        let bytes = as_bytes(&expected);
        one_of_bytes!($i, bytes)
      }
    }
  );
);

#[doc(hidden)]
#[macro_export]
macro_rules! one_of_bytes (
  ($i:expr, $bytes: expr) => (
    {
      if $i.is_empty() {
        $crate::IResult::Incomplete::<_, _>($crate::Needed::Size(1))
      } else {
        let mut found = false;

        for &i in $bytes {
          if i == $i[0] {
            found = true;
            break;
          }
        }

        if found {
          $crate::IResult::Done(&$i[1..], $i[0] as char)
        } else {
          $crate::IResult::Error(error_position!($crate::ErrorKind::OneOf, $i))
        }
      }
    }
  );
);

/// matches anything but the provided characters
#[macro_export]
macro_rules! none_of (
  ($i:expr, $inp: expr) => (
    {
      if $i.is_empty() {
        $crate::IResult::Incomplete::<_, _>($crate::Needed::Size(1))
      } else {
        #[inline(always)]
        fn as_bytes<T: $crate::AsBytes>(b: &T) -> &[u8] {
          b.as_bytes()
        }

        let expected = $inp;
        let bytes = as_bytes(&expected);
        none_of_bytes!($i, bytes)
      }
    }
  );
);

#[doc(hidden)]
#[macro_export]
macro_rules! none_of_bytes (
  ($i:expr, $bytes: expr) => (
    {
      if $i.is_empty() {
        $crate::IResult::Incomplete::<_, _>($crate::Needed::Size(1))
      } else {
        let mut found = false;

        for &i in $bytes {
          if i == $i[0] {
            found = true;
            break;
          }
        }

        if !found {
          $crate::IResult::Done(&$i[1..], $i[0] as char)
        } else {
          $crate::IResult::Error(error_position!($crate::ErrorKind::NoneOf, $i))
        }
      }
    }
  );
);

/// matches one character: `char!(char) => &[u8] -> IResult<&[u8], char>
#[macro_export]
macro_rules! char (
  ($i:expr, $c: expr) => (
    {
      if $i.is_empty() {
        let res: $crate::IResult<&[u8], char> = $crate::IResult::Incomplete($crate::Needed::Size(1));
        res
      } else {
        if $i[0] == $c as u8 {
          $crate::IResult::Done(&$i[1..], $i[0] as char)
        } else {
          $crate::IResult::Error(error_position!($crate::ErrorKind::Char, $i))
        }
      }
    }
  );
);

named!(#[doc="Matches a newline character '\\n'"], pub newline<char>, char!('\n'));

named!(#[doc="Matches a tab character '\\t'"], pub tab<char>, char!('\t'));

pub fn anychar(input:&[u8]) -> IResult<&[u8], char> {
  if input.is_empty() {
    IResult::Incomplete(Needed::Size(1))
  } else {
    IResult::Done(&input[1..], input[0] as char)
  }
}

#[cfg(test)]
mod tests {
  use internal::IResult::*;
  use util::ErrorKind;

  #[test]
  fn one_of() {
    named!(f<char>, one_of!("ab"));

    let a = &b"abcd"[..];
    assert_eq!(f(a), Done(&b"bcd"[..], 'a'));

    let b = &b"cde"[..];
    assert_eq!(f(b), Error(error_position!(ErrorKind::OneOf, b)));
  }

  #[test]
  fn none_of() {
    named!(f<char>, none_of!("ab"));

    let a = &b"abcd"[..];
    assert_eq!(f(a), Error(error_position!(ErrorKind::NoneOf, a)));

    let b = &b"cde"[..];
    assert_eq!(f(b), Done(&b"de"[..], 'c'));
  }

  #[test]
  fn char() {
    named!(f<char>, char!('c'));

    let a = &b"abcd"[..];
    assert_eq!(f(a), Error(error_position!(ErrorKind::Char, a)));

    let b = &b"cde"[..];
    assert_eq!(f(b), Done(&b"de"[..], 'c'));
  }

}
<|endoftext|>


<commit_before>#![crate_type = "bin"]
#![crate_id="demo"]

extern crate sdl2;
extern crate sdl2_mixer;

use std::os;
use mix = sdl2_mixer;

fn main() {
    let args = os::args();
    if args.len() < 2 {
        println!("Usage: ./demo audio.[mp3|wav|ogg]")
    } else {
        match dump_info(&Path::new(args.get(1).to_owned())) {
            _ => ()
        }
    }
}

fn dump_info(filename: &Path) -> Result<(), ~str> {
    println!("linked version: {}", mix::get_linked_version());
    sdl2::init(sdl2::InitAudio | sdl2::InitTimer);
    println!("inited => {}", mix::init(sdl2_mixer::InitMp3 | sdl2_mixer::InitFlac |
                                       sdl2_mixer::InitMod | sdl2_mixer::InitFluidSynth |
                                       sdl2_mixer::InitModPlug | sdl2_mixer::InitOgg).bits());
    // TODO: 0x8010 is SDL_audio flag
    try!(mix::open_audio(mix::DEFAULT_FREQUENCY, 0x8010u16, 2, 1024));
    mix::allocate_channels(0);

    {
        let n = mix::get_chunk_decoders_number();
        println!("available chunk(sample) decoders: {}", n);
        for i in range(0, n) {
            println!("| decoder {} => {:?}", i, mix::get_chunk_decoder(i));
        }
    }

    {
        let n = mix::get_music_decoders_number();
        println!("available music decoders: {}", n);
        for i in range(0, n) {
            println!("| decoder {} => {:?}", i, mix::get_music_decoder(i));
        }
    }

    println!("query spec => {}", mix::query_spec());

    let music = mix::Music::from_file(filename).unwrap();

    mix::Music::hook_finished(|| { println!("play ends! from rust cb") });

    println!("music => {:?}", music);
    println!("music type => {}", music.get_type());

    println!("music volume => {}", mix::Music::get_volume());

    println!("play => {}", music.play(1));

    sdl2::timer::delay(10000);

    println!("fading out ... {}", mix::Music::fade_out(4000));

    sdl2::timer::delay(5000);

    println!("fading in from pos ... {}", music.fade_in_from_pos(1, 10000, 100.0));

    sdl2::timer::delay(5000);

    mix::Music::halt();

    sdl2::timer::delay(1000);
    // here will print hook_finished

    mix::quit();
    sdl2::quit();

    Ok(())
}
<commit_msg>fix demo compile error<commit_after>#![crate_type = "bin"]
#![crate_id="demo"]

extern crate debug;
extern crate sdl2;
extern crate sdl2_mixer;

use std::os;

fn main() {
    let args = os::args();
    if args.len() < 2 {
        println!("Usage: ./demo audio.[mp3|wav|ogg]")
    } else {
        match dump_info(&Path::new(args.get(1).to_string())) {
            _ => ()
        }
    }
}

fn dump_info(filename: &Path) -> Result<(), String> {
    println!("linked version: {}", sdl2_mixer::get_linked_version());
    sdl2::init(sdl2::InitAudio | sdl2::InitTimer);
    println!("inited => {}", sdl2_mixer::init(sdl2_mixer::InitMp3 | sdl2_mixer::InitFlac |
                                       sdl2_mixer::InitMod | sdl2_mixer::InitFluidSynth |
                                       sdl2_mixer::InitModPlug | sdl2_mixer::InitOgg).bits());
    // TODO: 0x8010 is SDL_audio flag
    try!(sdl2_mixer::open_audio(sdl2_mixer::DEFAULT_FREQUENCY, 0x8010u16, 2, 1024));
    sdl2_mixer::allocate_channels(0);

    {
        let n = sdl2_mixer::get_chunk_decoders_number();
        println!("available chunk(sample) decoders: {}", n);
        for i in range(0, n) {
            println!("| decoder {} => {:?}", i, sdl2_mixer::get_chunk_decoder(i));
        }
    }

    {
        let n = sdl2_mixer::get_music_decoders_number();
        println!("available music decoders: {}", n);
        for i in range(0, n) {
            println!("| decoder {} => {:?}", i, sdl2_mixer::get_music_decoder(i));
        }
    }

    println!("query spec => {}", sdl2_mixer::query_spec());

    let music = sdl2_mixer::Music::from_file(filename).unwrap();

    sdl2_mixer::Music::hook_finished(|| { println!("play ends! from rust cb") });

    println!("music => {:?}", music);
    println!("music type => {}", music.get_type());

    println!("music volume => {}", sdl2_mixer::Music::get_volume());

    println!("play => {}", music.play(1));

    sdl2::timer::delay(10000);

    println!("fading out ... {}", sdl2_mixer::Music::fade_out(4000));

    sdl2::timer::delay(5000);

    println!("fading in from pos ... {}", music.fade_in_from_pos(1, 10000, 100.0));

    sdl2::timer::delay(5000);

    sdl2_mixer::Music::halt();

    sdl2::timer::delay(1000);
    // here will print hook_finished

    sdl2_mixer::quit();
    sdl2::quit();

    Ok(())
}
<|endoftext|>