<commit_before>/*
 * TODO: better heuristic (https://bugzilla.gnome.org/show_bug.cgi?id=623635#c5)
 * TODO: ProgressCallback types (not specific to async).
 * TODO: add annotation for methods like g_file_replace_contents_bytes_async where the finish
 * method has a different prefix.
 */

use std::collections::HashMap;
use std::vec::Vec;

use analysis::bounds::Bounds;
use analysis::function_parameters::{self, Parameters, Transformation, TransformationType};
use analysis::out_parameters::use_function_return_for_result;
use analysis::imports::Imports;
use analysis::out_parameters;
use analysis::ref_mode::RefMode;
use analysis::return_value;
use analysis::rust_type::*;
use analysis::safety_assertion_mode::SafetyAssertionMode;
use analysis::signatures::{Signature, Signatures};
use config;
use env::Env;
use library::{self, Function, FunctionKind, Nullable, Parameter, ParameterScope, Type};
use nameutil;
use traits::*;
use version::Version;
use std::borrow::Borrow;

#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Visibility {
    Public,
    Comment,
    Private,
    Hidden,
}

impl Visibility {
    pub fn hidden(&self) -> bool {
        *self == Visibility::Hidden
    }
}

#[derive(Clone, Debug)]
pub struct AsyncTrampoline {
    pub is_method: bool,
    pub name: String,
    pub finish_func_name: String,
    pub callback_type: String,
    pub bound_name: char,
    pub output_params: Vec<Parameter>,
    pub ffi_ret: Option<Parameter>,
}

#[derive(Clone, Debug)]
pub struct AsyncFuture {
    pub is_method: bool,
    pub name: String,
    pub success_parameters: String,
    pub error_parameters: String,
}

#[derive(Debug)]
pub struct Info {
    pub name: String,
    pub glib_name: String,
    pub kind: library::FunctionKind,
    pub visibility: Visibility,
    pub type_name: Result,
    pub parameters: Parameters,
    pub ret: return_value::Info,
    pub bounds: Bounds,
    pub outs: out_parameters::Info,
    pub version: Option<Version>,
    pub deprecated_version: Option<Version>,
    pub not_version: Option<Version>,
    pub cfg_condition: Option<String>,
    pub assertion: SafetyAssertionMode,
    pub doc_hidden: bool,
    pub async: bool,
    pub trampoline: Option<AsyncTrampoline>,
    pub async_future: Option<AsyncFuture>,
}

impl Info {
    pub fn is_async_finish(&self, env: &Env) -> bool {
        let has_async_result = self.parameters.rust_parameters.iter()
            .any(|param| param.typ.full_name(&env.library) == "Gio.AsyncResult");
        self.name.ends_with("_finish") && has_async_result
    }
}

pub fn analyze<F: Borrow<library::Function>>(
    env: &Env,
    functions: &[F],
    type_tid: library::TypeId,
    in_trait: bool,
    obj: &config::gobjects::GObject,
    imports: &mut Imports,
    mut signatures: Option<&mut Signatures>,
    deps: Option<&[library::TypeId]>,
) -> Vec<Info> {
    let mut funcs = Vec::new();

    for func in functions {
        let func = func.borrow();
        let configured_functions = obj.functions.matched(&func.name);
        if configured_functions.iter().any(|f| f.ignore) {
            continue;
        }
        if env.is_totally_deprecated(func.deprecated_version) {
            continue;
        }
        let name = nameutil::mangle_keywords(&*func.name).into_owned();
        let signature_params = Signature::new(func);
        let mut not_version = None;
        if func.kind == library::FunctionKind::Method {
            if let Some(deps) = deps {
                let (has, version) = signature_params.has_in_deps(env, &name, deps);
                if has {
                    match version {
                        Some(v) if v > env.config.min_cfg_version => not_version = version,
                        _ => continue,
                    }
                }
            }
        }
        if let Some(ref mut signatures) = signatures {
            signatures.insert(name.clone(), signature_params);
        }

        let mut info = analyze_function(env, name, func, type_tid, in_trait,
                                        &configured_functions, imports);
        info.not_version = not_version;
        funcs.push(info);
    }

    funcs
}

fn analyze_function(
    env: &Env,
    name: String,
    func: &library::Function,
    type_tid: library::TypeId,
    in_trait: bool,
    configured_functions: &[&config::functions::Function],
    imports: &mut Imports,
) -> Info {
    let async = func.parameters.iter().any(|parameter| parameter.scope == ParameterScope::Async);
    let mut commented = false;
    let mut bounds: Bounds = Default::default();
    let mut to_glib_extras = HashMap::<usize, String>::new();
    let mut used_types: Vec<String> = Vec::with_capacity(4);
    let mut trampoline = None;
    let mut async_future = None;

    let version = configured_functions
        .iter()
        .filter_map(|f| f.version)
        .min()
        .or(func.version);
    let version = env.config.filter_version(version);
    let deprecated_version = func.deprecated_version;
    let cfg_condition = configured_functions
        .iter()
        .filter_map(|f| f.cfg_condition.clone())
        .next();
    let doc_hidden = configured_functions.iter().any(|f| f.doc_hidden);
    let disable_length_detect = configured_functions.iter().any(|f| f.disable_length_detect);

    let ret = return_value::analyze(
        env,
        func,
        type_tid,
        configured_functions,
        &mut used_types,
        imports,
    );
    commented |= ret.commented;

    let mut parameters = function_parameters::analyze(
        env,
        &func.parameters,
        configured_functions,
        disable_length_detect,
        async,
        in_trait
    );
    parameters.analyze_return(env, &ret.parameter);

    for (pos, par) in parameters.c_parameters.iter().enumerate() {
        assert!(
            !par.instance_parameter || pos == 0,
            "Wrong instance parameter in {}",
            func.c_identifier.as_ref().unwrap()
        );
        if let Ok(s) = used_rust_type(env, par.typ) {
            used_types.push(s);
        }
        let (to_glib_extra, type_string) = bounds.add_for_parameter(env, func, par, async);
        if let Some(to_glib_extra) = to_glib_extra {
            to_glib_extras.insert(pos, to_glib_extra);
        }
        if let Some((callback_type, success_parameters, error_parameters, bound_name)) = type_string {
            // Checks for /*Ignored*/ or other error comments
            if callback_type.find("/*").is_some() {
                commented = true;
            }
            let func_name = func.c_identifier.as_ref().unwrap();
            let finish_func_name = finish_function_name(func_name);
            let mut output_params = vec![];
            let mut ffi_ret = None;
            if let Some(function) = find_function(env, &finish_func_name) {
                if use_function_return_for_result(env, &function.ret) {
                    ffi_ret = Some(function.ret.clone());
                }

                output_params.extend(function.parameters.clone());
                for param in &mut output_params {
                    if nameutil::needs_mangling(&param.name) {
                        param.name = nameutil::mangle_keywords(&*param.name).into_owned();
                    }
                }
            }
            trampoline = Some(AsyncTrampoline {
                is_method: func.kind == FunctionKind::Method,
                name: format!("{}_trampoline", func.name),
                finish_func_name,
                callback_type,
                bound_name,
                output_params,
                ffi_ret,
            });

            async_future = Some(AsyncFuture {
                is_method: func.kind == FunctionKind::Method,
                name: format!("{}_future", func.name),
                success_parameters,
                error_parameters,
            });
        }
        let type_error =
            !(async && *env.library.type_(par.typ) == Type::Fundamental(library::Fundamental::Pointer)) &&
            parameter_rust_type(env, par.typ, par.direction, Nullable(false), RefMode::None)
                .is_err();
        if type_error {
            commented = true;
        }
    }

    if async && trampoline.is_none() {
        commented = true;
    }

    for par in &parameters.rust_parameters {
        // Disallow fundamental arrays without length
        let is_len_for_par = |t: &&Transformation| {
            if let TransformationType::Length { ref array_name, .. } = t.transformation_type {
                array_name == &par.name
            } else {
                false
            }
        };
        if is_carray_with_direct_elements(env, par.typ)
            && parameters
                .transformations
                .iter()
                .find(is_len_for_par)
                .is_none()
        {
            commented = true;
        }
    }

    let (outs, unsupported_outs) = out_parameters::analyze(env, func, configured_functions);
    if unsupported_outs {
        warn!(
            "Function {} has unsupported outs",
            func.c_identifier.as_ref().unwrap_or(&func.name)
        );
        commented = true;
    } else if !outs.is_empty() && !commented {
        out_parameters::analyze_imports(env, func, imports);
    }

    if async && !commented {
        if env.config.library_name != "Gio" {
            imports.add("gio_ffi", version);
            imports.add_with_constraint("gio", version, Some("futures"));
        }
        imports.add("glib_ffi", None);
        imports.add("gobject_ffi", None);
    }

    if async && !commented {
        imports.add_with_constraint("futures_core", version, Some("futures"));
        imports.add_with_constraint("std::boxed::Box as Box_", version, Some("futures"));
    }

    if !commented {
        for transformation in &mut parameters.transformations {
            if let Some(to_glib_extra) = to_glib_extras.get(&transformation.ind_c) {
                transformation
                    .transformation_type
                    .set_to_glib_extra(to_glib_extra);
            }
        }

        imports.add_used_types(&used_types, version);
        if ret.base_tid.is_some() {
            imports.add("glib::object::Downcast", None);
        }
        bounds.update_imports(imports);
    }

    let visibility = if commented || (async && trampoline.is_none()) {
        Visibility::Comment
    } else {
        Visibility::Public
    };
    let is_method = func.kind == library::FunctionKind::Method;
    let assertion = SafetyAssertionMode::of(env, is_method, &parameters);

    Info {
        name,
        glib_name: func.c_identifier.as_ref().unwrap().clone(),
        kind: func.kind,
        visibility,
        type_name: rust_type(env, type_tid),
        parameters,
        ret,
        bounds,
        outs,
        version,
        deprecated_version,
        not_version: None,
        cfg_condition,
        assertion,
        doc_hidden,
        async,
        trampoline,
        async_future,
    }
}

pub fn is_carray_with_direct_elements(env: &Env, typ: library::TypeId) -> bool {
    match *env.library.type_(typ) {
        Type::CArray(inner_tid) => {
            use super::conversion_type::ConversionType;
            match *env.library.type_(inner_tid) {
                Type::Fundamental(..)
                    if ConversionType::of(env, inner_tid) == ConversionType::Direct =>
                {
                    true
                }
                _ => false,
            }
        }
        _ => false,
    }
}

pub fn find_function<'a>(env: &'a Env, c_identifier: &str) -> Option<&'a Function> {
    let find = |functions: &'a [Function]| -> Option<&'a Function> {
        for function in functions {
            if let Some(ref func_c_identifier) = function.c_identifier {
                if func_c_identifier == c_identifier {
                    return Some(function);
                }
            }
        }
        None
    };

    if let Some(index) = env.library.find_namespace(&env.config.library_name) {
        let namespace = env.library.namespace(index);
        if let Some(f) = find(&namespace.functions) {
            return Some(f);
        }
        for typ in &namespace.types {
            if let Some(Type::Class(ref class)) = *typ {
                if let Some(f) = find(&class.functions) {
                    return Some(f);
                }
            } else if let Some(Type::Interface(ref interface)) = *typ {
                if let Some(f) = find(&interface.functions) {
                    return Some(f);
                }
            }
        }
    }
    None
}

/// Given async function name tries to guess the name of finish function.
pub fn finish_function_name(mut func_name: &str) -> String {
    if func_name.ends_with("_async") {
        let len = func_name.len() - "_async".len();
        func_name = &func_name[0..len];
    }
    format!("{}_finish", &func_name)
}

pub fn find_index_to_ignore(parameters: &[Parameter]) -> Option<usize> {
    parameters.iter()
        .find(|param| param.array_length.is_some())
        .and_then(|param| param.array_length.map(|length| length as usize))
}

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

    #[test]
    fn test_finish_function_name() {
        assert_eq!("g_file_copy_finish", &finish_function_name("g_file_copy_async"));
        assert_eq!("g_bus_get_finish", &finish_function_name("g_bus_get"));
    }
}
<commit_msg>Use declarations for finish function output parameters.<commit_after>/*
 * TODO: better heuristic (https://bugzilla.gnome.org/show_bug.cgi?id=623635#c5)
 * TODO: ProgressCallback types (not specific to async).
 * TODO: add annotation for methods like g_file_replace_contents_bytes_async where the finish
 * method has a different prefix.
 */

use std::collections::HashMap;
use std::vec::Vec;

use analysis::bounds::Bounds;
use analysis::function_parameters::{self, Parameters, Transformation, TransformationType};
use analysis::out_parameters::use_function_return_for_result;
use analysis::imports::Imports;
use analysis::out_parameters;
use analysis::ref_mode::RefMode;
use analysis::return_value;
use analysis::rust_type::*;
use analysis::safety_assertion_mode::SafetyAssertionMode;
use analysis::signatures::{Signature, Signatures};
use config;
use env::Env;
use library::{self, Function, FunctionKind, Nullable, Parameter, ParameterScope, Type};
use nameutil;
use traits::*;
use version::Version;
use std::borrow::Borrow;

#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Visibility {
    Public,
    Comment,
    Private,
    Hidden,
}

impl Visibility {
    pub fn hidden(&self) -> bool {
        *self == Visibility::Hidden
    }
}

#[derive(Clone, Debug)]
pub struct AsyncTrampoline {
    pub is_method: bool,
    pub name: String,
    pub finish_func_name: String,
    pub callback_type: String,
    pub bound_name: char,
    pub output_params: Vec<Parameter>,
    pub ffi_ret: Option<Parameter>,
}

#[derive(Clone, Debug)]
pub struct AsyncFuture {
    pub is_method: bool,
    pub name: String,
    pub success_parameters: String,
    pub error_parameters: String,
}

#[derive(Debug)]
pub struct Info {
    pub name: String,
    pub glib_name: String,
    pub kind: library::FunctionKind,
    pub visibility: Visibility,
    pub type_name: Result,
    pub parameters: Parameters,
    pub ret: return_value::Info,
    pub bounds: Bounds,
    pub outs: out_parameters::Info,
    pub version: Option<Version>,
    pub deprecated_version: Option<Version>,
    pub not_version: Option<Version>,
    pub cfg_condition: Option<String>,
    pub assertion: SafetyAssertionMode,
    pub doc_hidden: bool,
    pub async: bool,
    pub trampoline: Option<AsyncTrampoline>,
    pub async_future: Option<AsyncFuture>,
}

impl Info {
    pub fn is_async_finish(&self, env: &Env) -> bool {
        let has_async_result = self.parameters.rust_parameters.iter()
            .any(|param| param.typ.full_name(&env.library) == "Gio.AsyncResult");
        self.name.ends_with("_finish") && has_async_result
    }
}

pub fn analyze<F: Borrow<library::Function>>(
    env: &Env,
    functions: &[F],
    type_tid: library::TypeId,
    in_trait: bool,
    obj: &config::gobjects::GObject,
    imports: &mut Imports,
    mut signatures: Option<&mut Signatures>,
    deps: Option<&[library::TypeId]>,
) -> Vec<Info> {
    let mut funcs = Vec::new();

    for func in functions {
        let func = func.borrow();
        let configured_functions = obj.functions.matched(&func.name);
        if configured_functions.iter().any(|f| f.ignore) {
            continue;
        }
        if env.is_totally_deprecated(func.deprecated_version) {
            continue;
        }
        let name = nameutil::mangle_keywords(&*func.name).into_owned();
        let signature_params = Signature::new(func);
        let mut not_version = None;
        if func.kind == library::FunctionKind::Method {
            if let Some(deps) = deps {
                let (has, version) = signature_params.has_in_deps(env, &name, deps);
                if has {
                    match version {
                        Some(v) if v > env.config.min_cfg_version => not_version = version,
                        _ => continue,
                    }
                }
            }
        }
        if let Some(ref mut signatures) = signatures {
            signatures.insert(name.clone(), signature_params);
        }

        let mut info = analyze_function(env, name, func, type_tid, in_trait,
                                        &configured_functions, imports);
        info.not_version = not_version;
        funcs.push(info);
    }

    funcs
}

fn analyze_function(
    env: &Env,
    name: String,
    func: &library::Function,
    type_tid: library::TypeId,
    in_trait: bool,
    configured_functions: &[&config::functions::Function],
    imports: &mut Imports,
) -> Info {
    let async = func.parameters.iter().any(|parameter| parameter.scope == ParameterScope::Async);
    let mut commented = false;
    let mut bounds: Bounds = Default::default();
    let mut to_glib_extras = HashMap::<usize, String>::new();
    let mut used_types: Vec<String> = Vec::with_capacity(4);
    let mut trampoline = None;
    let mut async_future = None;

    let version = configured_functions
        .iter()
        .filter_map(|f| f.version)
        .min()
        .or(func.version);
    let version = env.config.filter_version(version);
    let deprecated_version = func.deprecated_version;
    let cfg_condition = configured_functions
        .iter()
        .filter_map(|f| f.cfg_condition.clone())
        .next();
    let doc_hidden = configured_functions.iter().any(|f| f.doc_hidden);
    let disable_length_detect = configured_functions.iter().any(|f| f.disable_length_detect);

    let ret = return_value::analyze(
        env,
        func,
        type_tid,
        configured_functions,
        &mut used_types,
        imports,
    );
    commented |= ret.commented;

    let mut parameters = function_parameters::analyze(
        env,
        &func.parameters,
        configured_functions,
        disable_length_detect,
        async,
        in_trait
    );
    parameters.analyze_return(env, &ret.parameter);

    for (pos, par) in parameters.c_parameters.iter().enumerate() {
        assert!(
            !par.instance_parameter || pos == 0,
            "Wrong instance parameter in {}",
            func.c_identifier.as_ref().unwrap()
        );
        if let Ok(s) = used_rust_type(env, par.typ) {
            used_types.push(s);
        }
        let (to_glib_extra, type_string) = bounds.add_for_parameter(env, func, par, async);
        if let Some(to_glib_extra) = to_glib_extra {
            to_glib_extras.insert(pos, to_glib_extra);
        }
        if let Some((callback_type, success_parameters, error_parameters, bound_name)) = type_string {
            // Checks for /*Ignored*/ or other error comments
            if callback_type.find("/*").is_some() {
                commented = true;
            }
            let func_name = func.c_identifier.as_ref().unwrap();
            let finish_func_name = finish_function_name(func_name);
            let mut output_params = vec![];
            let mut ffi_ret = None;
            if let Some(function) = find_function(env, &finish_func_name) {
                if use_function_return_for_result(env, &function.ret) {
                    ffi_ret = Some(function.ret.clone());
                }

                output_params.extend(function.parameters.clone());
                for param in &mut output_params {
                    if nameutil::needs_mangling(&param.name) {
                        param.name = nameutil::mangle_keywords(&*param.name).into_owned();
                    }
                }
            }
            trampoline = Some(AsyncTrampoline {
                is_method: func.kind == FunctionKind::Method,
                name: format!("{}_trampoline", func.name),
                finish_func_name,
                callback_type,
                bound_name,
                output_params,
                ffi_ret,
            });

            async_future = Some(AsyncFuture {
                is_method: func.kind == FunctionKind::Method,
                name: format!("{}_future", func.name),
                success_parameters,
                error_parameters,
            });
        }
        let type_error =
            !(async && *env.library.type_(par.typ) == Type::Fundamental(library::Fundamental::Pointer)) &&
            parameter_rust_type(env, par.typ, par.direction, Nullable(false), RefMode::None)
                .is_err();
        if type_error {
            commented = true;
        }
    }

    if async && trampoline.is_none() {
        commented = true;
    }

    for par in &parameters.rust_parameters {
        // Disallow fundamental arrays without length
        let is_len_for_par = |t: &&Transformation| {
            if let TransformationType::Length { ref array_name, .. } = t.transformation_type {
                array_name == &par.name
            } else {
                false
            }
        };
        if is_carray_with_direct_elements(env, par.typ)
            && parameters
                .transformations
                .iter()
                .find(is_len_for_par)
                .is_none()
        {
            commented = true;
        }
    }

    let (outs, unsupported_outs) = out_parameters::analyze(env, func, configured_functions);
    if unsupported_outs {
        warn!(
            "Function {} has unsupported outs",
            func.c_identifier.as_ref().unwrap_or(&func.name)
        );
        commented = true;
    } else if !outs.is_empty() && !commented {
        out_parameters::analyze_imports(env, func, imports);
    }

    if async && !commented {
        if env.config.library_name != "Gio" {
            imports.add("gio_ffi", version);
            imports.add_with_constraint("gio", version, Some("futures"));
        }
        imports.add("glib_ffi", None);
        imports.add("gobject_ffi", None);
        imports.add_with_constraint("futures_core", version, Some("futures"));
        imports.add_with_constraint("std::boxed::Box as Box_", version, Some("futures"));

        if let Some(ref trampoline) = trampoline {
            for par in &trampoline.output_params {
                if let Ok(s) = used_rust_type(env, par.typ) {
                    used_types.push(s);
                }
            }
            if let Some(ref par) = trampoline.ffi_ret {
                if let Ok(s) = used_rust_type(env, par.typ) {
                    used_types.push(s);
                }
            }
        }
    }

    if !commented {
        for transformation in &mut parameters.transformations {
            if let Some(to_glib_extra) = to_glib_extras.get(&transformation.ind_c) {
                transformation
                    .transformation_type
                    .set_to_glib_extra(to_glib_extra);
            }
        }

        imports.add_used_types(&used_types, version);
        if ret.base_tid.is_some() {
            imports.add("glib::object::Downcast", None);
        }
        bounds.update_imports(imports);
    }

    let visibility = if commented {
        Visibility::Comment
    } else {
        Visibility::Public
    };
    let is_method = func.kind == library::FunctionKind::Method;
    let assertion = SafetyAssertionMode::of(env, is_method, &parameters);

    Info {
        name,
        glib_name: func.c_identifier.as_ref().unwrap().clone(),
        kind: func.kind,
        visibility,
        type_name: rust_type(env, type_tid),
        parameters,
        ret,
        bounds,
        outs,
        version,
        deprecated_version,
        not_version: None,
        cfg_condition,
        assertion,
        doc_hidden,
        async,
        trampoline,
        async_future,
    }
}

pub fn is_carray_with_direct_elements(env: &Env, typ: library::TypeId) -> bool {
    match *env.library.type_(typ) {
        Type::CArray(inner_tid) => {
            use super::conversion_type::ConversionType;
            match *env.library.type_(inner_tid) {
                Type::Fundamental(..)
                    if ConversionType::of(env, inner_tid) == ConversionType::Direct =>
                {
                    true
                }
                _ => false,
            }
        }
        _ => false,
    }
}

pub fn find_function<'a>(env: &'a Env, c_identifier: &str) -> Option<&'a Function> {
    let find = |functions: &'a [Function]| -> Option<&'a Function> {
        for function in functions {
            if let Some(ref func_c_identifier) = function.c_identifier {
                if func_c_identifier == c_identifier {
                    return Some(function);
                }
            }
        }
        None
    };

    if let Some(index) = env.library.find_namespace(&env.config.library_name) {
        let namespace = env.library.namespace(index);
        if let Some(f) = find(&namespace.functions) {
            return Some(f);
        }
        for typ in &namespace.types {
            if let Some(Type::Class(ref class)) = *typ {
                if let Some(f) = find(&class.functions) {
                    return Some(f);
                }
            } else if let Some(Type::Interface(ref interface)) = *typ {
                if let Some(f) = find(&interface.functions) {
                    return Some(f);
                }
            }
        }
    }
    None
}

/// Given async function name tries to guess the name of finish function.
pub fn finish_function_name(mut func_name: &str) -> String {
    if func_name.ends_with("_async") {
        let len = func_name.len() - "_async".len();
        func_name = &func_name[0..len];
    }
    format!("{}_finish", &func_name)
}

pub fn find_index_to_ignore(parameters: &[Parameter]) -> Option<usize> {
    parameters.iter()
        .find(|param| param.array_length.is_some())
        .and_then(|param| param.array_length.map(|length| length as usize))
}

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

    #[test]
    fn test_finish_function_name() {
        assert_eq!("g_file_copy_finish", &finish_function_name("g_file_copy_async"));
        assert_eq!("g_bus_get_finish", &finish_function_name("g_bus_get"));
    }
}
<|endoftext|>


<commit_before>//! The procedural macro for vulkano's shader system.
//! Manages the compile-time compilation of GLSL into SPIR-V and generation of assosciated rust code.
//!
//! # Basic usage
//!
//! ```
//! mod vs {
//!     vulkano_shaders::shader!{
//!         ty: "vertex",
//!         src: "
//! #version 450
//!
//! layout(location = 0) in vec3 position;
//!
//! void main() {
//!     gl_Position = vec4(position, 1.0);
//! }"
//!     }
//! }
//! # fn main() {}
//! ```
//!
//! # Details
//!
//! If you want to take a look at what the macro generates, your best options
//! are to either read through the code that handles the generation (the
//! [`reflect`][reflect] function in the `vulkano-shaders` crate) or use a tool
//! such as [cargo-expand][cargo-expand] to view the expansion of the macro in your
//! own code. It is unfortunately not possible to provide a `generated_example`
//! module like some normal macro crates do since derive macros cannot be used from
//! the crate they are declared in. On the other hand, if you are looking for a
//! high-level overview, you can see the below section.
//!
//! # Generated code overview
//!
//! The macro generates the following items of interest:
//! * The `Shader` struct. This contains a single field, `shader`, which is an
//! `Arc<ShaderModule>`.
//! * The `Shader::load` constructor. This method takes an `Arc<Device>`, calls
//! [`ShaderModule::new`][ShaderModule::new] with the passed-in device and the
//! shader data provided via the macro, and returns `Result<Shader, OomError>`.
//! Before doing so, it loops through every capability instruction in the shader
//! data, verifying that the passed-in `Device` has the appropriate features
//! enabled. **This function currently panics if a feature required by the shader
//! is not enabled on the device.** At some point in the future it will return
//! an error instead.
//! * The `Shader::module` method. This method simply returns a reference to the
//! `Arc<ShaderModule>` contained within the `shader` field of the `Shader`
//! struct.
//! * Methods for each entry point of the shader module. These construct and
//! return the various entry point structs that can be found in the
//! [vulkano::pipeline::shader][pipeline::shader] module.
//! * A Rust struct translated from each struct contained in the shader data.
//! * The `Layout` newtype. This contains a [`ShaderStages`][ShaderStages] struct.
//! An implementation of [`PipelineLayoutDesc`][PipelineLayoutDesc] is also
//! generated for the newtype.
//! * The `SpecializationConstants` struct. This contains a field for every
//! specialization constant found in the shader data. Implementations of
//! `Default` and [`SpecializationConstants`][SpecializationConstants] are also
//! generated for the struct.
//!
//! All of these generated items will be accessed through the module specified
//! by `mod_name: foo` If you wanted to store the `Shader` in a struct of your own,
//! you could do something like this:
//!
//! ```
//! # fn main() {}
//! # use std::sync::Arc;
//! # use vulkano::OomError;
//! # use vulkano::device::Device;
//! #
//! # mod vs {
//! #     vulkano_shaders::shader!{
//! #         ty: "vertex",
//! #         src: "
//! # #version 450
//! #
//! # layout(location = 0) in vec3 position;
//! #
//! # void main() {
//! #     gl_Position = vec4(position, 1.0);
//! # }"
//! #     }
//! # }
//! // various use statements
//! // `vertex_shader` module with shader derive
//!
//! pub struct Shaders {
//!     pub vs: vs::Shader
//! }
//!
//! impl Shaders {
//!     pub fn load(device: Arc<Device>) -> Result<Self, OomError> {
//!         Ok(Self {
//!             vs: vs::Shader::load(device)?,
//!         })
//!     }
//! }
//! ```
//!
//! # Options
//!
//! The options available are in the form of the following attributes:
//!
//! ## `ty: "..."`
//!
//! This defines what shader type the given GLSL source will be compiled into.
//! The type can be any of the following:
//!
//! * `vertex`
//! * `fragment`
//! * `geometry`
//! * `tess_ctrl`
//! * `tess_eval`
//! * `compute`
//!
//! For details on what these shader types mean, [see Vulkano's documentation][pipeline].
//!
//! ## `src: "..."`
//!
//! Provides the raw GLSL source to be compiled in the form of a string. Cannot
//! be used in conjunction with the `path` or `bytes` field.
//!
//! ## `path: "..."`
//!
//! Provides the path to the GLSL source to be compiled, relative to `Cargo.toml`.
//! Cannot be used in conjunction with the `src` or `bytes` field.
//!
//! ## `bytes: "..."`
//!
//! Provides the path to precompiled SPIR-V bytecode, relative to `Cargo.toml`.
//! Cannot be used in conjunction with the `src` or `bytes` field.
//! This allows using shaders compiled through a separate build system.
//!
//! ## `include: ["...", "...", ..., "..."]`
//!
//! Specifies the standard include directories to be searched through when using the
//! `#include <...>` directive within a shader source. Include directories can be absolute
//! or relative to `Cargo.toml`.
//! If `path` was specified, relative paths can also be used (`#include "..."`), without the need
//! to specify one or more standard include directories. Relative paths are relative to the
//! directory, which contains the source file the `#include "..."` directive is declared in.
//!
//! ## `define: [("NAME", "VALUE"), ...]`
//!
//! Adds the given macro definitions to the pre-processor. This is equivalent to passing `-DNAME=VALUE`
//! on the command line.
//!
//! ## `dump: true`
//!
//! The crate fails to compile but prints the generated rust code to stdout.
//!
//! [reflect]: https://github.com/vulkano-rs/vulkano/blob/master/vulkano-shaders/src/lib.rs#L67
//! [cargo-expand]: https://github.com/dtolnay/cargo-expand
//! [ShaderModule::new]: https://docs.rs/vulkano/*/vulkano/pipeline/shader/struct.ShaderModule.html#method.new
//! [OomError]: https://docs.rs/vulkano/*/vulkano/enum.OomError.html
//! [pipeline::shader]: https://docs.rs/vulkano/*/vulkano/pipeline/shader/index.html
//! [descriptor]: https://docs.rs/vulkano/*/vulkano/descriptor/index.html
//! [ShaderStages]: https://docs.rs/vulkano/*/vulkano/descriptor/descriptor/struct.ShaderStages.html
//! [PipelineLayoutDesc]: https://docs.rs/vulkano/*/vulkano/descriptor/pipeline_layout/trait.PipelineLayoutDesc.html
//! [SpecializationConstants]: https://docs.rs/vulkano/*/vulkano/pipeline/shader/trait.SpecializationConstants.html
//! [pipeline]: https://docs.rs/vulkano/*/vulkano/pipeline/index.html

#![doc(html_logo_url = "https://raw.githubusercontent.com/vulkano-rs/vulkano/master/logo.png")]
#![recursion_limit = "1024"]
#[macro_use]
extern crate quote;
#[macro_use]
extern crate syn;
extern crate proc_macro;

use std::env;
use std::fs;
use std::fs::File;
use std::io::{Read, Result as IoResult};
use std::path::Path;

use syn::parse::{Parse, ParseStream, Result};
use syn::{Ident, LitBool, LitStr};

mod codegen;
mod descriptor_sets;
mod entry_point;
mod enums;
mod parse;
mod spec_consts;
mod spirv_search;
mod structs;

use crate::codegen::ShaderKind;
use std::slice::from_raw_parts;

enum SourceKind {
    Src(String),
    Path(String),
    Bytes(String),
}

struct MacroInput {
    shader_kind: ShaderKind,
    source_kind: SourceKind,
    include_directories: Vec<String>,
    macro_defines: Vec<(String, String)>,
    dump: bool,
}

impl Parse for MacroInput {
    fn parse(input: ParseStream) -> Result<Self> {
        let mut dump = None;
        let mut shader_kind = None;
        let mut source_kind = None;
        let mut include_directories = Vec::new();
        let mut macro_defines = Vec::new();

        while !input.is_empty() {
            let name: Ident = input.parse()?;
            input.parse::<Token![:]>()?;

            match name.to_string().as_ref() {
                "ty" => {
                    if shader_kind.is_some() {
                        panic!("Only one `ty` can be defined")
                    }

                    let ty: LitStr = input.parse()?;
                    let ty = match ty.value().as_ref() {
                        "vertex" => ShaderKind::Vertex,
                        "fragment" => ShaderKind::Fragment,
                        "geometry" => ShaderKind::Geometry,
                        "tess_ctrl" => ShaderKind::TessControl,
                        "tess_eval" => ShaderKind::TessEvaluation,
                        "compute" => ShaderKind::Compute,
                        _ => panic!("Unexpected shader type, valid values: vertex, fragment, geometry, tess_ctrl, tess_eval, compute")
                    };
                    shader_kind = Some(ty);
                }
                "src" => {
                    if source_kind.is_some() {
                        panic!("Only one of `src`, `path`, or `bytes` can be defined")
                    }

                    let src: LitStr = input.parse()?;
                    source_kind = Some(SourceKind::Src(src.value()));
                }
                "path" => {
                    if source_kind.is_some() {
                        panic!("Only one of `src`, `path`, or `bytes` can be defined")
                    }

                    let path: LitStr = input.parse()?;
                    source_kind = Some(SourceKind::Path(path.value()));
                }
                "bytes" => {
                    if source_kind.is_some() {
                        panic!("Only one of `src`, `path`, or `bytes` can be defined")
                    }

                    let path: LitStr = input.parse()?;
                    source_kind = Some(SourceKind::Bytes(path.value()));
                }
                "define" => {
                    let array_input;
                    bracketed!(array_input in input);

                    while !array_input.is_empty() {
                        let tuple_input;
                        parenthesized!(tuple_input in array_input);

                        let name: LitStr = tuple_input.parse()?;
                        tuple_input.parse::<Token![,]>()?;
                        let value: LitStr = tuple_input.parse()?;
                        macro_defines.push((name.value(), value.value()));

                        if !array_input.is_empty() {
                            array_input.parse::<Token![,]>()?;
                        }
                    }
                }
                "include" => {
                    let in_brackets;
                    bracketed!(in_brackets in input);

                    while !in_brackets.is_empty() {
                        let path: LitStr = in_brackets.parse()?;

                        include_directories.push(path.value());

                        if !in_brackets.is_empty() {
                            in_brackets.parse::<Token![,]>()?;
                        }
                    }
                }
                "dump" => {
                    if dump.is_some() {
                        panic!("Only one `dump` can be defined")
                    }
                    let dump_lit: LitBool = input.parse()?;
                    dump = Some(dump_lit.value);
                }
                name => panic!(format!("Unknown field name: {}", name)),
            }

            if !input.is_empty() {
                input.parse::<Token![,]>()?;
            }
        }

        let shader_kind = match shader_kind {
            Some(shader_kind) => shader_kind,
            None => panic!("Please provide a shader type e.g. `ty: \"vertex\"`"),
        };

        let source_kind = match source_kind {
            Some(source_kind) => source_kind,
            None => panic!("Please provide a source e.g. `path: \"foo.glsl\"` or `src: \"glsl source code here ...\"`")
        };

        let dump = dump.unwrap_or(false);

        Ok(MacroInput {
            shader_kind,
            source_kind,
            include_directories,
            dump,
            macro_defines,
        })
    }
}

pub(self) fn read_file_to_string(full_path: &Path) -> IoResult<String> {
    let mut buf = String::new();
    File::open(full_path).and_then(|mut file| file.read_to_string(&mut buf))?;
    Ok(buf)
}

#[proc_macro]
pub fn shader(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
    let input = parse_macro_input!(input as MacroInput);

    let root = env::var("CARGO_MANIFEST_DIR").unwrap_or(".".into());
    let root_path = Path::new(&root);

    if let SourceKind::Bytes(path) = input.source_kind {
        let full_path = root_path.join(&path);

        let bytes = if full_path.is_file() {
            fs::read(full_path).expect(&format!("Error reading source from {:?}", path))
        } else {
            panic!(
                "File {:?} was not found ; note that the path must be relative to your Cargo.toml",
                path
            );
        };

        // The SPIR-V specification essentially guarantees that
        // a shader will always be an integer number of words
        assert_eq!(0, bytes.len() % 4);
        codegen::reflect(
            "Shader",
            unsafe { from_raw_parts(bytes.as_slice().as_ptr() as *const u32, bytes.len() / 4) },
            input.dump,
        )
        .unwrap()
        .into()
    } else {
        let (path, source_code) = match input.source_kind {
            SourceKind::Src(source) => (None, source),
            SourceKind::Path(path) => (Some(path.clone()), {
                let full_path = root_path.join(&path);

                if full_path.is_file() {
                    read_file_to_string(&full_path)
                        .expect(&format!("Error reading source from {:?}", path))
                } else {
                    panic!("File {:?} was not found ; note that the path must be relative to your Cargo.toml", path);
                }
            }),
            SourceKind::Bytes(_) => unreachable!(),
        };

        let include_paths = input
            .include_directories
            .iter()
            .map(|include_directory| {
                let include_path = Path::new(include_directory);
                let mut full_include_path = root_path.to_owned();
                full_include_path.push(include_path);
                full_include_path
            })
            .collect::<Vec<_>>();

        let content = match codegen::compile(
            path,
            &root_path,
            &source_code,
            input.shader_kind,
            &include_paths,
            &input.macro_defines,
        ) {
            Ok(ok) => ok,
            Err(e) => panic!(e.replace("(s): ", "(s):\n")),
        };

        codegen::reflect("Shader", content.as_binary(), input.dump)
            .unwrap()
            .into()
    }
}
<commit_msg>Fix vulkano-shaders bytes documentation (#1458)<commit_after>//! The procedural macro for vulkano's shader system.
//! Manages the compile-time compilation of GLSL into SPIR-V and generation of assosciated rust code.
//!
//! # Basic usage
//!
//! ```
//! mod vs {
//!     vulkano_shaders::shader!{
//!         ty: "vertex",
//!         src: "
//! #version 450
//!
//! layout(location = 0) in vec3 position;
//!
//! void main() {
//!     gl_Position = vec4(position, 1.0);
//! }"
//!     }
//! }
//! # fn main() {}
//! ```
//!
//! # Details
//!
//! If you want to take a look at what the macro generates, your best options
//! are to either read through the code that handles the generation (the
//! [`reflect`][reflect] function in the `vulkano-shaders` crate) or use a tool
//! such as [cargo-expand][cargo-expand] to view the expansion of the macro in your
//! own code. It is unfortunately not possible to provide a `generated_example`
//! module like some normal macro crates do since derive macros cannot be used from
//! the crate they are declared in. On the other hand, if you are looking for a
//! high-level overview, you can see the below section.
//!
//! # Generated code overview
//!
//! The macro generates the following items of interest:
//! * The `Shader` struct. This contains a single field, `shader`, which is an
//! `Arc<ShaderModule>`.
//! * The `Shader::load` constructor. This method takes an `Arc<Device>`, calls
//! [`ShaderModule::new`][ShaderModule::new] with the passed-in device and the
//! shader data provided via the macro, and returns `Result<Shader, OomError>`.
//! Before doing so, it loops through every capability instruction in the shader
//! data, verifying that the passed-in `Device` has the appropriate features
//! enabled. **This function currently panics if a feature required by the shader
//! is not enabled on the device.** At some point in the future it will return
//! an error instead.
//! * The `Shader::module` method. This method simply returns a reference to the
//! `Arc<ShaderModule>` contained within the `shader` field of the `Shader`
//! struct.
//! * Methods for each entry point of the shader module. These construct and
//! return the various entry point structs that can be found in the
//! [vulkano::pipeline::shader][pipeline::shader] module.
//! * A Rust struct translated from each struct contained in the shader data.
//! * The `Layout` newtype. This contains a [`ShaderStages`][ShaderStages] struct.
//! An implementation of [`PipelineLayoutDesc`][PipelineLayoutDesc] is also
//! generated for the newtype.
//! * The `SpecializationConstants` struct. This contains a field for every
//! specialization constant found in the shader data. Implementations of
//! `Default` and [`SpecializationConstants`][SpecializationConstants] are also
//! generated for the struct.
//!
//! All of these generated items will be accessed through the module specified
//! by `mod_name: foo` If you wanted to store the `Shader` in a struct of your own,
//! you could do something like this:
//!
//! ```
//! # fn main() {}
//! # use std::sync::Arc;
//! # use vulkano::OomError;
//! # use vulkano::device::Device;
//! #
//! # mod vs {
//! #     vulkano_shaders::shader!{
//! #         ty: "vertex",
//! #         src: "
//! # #version 450
//! #
//! # layout(location = 0) in vec3 position;
//! #
//! # void main() {
//! #     gl_Position = vec4(position, 1.0);
//! # }"
//! #     }
//! # }
//! // various use statements
//! // `vertex_shader` module with shader derive
//!
//! pub struct Shaders {
//!     pub vs: vs::Shader
//! }
//!
//! impl Shaders {
//!     pub fn load(device: Arc<Device>) -> Result<Self, OomError> {
//!         Ok(Self {
//!             vs: vs::Shader::load(device)?,
//!         })
//!     }
//! }
//! ```
//!
//! # Options
//!
//! The options available are in the form of the following attributes:
//!
//! ## `ty: "..."`
//!
//! This defines what shader type the given GLSL source will be compiled into.
//! The type can be any of the following:
//!
//! * `vertex`
//! * `fragment`
//! * `geometry`
//! * `tess_ctrl`
//! * `tess_eval`
//! * `compute`
//!
//! For details on what these shader types mean, [see Vulkano's documentation][pipeline].
//!
//! ## `src: "..."`
//!
//! Provides the raw GLSL source to be compiled in the form of a string. Cannot
//! be used in conjunction with the `path` or `bytes` field.
//!
//! ## `path: "..."`
//!
//! Provides the path to the GLSL source to be compiled, relative to `Cargo.toml`.
//! Cannot be used in conjunction with the `src` or `bytes` field.
//!
//! ## `bytes: "..."`
//!
//! Provides the path to precompiled SPIR-V bytecode, relative to `Cargo.toml`.
//! Cannot be used in conjunction with the `src` or `path` field.
//! This allows using shaders compiled through a separate build system.
//!
//! ## `include: ["...", "...", ..., "..."]`
//!
//! Specifies the standard include directories to be searched through when using the
//! `#include <...>` directive within a shader source. Include directories can be absolute
//! or relative to `Cargo.toml`.
//! If `path` was specified, relative paths can also be used (`#include "..."`), without the need
//! to specify one or more standard include directories. Relative paths are relative to the
//! directory, which contains the source file the `#include "..."` directive is declared in.
//!
//! ## `define: [("NAME", "VALUE"), ...]`
//!
//! Adds the given macro definitions to the pre-processor. This is equivalent to passing `-DNAME=VALUE`
//! on the command line.
//!
//! ## `dump: true`
//!
//! The crate fails to compile but prints the generated rust code to stdout.
//!
//! [reflect]: https://github.com/vulkano-rs/vulkano/blob/master/vulkano-shaders/src/lib.rs#L67
//! [cargo-expand]: https://github.com/dtolnay/cargo-expand
//! [ShaderModule::new]: https://docs.rs/vulkano/*/vulkano/pipeline/shader/struct.ShaderModule.html#method.new
//! [OomError]: https://docs.rs/vulkano/*/vulkano/enum.OomError.html
//! [pipeline::shader]: https://docs.rs/vulkano/*/vulkano/pipeline/shader/index.html
//! [descriptor]: https://docs.rs/vulkano/*/vulkano/descriptor/index.html
//! [ShaderStages]: https://docs.rs/vulkano/*/vulkano/descriptor/descriptor/struct.ShaderStages.html
//! [PipelineLayoutDesc]: https://docs.rs/vulkano/*/vulkano/descriptor/pipeline_layout/trait.PipelineLayoutDesc.html
//! [SpecializationConstants]: https://docs.rs/vulkano/*/vulkano/pipeline/shader/trait.SpecializationConstants.html
//! [pipeline]: https://docs.rs/vulkano/*/vulkano/pipeline/index.html

#![doc(html_logo_url = "https://raw.githubusercontent.com/vulkano-rs/vulkano/master/logo.png")]
#![recursion_limit = "1024"]
#[macro_use]
extern crate quote;
#[macro_use]
extern crate syn;
extern crate proc_macro;

use std::env;
use std::fs;
use std::fs::File;
use std::io::{Read, Result as IoResult};
use std::path::Path;

use syn::parse::{Parse, ParseStream, Result};
use syn::{Ident, LitBool, LitStr};

mod codegen;
mod descriptor_sets;
mod entry_point;
mod enums;
mod parse;
mod spec_consts;
mod spirv_search;
mod structs;

use crate::codegen::ShaderKind;
use std::slice::from_raw_parts;

enum SourceKind {
    Src(String),
    Path(String),
    Bytes(String),
}

struct MacroInput {
    shader_kind: ShaderKind,
    source_kind: SourceKind,
    include_directories: Vec<String>,
    macro_defines: Vec<(String, String)>,
    dump: bool,
}

impl Parse for MacroInput {
    fn parse(input: ParseStream) -> Result<Self> {
        let mut dump = None;
        let mut shader_kind = None;
        let mut source_kind = None;
        let mut include_directories = Vec::new();
        let mut macro_defines = Vec::new();

        while !input.is_empty() {
            let name: Ident = input.parse()?;
            input.parse::<Token![:]>()?;

            match name.to_string().as_ref() {
                "ty" => {
                    if shader_kind.is_some() {
                        panic!("Only one `ty` can be defined")
                    }

                    let ty: LitStr = input.parse()?;
                    let ty = match ty.value().as_ref() {
                        "vertex" => ShaderKind::Vertex,
                        "fragment" => ShaderKind::Fragment,
                        "geometry" => ShaderKind::Geometry,
                        "tess_ctrl" => ShaderKind::TessControl,
                        "tess_eval" => ShaderKind::TessEvaluation,
                        "compute" => ShaderKind::Compute,
                        _ => panic!("Unexpected shader type, valid values: vertex, fragment, geometry, tess_ctrl, tess_eval, compute")
                    };
                    shader_kind = Some(ty);
                }
                "src" => {
                    if source_kind.is_some() {
                        panic!("Only one of `src`, `path`, or `bytes` can be defined")
                    }

                    let src: LitStr = input.parse()?;
                    source_kind = Some(SourceKind::Src(src.value()));
                }
                "path" => {
                    if source_kind.is_some() {
                        panic!("Only one of `src`, `path`, or `bytes` can be defined")
                    }

                    let path: LitStr = input.parse()?;
                    source_kind = Some(SourceKind::Path(path.value()));
                }
                "bytes" => {
                    if source_kind.is_some() {
                        panic!("Only one of `src`, `path`, or `bytes` can be defined")
                    }

                    let path: LitStr = input.parse()?;
                    source_kind = Some(SourceKind::Bytes(path.value()));
                }
                "define" => {
                    let array_input;
                    bracketed!(array_input in input);

                    while !array_input.is_empty() {
                        let tuple_input;
                        parenthesized!(tuple_input in array_input);

                        let name: LitStr = tuple_input.parse()?;
                        tuple_input.parse::<Token![,]>()?;
                        let value: LitStr = tuple_input.parse()?;
                        macro_defines.push((name.value(), value.value()));

                        if !array_input.is_empty() {
                            array_input.parse::<Token![,]>()?;
                        }
                    }
                }
                "include" => {
                    let in_brackets;
                    bracketed!(in_brackets in input);

                    while !in_brackets.is_empty() {
                        let path: LitStr = in_brackets.parse()?;

                        include_directories.push(path.value());

                        if !in_brackets.is_empty() {
                            in_brackets.parse::<Token![,]>()?;
                        }
                    }
                }
                "dump" => {
                    if dump.is_some() {
                        panic!("Only one `dump` can be defined")
                    }
                    let dump_lit: LitBool = input.parse()?;
                    dump = Some(dump_lit.value);
                }
                name => panic!(format!("Unknown field name: {}", name)),
            }

            if !input.is_empty() {
                input.parse::<Token![,]>()?;
            }
        }

        let shader_kind = match shader_kind {
            Some(shader_kind) => shader_kind,
            None => panic!("Please provide a shader type e.g. `ty: \"vertex\"`"),
        };

        let source_kind = match source_kind {
            Some(source_kind) => source_kind,
            None => panic!("Please provide a source e.g. `path: \"foo.glsl\"` or `src: \"glsl source code here ...\"`")
        };

        let dump = dump.unwrap_or(false);

        Ok(MacroInput {
            shader_kind,
            source_kind,
            include_directories,
            dump,
            macro_defines,
        })
    }
}

pub(self) fn read_file_to_string(full_path: &Path) -> IoResult<String> {
    let mut buf = String::new();
    File::open(full_path).and_then(|mut file| file.read_to_string(&mut buf))?;
    Ok(buf)
}

#[proc_macro]
pub fn shader(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
    let input = parse_macro_input!(input as MacroInput);

    let root = env::var("CARGO_MANIFEST_DIR").unwrap_or(".".into());
    let root_path = Path::new(&root);

    if let SourceKind::Bytes(path) = input.source_kind {
        let full_path = root_path.join(&path);

        let bytes = if full_path.is_file() {
            fs::read(full_path).expect(&format!("Error reading source from {:?}", path))
        } else {
            panic!(
                "File {:?} was not found ; note that the path must be relative to your Cargo.toml",
                path
            );
        };

        // The SPIR-V specification essentially guarantees that
        // a shader will always be an integer number of words
        assert_eq!(0, bytes.len() % 4);
        codegen::reflect(
            "Shader",
            unsafe { from_raw_parts(bytes.as_slice().as_ptr() as *const u32, bytes.len() / 4) },
            input.dump,
        )
        .unwrap()
        .into()
    } else {
        let (path, source_code) = match input.source_kind {
            SourceKind::Src(source) => (None, source),
            SourceKind::Path(path) => (Some(path.clone()), {
                let full_path = root_path.join(&path);

                if full_path.is_file() {
                    read_file_to_string(&full_path)
                        .expect(&format!("Error reading source from {:?}", path))
                } else {
                    panic!("File {:?} was not found ; note that the path must be relative to your Cargo.toml", path);
                }
            }),
            SourceKind::Bytes(_) => unreachable!(),
        };

        let include_paths = input
            .include_directories
            .iter()
            .map(|include_directory| {
                let include_path = Path::new(include_directory);
                let mut full_include_path = root_path.to_owned();
                full_include_path.push(include_path);
                full_include_path
            })
            .collect::<Vec<_>>();

        let content = match codegen::compile(
            path,
            &root_path,
            &source_code,
            input.shader_kind,
            &include_paths,
            &input.macro_defines,
        ) {
            Ok(ok) => ok,
            Err(e) => panic!(e.replace("(s): ", "(s):\n")),
        };

        codegen::reflect("Shader", content.as_binary(), input.dump)
            .unwrap()
            .into()
    }
}
<|endoftext|>


<commit_before>use super::binding as openvr;
use super::binding::EVRInitError::*;
use super::binding::EVRApplicationType::*;
use super::binding::ETrackedDeviceClass::*;
use super::binding::EVREventType::*;
use super::constants;
use super::display::{OpenVRDisplay, OpenVRDisplayPtr};
use super::gamepad::{OpenVRGamepad, OpenVRGamepadPtr};
use super::library::OpenVRLibrary;
use std::ffi::CString;
use std::ptr;
use std::mem;
use {VRService, VRDisplay, VRDisplayPtr, VREvent, VRDisplayEvent, VRDisplayEventReason,
    VRGamepadEvent, VRGamepad, VRGamepadPtr};

// OpenVR Service implementation
pub struct OpenVRService {
    initialized: bool,
    lib: Option<OpenVRLibrary>,
    displays: Vec<OpenVRDisplayPtr>,
    gamepads: Vec<OpenVRGamepadPtr>,
    system: *mut openvr::VR_IVRSystem_FnTable,
    chaperone: *mut openvr::VR_IVRChaperone_FnTable,
}

unsafe impl Send for OpenVRService {}

impl VRService for OpenVRService {
    fn initialize(&mut self) -> Result<(), String> {
        if self.initialized {
            return Ok(());
        }

        // Load OpenVR library
        match unsafe { OpenVRLibrary::new() } {
            Ok(lib) => self.lib = Some(lib),
            Err(msg) => {
                return Err(format!("Error loading OpenVR dll: {:?}", msg));
            }
        };

        if !self.is_available() {
            return Err("Not available".into());
        }

        // Initialize OpenVR
        let mut error = EVRInitError_VRInitError_None;
        unsafe {
             (*self.lib.as_ref().unwrap().init_internal)(&mut error, EVRApplicationType_VRApplication_Scene);
        }

        if error as u32 != EVRInitError_VRInitError_None as u32 {
            return Err(format!("OpenVR Internal failed with error {}", error as u32));
        }

        // Initialize System
        error = EVRInitError_VRInitError_None;
        unsafe {
            let name = CString::new(format!("FnTable:{}", constants::IVRSystem_Version)).unwrap();
            self.system = (*self.lib.as_ref().unwrap().get_interface)(name.as_ptr(), &mut error)
                          as *mut openvr::VR_IVRSystem_FnTable;
        }

        if error as u32 != EVRInitError_VRInitError_None as u32 {
            return Err(format!("OpenVR GetGenericInterface failed with error {}", error as u32));
        }

        // Initialize Chaperone
        error = EVRInitError_VRInitError_None;
        unsafe {
            let name = CString::new(format!("FnTable:{}", constants::IVRChaperone_Version)).unwrap();
            self.chaperone = (*self.lib.as_ref().unwrap().get_interface)(name.as_ptr(), &mut error)
                             as *mut openvr::VR_IVRChaperone_FnTable;
        }

        if error as u32 != EVRInitError_VRInitError_None as u32 {
            return Err(format!("OpenVR GetGenericInterface failed with error {:?}", error));
        }

        self.initialized = true;
        Ok(())
    }

    fn fetch_displays(&mut self) -> Result<Vec<VRDisplayPtr>,String> {
        // Return cached displays if available
        if self.initialized && self.displays.len() > 0 {
            return Ok(self.clone_displays());
        }
        // Ensure that there are not initialization errors
        try!(self.initialize());

        let max_device_count: u32 = openvr::k_unMaxTrackedDeviceCount;
        self.displays.clear();

        for i in 0..max_device_count {
            let device_class: openvr::ETrackedDeviceClass = unsafe {
                (*self.system).GetTrackedDeviceClass.unwrap()(i as openvr::TrackedDeviceIndex_t)
            };

            match device_class {
                ETrackedDeviceClass_TrackedDeviceClass_HMD => {
                    self.displays.push(OpenVRDisplay::new(self.lib.as_ref().unwrap(), i, self.system, self.chaperone));
                },
                _ => {}
            }
        }

        Ok(self.clone_displays())
    }

    fn fetch_gamepads(&mut self) -> Result<Vec<VRGamepadPtr>,String> {
        // Return cached gamepads if available
        if self.initialized && self.gamepads.len() > 0 {
            return Ok(self.clone_gamepads());
        }
        try!(self.initialize());

        let max_device_count: u32 = openvr::k_unMaxTrackedDeviceCount;
        self.gamepads.clear();

        let display_id = match self.displays.first() {
            Some(display) => display.borrow().id(),
            None => 0
        };

        for i in 0..max_device_count {
            let device_class: openvr::ETrackedDeviceClass = unsafe {
                (*self.system).GetTrackedDeviceClass.unwrap()(i as openvr::TrackedDeviceIndex_t)
            };

            match device_class {
                ETrackedDeviceClass_TrackedDeviceClass_Controller => {
                    self.gamepads.push(OpenVRGamepad::new(i, self.system, display_id));
                },
                _ => {}
            }
        }

        Ok(self.clone_gamepads())
    }

    fn is_available(&self) -> bool {
        unsafe {
            match self.lib {
                Some(ref lib) => (*lib.is_hmd_present)(),
                None => false
            }
        }
    }

    fn poll_events(&self) -> Vec<VREvent> {
        let mut result = Vec::new();
        if !self.initialized || self.system.is_null() {
            return result;
        }
        let mut event: openvr::VREvent_t = unsafe { mem::uninitialized() };
        let size = mem::size_of::<openvr::VREvent_t>() as u32;
        while unsafe { (*self.system).PollNextEvent.unwrap()(&mut event, size) } {

            let event_type: openvr::EVREventType = unsafe { mem::transmute(event.eventType) };

            match event_type {
                EVREventType_VREvent_TrackedDeviceUserInteractionStarted => {
                    if let Some(display) = self.get_display(event.trackedDeviceIndex) {
                        result.push(VRDisplayEvent::Activate(display.borrow().data(),
                                                             VRDisplayEventReason::Mounted)
                                                             .into());
                    }
                },
                EVREventType_VREvent_TrackedDeviceUserInteractionEnded => {
                    if let Some(display) = self.get_display(event.trackedDeviceIndex) {
                        result.push(VRDisplayEvent::Deactivate(display.borrow().data(),
                                                               VRDisplayEventReason::Unmounted)
                                                               .into());
                    }
                },
                EVREventType_VREvent_TrackedDeviceActivated => {
                    if let Some(display) = self.get_display(event.trackedDeviceIndex) {
                        result.push(VRDisplayEvent::Connect(display.borrow().data()).into())
                    }
                    else if let Some(gamepad) = self.get_gamepad(event.trackedDeviceIndex) {
                        result.push(VRGamepadEvent::Connect(gamepad.borrow().state()).into());
                    }
                },
                EVREventType_VREvent_TrackedDeviceDeactivated => {
                    if let Some(display) = self.get_display(event.trackedDeviceIndex) {
                        result.push(VRDisplayEvent::Disconnect(display.borrow().id()).into())
                    }
                    else if let Some(gamepad) = self.get_gamepad(event.trackedDeviceIndex) {
                        result.push(VRGamepadEvent::Disconnect(gamepad.borrow().id()).into());
                    }
                },
                EVREventType_VREvent_DashboardActivated => {
                    if let Some(display) = self.get_display(event.trackedDeviceIndex) {
                        result.push(VRDisplayEvent::Blur(display.borrow().data()).into())
                    }
                },
                EVREventType_VREvent_DashboardDeactivated => {
                    if let Some(display) = self.get_display(event.trackedDeviceIndex) {
                        result.push(VRDisplayEvent::Focus(display.borrow().data()).into())
                    }
                },
                EVREventType_VREvent_ChaperoneDataHasChanged |
                EVREventType_VREvent_IpdChanged |
                EVREventType_VREvent_TrackedDeviceUpdated => {
                    if let Some(display) = self.get_display(event.trackedDeviceIndex) {
                        result.push(VRDisplayEvent::Change(display.borrow().data()).into())
                    }
                },
                _ => {}
            };
        }

        result
    }
}

impl Drop for OpenVRService {
    fn drop(&mut self) {
        if self.initialized {
            unsafe {
                self.gamepads.clear();
                self.displays.clear();
                println!("OpenVR Shutdown");
                (*self.lib.as_ref().unwrap().shutdown_internal)();
            }
        }
    }
}

impl OpenVRService {
    pub fn new() -> OpenVRService {
        OpenVRService {
            initialized: false,
            lib: None,
            displays: Vec::new(),
            gamepads: Vec::new(),
            system: ptr::null_mut(),
            chaperone: ptr::null_mut()
        }
    }

    fn clone_displays(&self) -> Vec<VRDisplayPtr> {
        self.displays.iter().map(|d| d.clone() as VRDisplayPtr).collect()
    }

    fn clone_gamepads(&self) -> Vec<VRGamepadPtr> {
        self.gamepads.iter().map(|d| d.clone() as VRGamepadPtr).collect()
    }

    pub fn get_display(&self, index: openvr::TrackedDeviceIndex_t) -> Option<&OpenVRDisplayPtr> {
        self.displays.iter().find(|&d| d.borrow().index() == index)
    }

    pub fn get_gamepad(&self, index: openvr::TrackedDeviceIndex_t) -> Option<&OpenVRGamepadPtr> {
        self.gamepads.iter().find(|&d| d.borrow().index() == index)
    }
}<commit_msg><commit_after>use super::binding as openvr;
use super::binding::EVRInitError::*;
use super::binding::EVRApplicationType::*;
use super::binding::ETrackedDeviceClass::*;
use super::binding::EVREventType::*;
use super::constants;
use super::display::{OpenVRDisplay, OpenVRDisplayPtr};
use super::gamepad::{OpenVRGamepad, OpenVRGamepadPtr};
use super::library::OpenVRLibrary;
use std::ffi::CString;
use std::ptr;
use std::mem;
use {VRService, VRDisplay, VRDisplayPtr, VREvent, VRDisplayEvent, VRDisplayEventReason,
    VRGamepadEvent, VRGamepad, VRGamepadPtr};

// OpenVR Service implementation
pub struct OpenVRService {
    initialized: bool,
    lib: Option<OpenVRLibrary>,
    displays: Vec<OpenVRDisplayPtr>,
    gamepads: Vec<OpenVRGamepadPtr>,
    system: *mut openvr::VR_IVRSystem_FnTable,
    chaperone: *mut openvr::VR_IVRChaperone_FnTable,
}

unsafe impl Send for OpenVRService {}

impl VRService for OpenVRService {
    fn initialize(&mut self) -> Result<(), String> {
        if self.initialized {
            return Ok(());
        }

        // Load OpenVR library
        match unsafe { OpenVRLibrary::new() } {
            Ok(lib) => self.lib = Some(lib),
            Err(msg) => {
                return Err(format!("Error loading OpenVR dll: {:?}", msg));
            }
        };

        if !self.is_available() {
            return Err("Not available".into());
        }

        // Initialize OpenVR
        let mut error = EVRInitError_VRInitError_None;
        unsafe {
             (*self.lib.as_ref().unwrap().init_internal)(&mut error, EVRApplicationType_VRApplication_Scene);
        }

        if error as u32 != EVRInitError_VRInitError_None as u32 {
            return Err(format!("OpenVR Internal failed with error {}", error as u32));
        }

        // Initialize System
        error = EVRInitError_VRInitError_None;
        unsafe {
            let name = CString::new(format!("FnTable:{}", constants::IVRSystem_Version)).unwrap();
            self.system = (*self.lib.as_ref().unwrap().get_interface)(name.as_ptr(), &mut error)
                          as *mut openvr::VR_IVRSystem_FnTable;
        }

        if error as u32 != EVRInitError_VRInitError_None as u32 {
            return Err(format!("OpenVR GetGenericInterface failed with error {}", error as u32));
        }

        // Initialize Chaperone
        error = EVRInitError_VRInitError_None;
        unsafe {
            let name = CString::new(format!("FnTable:{}", constants::IVRChaperone_Version)).unwrap();
            self.chaperone = (*self.lib.as_ref().unwrap().get_interface)(name.as_ptr(), &mut error)
                             as *mut openvr::VR_IVRChaperone_FnTable;
        }

        if error as u32 != EVRInitError_VRInitError_None as u32 {
            return Err(format!("OpenVR GetGenericInterface failed with error {:?}", error));
        }

        self.initialized = true;
        Ok(())
    }

    fn fetch_displays(&mut self) -> Result<Vec<VRDisplayPtr>,String> {
        // Return cached displays if available
        if self.initialized && self.displays.len() > 0 {
            return Ok(self.clone_displays());
        }
        // Ensure that there are not initialization errors
        try!(self.initialize());

        let max_device_count: u32 = openvr::k_unMaxTrackedDeviceCount;
        self.displays.clear();

        for i in 0..max_device_count {
            let device_class: openvr::ETrackedDeviceClass = unsafe {
                (*self.system).GetTrackedDeviceClass.unwrap()(i as openvr::TrackedDeviceIndex_t)
            };

            match device_class {
                ETrackedDeviceClass_TrackedDeviceClass_HMD => {
                    self.displays.push(OpenVRDisplay::new(self.lib.as_ref().unwrap(), i, self.system, self.chaperone));
                },
                _ => {}
            }
        }

        Ok(self.clone_displays())
    }

    fn fetch_gamepads(&mut self) -> Result<Vec<VRGamepadPtr>,String> {
        // Return cached gamepads if available
        if self.initialized && self.gamepads.len() > 0 {
            return Ok(self.clone_gamepads());
        }
        try!(self.initialize());

        let max_device_count: u32 = openvr::k_unMaxTrackedDeviceCount;
        self.gamepads.clear();

        let display_id = match self.displays.first() {
            Some(display) => display.borrow().id(),
            None => 0
        };

        for i in 0..max_device_count {
            let device_class: openvr::ETrackedDeviceClass = unsafe {
                (*self.system).GetTrackedDeviceClass.unwrap()(i as openvr::TrackedDeviceIndex_t)
            };

            match device_class {
                ETrackedDeviceClass_TrackedDeviceClass_Controller => {
                    self.gamepads.push(OpenVRGamepad::new(i, self.system, display_id));
                },
                _ => {}
            }
        }

        Ok(self.clone_gamepads())
    }

    fn is_available(&self) -> bool {
        unsafe {
            match self.lib {
                Some(ref lib) => (*lib.is_hmd_present)(),
                None => false
            }
        }
    }

    fn poll_events(&self) -> Vec<VREvent> {
        let mut result = Vec::new();
        if !self.initialized || self.system.is_null() {
            return result;
        }
        let mut event: openvr::VREvent_t = unsafe { mem::uninitialized() };
        let size = mem::size_of::<openvr::VREvent_t>() as u32;
        while unsafe { (*self.system).PollNextEvent.unwrap()(&mut event, size) } {

            let event_type: openvr::EVREventType = unsafe { mem::transmute(event.eventType) };

            match event_type {
                EVREventType_VREvent_TrackedDeviceUserInteractionStarted => {
                    if let Some(display) = self.get_display(event.trackedDeviceIndex) {
                        result.push(VRDisplayEvent::Activate(display.borrow().data(),
                                                             VRDisplayEventReason::Mounted)
                                                             .into());
                    }
                },
                EVREventType_VREvent_TrackedDeviceUserInteractionEnded => {
                    if let Some(display) = self.get_display(event.trackedDeviceIndex) {
                        result.push(VRDisplayEvent::Deactivate(display.borrow().data(),
                                                               VRDisplayEventReason::Unmounted)
                                                               .into());
                    }
                },
                EVREventType_VREvent_TrackedDeviceActivated => {
                    if let Some(display) = self.get_display(event.trackedDeviceIndex) {
                        result.push(VRDisplayEvent::Connect(display.borrow().data()).into())
                    }
                    else if let Some(gamepad) = self.get_gamepad(event.trackedDeviceIndex) {
                        let g = gamepad.borrow();
                        result.push(VRGamepadEvent::Connect(g.data(), g.state()).into());
                    }
                },
                EVREventType_VREvent_TrackedDeviceDeactivated => {
                    if let Some(display) = self.get_display(event.trackedDeviceIndex) {
                        result.push(VRDisplayEvent::Disconnect(display.borrow().id()).into())
                    }
                    else if let Some(gamepad) = self.get_gamepad(event.trackedDeviceIndex) {
                        result.push(VRGamepadEvent::Disconnect(gamepad.borrow().id()).into());
                    }
                },
                EVREventType_VREvent_DashboardActivated => {
                    if let Some(display) = self.get_display(event.trackedDeviceIndex) {
                        result.push(VRDisplayEvent::Blur(display.borrow().data()).into())
                    }
                },
                EVREventType_VREvent_DashboardDeactivated => {
                    if let Some(display) = self.get_display(event.trackedDeviceIndex) {
                        result.push(VRDisplayEvent::Focus(display.borrow().data()).into())
                    }
                },
                EVREventType_VREvent_ChaperoneDataHasChanged |
                EVREventType_VREvent_IpdChanged |
                EVREventType_VREvent_TrackedDeviceUpdated => {
                    if let Some(display) = self.get_display(event.trackedDeviceIndex) {
                        result.push(VRDisplayEvent::Change(display.borrow().data()).into())
                    }
                },
                _ => {}
            };
        }

        result
    }
}

impl Drop for OpenVRService {
    fn drop(&mut self) {
        if self.initialized {
            unsafe {
                self.gamepads.clear();
                self.displays.clear();
                println!("OpenVR Shutdown");
                (*self.lib.as_ref().unwrap().shutdown_internal)();
            }
        }
    }
}

impl OpenVRService {
    pub fn new() -> OpenVRService {
        OpenVRService {
            initialized: false,
            lib: None,
            displays: Vec::new(),
            gamepads: Vec::new(),
            system: ptr::null_mut(),
            chaperone: ptr::null_mut()
        }
    }

    fn clone_displays(&self) -> Vec<VRDisplayPtr> {
        self.displays.iter().map(|d| d.clone() as VRDisplayPtr).collect()
    }

    fn clone_gamepads(&self) -> Vec<VRGamepadPtr> {
        self.gamepads.iter().map(|d| d.clone() as VRGamepadPtr).collect()
    }

    pub fn get_display(&self, index: openvr::TrackedDeviceIndex_t) -> Option<&OpenVRDisplayPtr> {
        self.displays.iter().find(|&d| d.borrow().index() == index)
    }

    pub fn get_gamepad(&self, index: openvr::TrackedDeviceIndex_t) -> Option<&OpenVRGamepadPtr> {
        self.gamepads.iter().find(|&d| d.borrow().index() == index)
    }
}<|endoftext|>


<commit_before>use parser::Identifier;
use operations::item_type::ItemType;
use operations::scope::ScopeStack;

pub fn populate_scope(scope: &mut ScopeStack) {
    // Taking advantage of the scope system to simulate modules
    // This will be replaced with something better in:
    // https://github.com/brain-lang/brain/issues/37
    scope.push_scope();

    let bool_type = scope.declare_type(
        Identifier::from("bool"),
        ItemType::Primitive(1)
    );

    scope.declare_constant(
        Identifier::from("true"),
        bool_type,
        vec![1u8]
    );

    scope.declare_constant(
        Identifier::from("false"),
        bool_type,
        vec![0u8]
    );

    // boolean and operator (operator&&) and boolean or operator (operator||)
    // These operations have special names because they are not regular functions
    // that can be defined or overloaded
    // The reason these are not definable is because they have to support short
    // circuiting. This behaviour cannot be modelled by a trait, so these special
    // operators are not definable by the user.
    //TODO
}

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

    use parser::Identifier;
    use operations::scope::ScopeItem;

    #[test]
    fn constants() {
        let mut scope = ScopeStack::new();
        populate_scope(&mut scope);

        let type_id = match **scope.lookup(&Identifier::from("bool")).first().unwrap() {
            ScopeItem::Type(id) => id,
            _ => unreachable!(),
        };

        let true_bytes = match **scope.lookup(&Identifier::from("true")).first().unwrap() {
            ScopeItem::Constant {type_id, ref bytes} => {
                assert_eq!(type_id, type_id);
                assert_eq!(*bytes, vec![1]);
                bytes
            },
            _ => unreachable!(),
        };

        let false_bytes = match **scope.lookup(&Identifier::from("false")).first().unwrap() {
            ScopeItem::Constant {type_id, ref bytes} => {
                assert_eq!(type_id, type_id);
                assert_eq!(*bytes, vec![0]);
                bytes
            },
            _ => unreachable!(),
        };

        // No matter what the value of both, this property must always hold
        assert!(true_bytes != false_bytes);
    }
}
<commit_msg>Accidentlly ran into a problem with a variable being shadowed<commit_after>use parser::Identifier;
use operations::item_type::ItemType;
use operations::scope::ScopeStack;

pub fn populate_scope(scope: &mut ScopeStack) {
    // Taking advantage of the scope system to simulate modules
    // This will be replaced with something better in:
    // https://github.com/brain-lang/brain/issues/37
    scope.push_scope();

    let bool_type = scope.declare_type(
        Identifier::from("bool"),
        ItemType::Primitive(1)
    );

    scope.declare_constant(
        Identifier::from("true"),
        bool_type,
        vec![1u8]
    );

    scope.declare_constant(
        Identifier::from("false"),
        bool_type,
        vec![0u8]
    );

    // boolean and operator (operator&&) and boolean or operator (operator||)
    // These operations have special names because they are not regular functions
    // that can be defined or overloaded
    // The reason these are not definable is because they have to support short
    // circuiting. This behaviour cannot be modelled by a trait, so these special
    // operators are not definable by the user.
    //TODO
}

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

    use parser::Identifier;
    use operations::scope::ScopeItem;

    #[test]
    fn constants() {
        let mut scope = ScopeStack::new();
        populate_scope(&mut scope);

        let bool_type_id = match **scope.lookup(&Identifier::from("bool")).first().unwrap() {
            ScopeItem::Type(id) => id,
            _ => unreachable!(),
        };

        let true_bytes = match **scope.lookup(&Identifier::from("true")).first().unwrap() {
            ScopeItem::Constant {type_id, ref bytes} => {
                assert_eq!(type_id, bool_type_id);
                assert_eq!(*bytes, vec![1]);
                bytes
            },
            _ => unreachable!(),
        };

        let false_bytes = match **scope.lookup(&Identifier::from("false")).first().unwrap() {
            ScopeItem::Constant {type_id, ref bytes} => {
                assert_eq!(type_id, bool_type_id);
                assert_eq!(*bytes, vec![0]);
                bytes
            },
            _ => unreachable!(),
        };

        // No matter what the value of both, this property must always hold
        assert!(true_bytes != false_bytes);
    }
}
<|endoftext|>


<commit_before>use core::{Distribution, DefaultDistribution, IntoDistribution};

use std::ops::{RangeFull};
use std::marker;
use rand::Rng;

pub struct Uniform<T> (marker::PhantomData<fn() -> T>);

impl <T> Uniform<T> where Uniform<T>: Distribution<Output=T> {
    #[inline]
    pub fn new() -> Uniform<T> {
        Uniform(marker::PhantomData)
    }
}

impl <X> IntoDistribution<X> for RangeFull where
    Uniform<X>: Distribution<Output=X>
{
    type Distribution = Uniform<X>;

    #[inline]
    fn into_distribution(self) -> Uniform<X> {
        Uniform::new()
    }
}

macro_rules! integer_size_impls {
    ($mod_name:ident, $ty:ty, $ty32:ty, $ty64:ty) => {
        mod $mod_name {
            use super::Uniform;
            use core::Distribution;

            use std::mem;
            use rand::Rng;

            impl Distribution for Uniform<$ty> {
                type Output = $ty;

                #[inline]
                fn sample<R: Rng>(&self, rng: &mut R) -> $ty {
                    if mem::size_of::<$ty>() == 4 {
                        Uniform::<$ty32>::new().sample(rng) as $ty
                    } else {
                        Uniform::<$ty64>::new().sample(rng) as $ty
                    }
                }
            }
        }
    }
}

integer_size_impls! { isize_uniform_impls, isize, i32, i64 }
integer_size_impls! { usize_uniform_impls, usize, u32, u64 }

macro_rules! integer_impls {
    ($mod_name:ident, $ty:ty, $method_name:ident) => {
        mod $mod_name {
            use super::Uniform;
            use core::Distribution;

            use rand::Rng;

            impl Distribution for Uniform<$ty> {
                type Output = $ty;

                #[inline]
                fn sample<R: Rng>(&self, rng: &mut R) -> $ty {
                    rng.$method_name() as $ty
                }
            }
        }
    }
}

integer_impls! { i8_uniform_impls,  i8,  next_u32 }
integer_impls! { i16_uniform_impls, i16, next_u32 }
integer_impls! { i32_uniform_impls, i32, next_u32 }
integer_impls! { i64_uniform_impls, i64, next_u64 }
integer_impls! { u8_uniform_impls,  u8,  next_u32 }
integer_impls! { u16_uniform_impls, u16, next_u32 }
integer_impls! { u32_uniform_impls, u32, next_u32 }
integer_impls! { u64_uniform_impls, u64, next_u64 }

impl Distribution for Uniform<bool> {
    type Output = bool;

    #[inline]
    fn sample<R: Rng>(&self, rng: &mut R) -> bool {
        Uniform::<u8>::new().sample(rng) & 1 == 1
    }
}

macro_rules! default_distribution_is_uniform {
    ($($ty:ty),* ) => {
        $(
            impl DefaultDistribution for $ty {
                type Distribution = Uniform<Self>;

                #[inline]
                fn default_distribution() -> Uniform<Self> {
                    Uniform::new()
                }
            }
        )*
    }
}

default_distribution_is_uniform! { isize, i8, i16, i32, i64,
                                   usize, u8, u16, u32, u64,
                                   bool }

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

    use core::{Distribution, IntoDistribution};

    use rand::{self, thread_rng, Rng};
    use std::ops::{RangeFull};

    #[test]
    fn test_generate_u8() {
        let mut rng: rand::XorShiftRng = rand::thread_rng().gen();

        fn foo<D: IntoDistribution<T>, T, R: Rng>(d: D, rng: &mut R) -> T {
            d.into_distribution().sample(rng)
        }

        let _: u8 = foo(RangeFull, &mut rng);
    }

    #[test]
    fn test_generate_u8_with_type_annotation() {
        let mut rng: rand::XorShiftRng = rand::thread_rng().gen();

        let d = IntoDistribution::<u8>::into_distribution(RangeFull);
        let _ = d.sample(&mut rng);
    }

    #[test]
    fn test_range_full_into_distribution() {
        let mut rng: rand::XorShiftRng = rand::thread_rng().gen();

        let d: Uniform<u8> = (..).into_distribution();
        let _ = d.sample(&mut rng);
    }
}
<commit_msg>Cleaned up uniform.rs<commit_after>use core::{Distribution, DefaultDistribution, IntoDistribution};

use std::ops::{RangeFull};
use std::marker;
use std::mem;

use rand::Rng;
use num::PrimInt;

pub struct Uniform<T> (marker::PhantomData<fn() -> T>);

trait DowncastPrimitiveInteger: PrimInt {
    fn from_u32(u: u32) -> Self;
    fn from_u64(u: u64) -> Self;
}

impl <T> Uniform<T> {
    #[inline]
    pub fn new() -> Uniform<T> {
        Uniform(marker::PhantomData)
    }
}

impl <T: DowncastPrimitiveInteger> IntoDistribution<T> for RangeFull
{
    type Distribution = Uniform<T>;

    #[inline]
    fn into_distribution(self) -> Uniform<T> {
        Uniform::new()
    }
}

impl <T: DowncastPrimitiveInteger> Distribution for Uniform<T>
{
    type Output = T;

    #[inline]
    fn sample<R: Rng>(&self, rng: &mut R) -> T {
        if mem::size_of::<T>() <= 4 {
            T::from_u32(rng.next_u32())
        } else {
            T::from_u64(rng.next_u64())
        }
    }
}

macro_rules! integer_impls {
    ($($ty:ty),*) => {
        $(
            impl DowncastPrimitiveInteger for $ty {
                #[inline]
                fn from_u32(u: u32) -> $ty {
                    u as $ty
                }

                #[inline]
                fn from_u64(u: u64) -> $ty {
                    u as $ty
                }
            }
        )*
    }
}

integer_impls! {
    i8, i16, i32, i64, isize,
    u8, u16, u32, u64, usize
}

impl Distribution for Uniform<bool> {
    type Output = bool;

    #[inline]
    fn sample<R: Rng>(&self, rng: &mut R) -> bool {
        Uniform::<u8>::new().sample(rng) & 1 == 1
    }
}

macro_rules! default_distribution_is_uniform {
    ($($ty:ty),* ) => {
        $(
            impl DefaultDistribution for $ty {
                type Distribution = Uniform<Self>;

                #[inline]
                fn default_distribution() -> Uniform<Self> {
                    Uniform::new()
                }
            }
        )*
    }
}

default_distribution_is_uniform! { isize, i8, i16, i32, i64,
                                   usize, u8, u16, u32, u64,
                                   bool }

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

    use core::{Distribution, IntoDistribution};

    use rand::{self, thread_rng, Rng};
    use std::ops::{RangeFull};

    #[test]
    fn test_generate_u8() {
        let mut rng: rand::XorShiftRng = rand::thread_rng().gen();

        fn foo<D: IntoDistribution<T>, T, R: Rng>(d: D, rng: &mut R) -> T {
            d.into_distribution().sample(rng)
        }

        let _: u8 = foo(RangeFull, &mut rng);
    }

    #[test]
    fn test_generate_u8_with_type_annotation() {
        let mut rng: rand::XorShiftRng = rand::thread_rng().gen();

        let d = IntoDistribution::<u8>::into_distribution(RangeFull);
        let _: u8 = d.sample(&mut rng);
    }

    #[test]
    fn test_range_full_into_distribution() {
        let mut rng: rand::XorShiftRng = rand::thread_rng().gen();

        let d: Uniform<u8> = (..).into_distribution();
        let _: u8 = d.sample(&mut rng);
    }
}
<|endoftext|>