<commit_before>use std::result;

use analysis::ref_mode::RefMode;
use env::Env;
use library::{self, Nullable};
use super::conversion_type::ConversionType;
use traits::*;

#[derive(Clone, Debug, PartialEq, Eq)]
pub enum TypeError {
    Ignored(String),
    Mismatch(String),
    Unimplemented(String),
}

pub type Result = result::Result<String, TypeError>;

fn into_inner(res: Result) -> String {
    use self::TypeError::*;
    match res {
        Ok(s) | Err(Ignored(s)) | Err(Mismatch(s)) | Err(Unimplemented(s)) => s,
    }
}

impl IntoString for Result {
    fn into_string(self) -> String {
        use self::TypeError::*;
        match self {
            Ok(s) => s,
            Err(Ignored(s)) => format!("/*Ignored*/{}", s),
            Err(Mismatch(s)) => format!("/*Metadata mismatch*/{}", s),
            Err(Unimplemented(s)) => format!("/*Unimplemented*/{}", s),
        }
    }
}

impl MapAny<String> for Result {
    fn map_any<F: FnOnce(String) -> String>(self, op: F) -> Result {
        use self::TypeError::*;
        match self {
            Ok(s) => Ok(op(s)),
            Err(Ignored(s)) => Err(Ignored(op(s))),
            Err(Mismatch(s)) => Err(Mismatch(op(s))),
            Err(Unimplemented(s)) => Err(Unimplemented(op(s))),
        }
    }
}

pub fn rust_type(env: &Env, type_id: library::TypeId) -> Result {
    rust_type_full(env, type_id, Nullable(false), RefMode::None)
}

pub fn bounds_rust_type(env: &Env, type_id: library::TypeId) -> Result {
    rust_type_full(env, type_id, Nullable(false), RefMode::ByRefFake)
}

fn rust_type_full(
    env: &Env,
    type_id: library::TypeId,
    nullable: Nullable,
    ref_mode: RefMode,
) -> Result {
    use library::Type::*;
    use library::Fundamental::*;
    let ok = |s: &str| Ok(s.into());
    let err = |s: &str| Err(TypeError::Unimplemented(s.into()));
    let mut skip_option = false;
    let type_ = env.library.type_(type_id);
    let mut rust_type = match *type_ {
        Fundamental(fund) => {
            match fund {
                None => err("()"),
                Boolean => ok("bool"),
                Int8 => ok("i8"),
                UInt8 => ok("u8"),
                Int16 => ok("i16"),
                UInt16 => ok("u16"),
                Int32 => ok("i32"),
                UInt32 => ok("u32"),
                Int64 => ok("i64"),
                UInt64 => ok("u64"),

                Int => ok("i32"),      //maybe dependent on target system
                UInt => ok("u32"),     //maybe dependent on target system

                Short => ok("libc::c_short"),   //depends of target system
                UShort => ok("libc::c_ushort"), //depends of target system
                Long => ok("libc::c_long"),     //depends of target system
                ULong => ok("libc::c_ulong"),   //depends of target system

                Size => ok("usize"),     //depends of target system
                SSize => ok("isize"),    //depends of target system

                Float => ok("f32"),
                Double => ok("f64"),

                UniChar => ok("char"),
                Utf8 => {
                    if ref_mode.is_ref() {
                        ok("str")
                    } else {
                        ok("String")
                    }
                }
                Filename => {
                    if ref_mode.is_ref() {
                        ok("std::path::Path")
                    } else {
                        ok("std::path::PathBuf")
                    }
                }
                Type if env.namespaces.glib_ns_id == library::MAIN_NAMESPACE => ok("types::Type"),
                Type => ok("glib::types::Type"),
                Unsupported => err("Unsupported"),
                _ => err(&format!("Fundamental: {:?}", fund)),
            }
        }
        Alias(ref alias) => {
            rust_type_full(env, alias.typ, nullable, ref_mode).map_any(|_| alias.name.clone())
        }
        Record(library::Record { ref c_type, .. }) if c_type == "GVariantType" => {
            if ref_mode.is_ref() {
                ok("VariantTy")
            } else {
                ok("VariantType")
            }
        }
        Enumeration(..) | Bitfield(..) | Record(..) | Union(..) | Class(..) | Interface(..) => {
            let name = type_.get_name().to_owned();
            if env.type_status(&type_id.full_name(&env.library)).ignored() {
                Err(TypeError::Ignored(name))
            } else {
                Ok(name)
            }
        }
        List(inner_tid) | SList(inner_tid) | CArray(inner_tid)
            if ConversionType::of(&env.library, inner_tid) == ConversionType::Pointer => {
            skip_option = true;
            let inner_ref_mode = match *env.library.type_(inner_tid) {
                Class(..) | Interface(..) => RefMode::None,
                _ => ref_mode,
            };
            rust_type_full(env, inner_tid, Nullable(false), inner_ref_mode)
                .map_any(|s| if ref_mode.is_ref() {
                    format!("[{}]", s)
                } else {
                    format!("Vec<{}>", s)
                })
        }
        CArray(inner_tid) if ConversionType::of(&env.library, inner_tid) == ConversionType::Direct => {
            if let Fundamental(fund) = *env.library.type_(inner_tid) {
                let array_type = match fund {
                    Int8 => Some("i8"),
                    UInt8 => Some("u8"),
                    Int16 => Some("i16"),
                    UInt16 => Some("u16"),
                    Int32 => Some("i32"),
                    UInt32 => Some("u32"),
                    Int64 => Some("i64"),
                    UInt64 => Some("u64"),

                    Int => Some("i32"),      //maybe dependent on target system
                    UInt => Some("u32"),     //maybe dependent on target system

                    Float => Some("f32"),
                    Double => Some("f64"),
                    _ => Option::None,
                };

                if let Some(s) = array_type {
                    skip_option = true;
                    if ref_mode.is_ref() {
                        Ok(format!("[{}]", s))
                    } else {
                        Ok(format!("Vec<{}>", s))
                    }
                } else {
                    Err(TypeError::Unimplemented(type_.get_name().to_owned()))
                }
            } else {
                Err(TypeError::Unimplemented(type_.get_name().to_owned()))
            }
        }
        _ => Err(TypeError::Unimplemented(type_.get_name().to_owned())),
    };

    if type_id.ns_id != library::MAIN_NAMESPACE && type_id.ns_id != library::INTERNAL_NAMESPACE &&
        !implemented_in_main_namespace(&env.library, type_id)
    {
        if env.type_status(&type_id.full_name(&env.library)).ignored() {
            rust_type = Err(TypeError::Ignored(into_inner(rust_type)));
        }
        rust_type = rust_type.map_any(|s| {
            format!("{}::{}", env.namespaces[type_id.ns_id].higher_crate_name, s)
        });
    }

    match ref_mode {
        RefMode::None | RefMode::ByRefFake => {}
        RefMode::ByRef | RefMode::ByRefImmut => {
            rust_type = rust_type.map_any(|s| format!("&{}", s))
        }
        RefMode::ByRefMut => rust_type = rust_type.map_any(|s| format!("&mut {}", s)),
    }
    if *nullable && !skip_option {
        match ConversionType::of(&env.library, type_id) {
            ConversionType::Pointer | ConversionType::Scalar => {
                rust_type = rust_type.map_any(|s| format!("Option<{}>", s))
            }
            _ => (),
        }
    }

    rust_type
}

pub fn used_rust_type(env: &Env, type_id: library::TypeId) -> Result {
    use library::Type::*;
    match *env.library.type_(type_id) {
        Fundamental(library::Fundamental::Type) |
        Fundamental(library::Fundamental::Short) |
        Fundamental(library::Fundamental::UShort) |
        Fundamental(library::Fundamental::Long) |
        Fundamental(library::Fundamental::ULong) |
        Fundamental(library::Fundamental::Filename) |
        Alias(..) |
        Bitfield(..) |
        Record(..) |
        Union(..) |
        Class(..) |
        Enumeration(..) |
        Interface(..) => rust_type(env, type_id),
        List(inner_tid) | SList(inner_tid) | CArray(inner_tid) => used_rust_type(env, inner_tid),
        _ => Err(TypeError::Ignored("Don't need use".to_owned())),
    }
}

pub fn parameter_rust_type(
    env: &Env,
    type_id: library::TypeId,
    direction: library::ParameterDirection,
    nullable: Nullable,
    ref_mode: RefMode,
) -> Result {
    use library::Type::*;
    let type_ = env.library.type_(type_id);
    let rust_type = rust_type_full(env, type_id, nullable, ref_mode);
    match *type_ {
        Fundamental(fund) => {
            if fund == library::Fundamental::Utf8 || fund == library::Fundamental::Filename {
                if direction == library::ParameterDirection::InOut ||
                    (direction == library::ParameterDirection::Out &&
                         ref_mode == RefMode::ByRefMut)
                {
                    return Err(TypeError::Unimplemented(into_inner(rust_type)));
                }
            }
            rust_type.map_any(|s| format_parameter(s, direction))
        }

        Alias(ref alias) => {
            rust_type
                .and_then(|s| {
                    parameter_rust_type(env, alias.typ, direction, nullable, ref_mode)
                        .map_any(|_| s)
                })
                .map_any(|s| format_parameter(s, direction))
        }

        Enumeration(..) | Union(..) | Bitfield(..) => {
            rust_type.map_any(|s| format_parameter(s, direction))
        }

        Record(..) => {
            if direction == library::ParameterDirection::InOut {
                Err(TypeError::Unimplemented(into_inner(rust_type)))
            } else {
                rust_type
            }
        }

        Class(..) | Interface(..) => {
            match direction {
                library::ParameterDirection::In |
                library::ParameterDirection::Out |
                library::ParameterDirection::Return => rust_type,
                _ => Err(TypeError::Unimplemented(into_inner(rust_type))),
            }
        }

        List(..) | SList(..) | CArray(..) => {
            match direction {
                library::ParameterDirection::In | library::ParameterDirection::Return => rust_type,
                _ => Err(TypeError::Unimplemented(into_inner(rust_type))),
            }
        }
        _ => Err(TypeError::Unimplemented(type_.get_name().to_owned())),
    }
}

#[inline]
fn format_parameter(rust_type: String, direction: library::ParameterDirection) -> String {
    if direction.is_out() {
        format!("&mut {}", rust_type)
    } else {
        rust_type
    }
}

//TODO: remove
fn implemented_in_main_namespace(library: &library::Library, type_id: library::TypeId) -> bool {
    type_id.full_name(library) == "GLib.Error"
}
<commit_msg>Add support for arrays as out parameters<commit_after>use std::result;

use analysis::ref_mode::RefMode;
use env::Env;
use library::{self, Nullable};
use super::conversion_type::ConversionType;
use traits::*;

#[derive(Clone, Debug, PartialEq, Eq)]
pub enum TypeError {
    Ignored(String),
    Mismatch(String),
    Unimplemented(String),
}

pub type Result = result::Result<String, TypeError>;

fn into_inner(res: Result) -> String {
    use self::TypeError::*;
    match res {
        Ok(s) | Err(Ignored(s)) | Err(Mismatch(s)) | Err(Unimplemented(s)) => s,
    }
}

impl IntoString for Result {
    fn into_string(self) -> String {
        use self::TypeError::*;
        match self {
            Ok(s) => s,
            Err(Ignored(s)) => format!("/*Ignored*/{}", s),
            Err(Mismatch(s)) => format!("/*Metadata mismatch*/{}", s),
            Err(Unimplemented(s)) => format!("/*Unimplemented*/{}", s),
        }
    }
}

impl MapAny<String> for Result {
    fn map_any<F: FnOnce(String) -> String>(self, op: F) -> Result {
        use self::TypeError::*;
        match self {
            Ok(s) => Ok(op(s)),
            Err(Ignored(s)) => Err(Ignored(op(s))),
            Err(Mismatch(s)) => Err(Mismatch(op(s))),
            Err(Unimplemented(s)) => Err(Unimplemented(op(s))),
        }
    }
}

pub fn rust_type(env: &Env, type_id: library::TypeId) -> Result {
    rust_type_full(env, type_id, Nullable(false), RefMode::None)
}

pub fn bounds_rust_type(env: &Env, type_id: library::TypeId) -> Result {
    rust_type_full(env, type_id, Nullable(false), RefMode::ByRefFake)
}

fn rust_type_full(
    env: &Env,
    type_id: library::TypeId,
    nullable: Nullable,
    ref_mode: RefMode,
) -> Result {
    use library::Type::*;
    use library::Fundamental::*;
    let ok = |s: &str| Ok(s.into());
    let err = |s: &str| Err(TypeError::Unimplemented(s.into()));
    let mut skip_option = false;
    let type_ = env.library.type_(type_id);
    let mut rust_type = match *type_ {
        Fundamental(fund) => {
            match fund {
                None => err("()"),
                Boolean => ok("bool"),
                Int8 => ok("i8"),
                UInt8 => ok("u8"),
                Int16 => ok("i16"),
                UInt16 => ok("u16"),
                Int32 => ok("i32"),
                UInt32 => ok("u32"),
                Int64 => ok("i64"),
                UInt64 => ok("u64"),

                Int => ok("i32"),      //maybe dependent on target system
                UInt => ok("u32"),     //maybe dependent on target system

                Short => ok("libc::c_short"),   //depends of target system
                UShort => ok("libc::c_ushort"), //depends of target system
                Long => ok("libc::c_long"),     //depends of target system
                ULong => ok("libc::c_ulong"),   //depends of target system

                Size => ok("usize"),     //depends of target system
                SSize => ok("isize"),    //depends of target system

                Float => ok("f32"),
                Double => ok("f64"),

                UniChar => ok("char"),
                Utf8 => {
                    if ref_mode.is_ref() {
                        ok("str")
                    } else {
                        ok("String")
                    }
                }
                Filename => {
                    if ref_mode.is_ref() {
                        ok("std::path::Path")
                    } else {
                        ok("std::path::PathBuf")
                    }
                }
                Type if env.namespaces.glib_ns_id == library::MAIN_NAMESPACE => ok("types::Type"),
                Type => ok("glib::types::Type"),
                Unsupported => err("Unsupported"),
                _ => err(&format!("Fundamental: {:?}", fund)),
            }
        }
        Alias(ref alias) => {
            rust_type_full(env, alias.typ, nullable, ref_mode).map_any(|_| alias.name.clone())
        }
        Record(library::Record { ref c_type, .. }) if c_type == "GVariantType" => {
            if ref_mode.is_ref() {
                ok("VariantTy")
            } else {
                ok("VariantType")
            }
        }
        Enumeration(..) | Bitfield(..) | Record(..) | Union(..) | Class(..) | Interface(..) => {
            let name = type_.get_name().to_owned();
            if env.type_status(&type_id.full_name(&env.library)).ignored() {
                Err(TypeError::Ignored(name))
            } else {
                Ok(name)
            }
        }
        List(inner_tid) | SList(inner_tid) | CArray(inner_tid)
            if ConversionType::of(&env.library, inner_tid) == ConversionType::Pointer => {
            skip_option = true;
            let inner_ref_mode = match *env.library.type_(inner_tid) {
                Class(..) | Interface(..) => RefMode::None,
                _ => ref_mode,
            };
            rust_type_full(env, inner_tid, Nullable(false), inner_ref_mode)
                .map_any(|s| if ref_mode.is_ref() {
                    format!("[{}]", s)
                } else {
                    format!("Vec<{}>", s)
                })
        }
        CArray(inner_tid) if ConversionType::of(&env.library, inner_tid) == ConversionType::Direct => {
            if let Fundamental(fund) = *env.library.type_(inner_tid) {
                let array_type = match fund {
                    Int8 => Some("i8"),
                    UInt8 => Some("u8"),
                    Int16 => Some("i16"),
                    UInt16 => Some("u16"),
                    Int32 => Some("i32"),
                    UInt32 => Some("u32"),
                    Int64 => Some("i64"),
                    UInt64 => Some("u64"),

                    Int => Some("i32"),      //maybe dependent on target system
                    UInt => Some("u32"),     //maybe dependent on target system

                    Float => Some("f32"),
                    Double => Some("f64"),
                    _ => Option::None,
                };

                if let Some(s) = array_type {
                    skip_option = true;
                    if ref_mode.is_ref() {
                        Ok(format!("[{}]", s))
                    } else {
                        Ok(format!("Vec<{}>", s))
                    }
                } else {
                    Err(TypeError::Unimplemented(type_.get_name().to_owned()))
                }
            } else {
                Err(TypeError::Unimplemented(type_.get_name().to_owned()))
            }
        }
        _ => Err(TypeError::Unimplemented(type_.get_name().to_owned())),
    };

    if type_id.ns_id != library::MAIN_NAMESPACE && type_id.ns_id != library::INTERNAL_NAMESPACE &&
        !implemented_in_main_namespace(&env.library, type_id)
    {
        if env.type_status(&type_id.full_name(&env.library)).ignored() {
            rust_type = Err(TypeError::Ignored(into_inner(rust_type)));
        }
        rust_type = rust_type.map_any(|s| {
            format!("{}::{}", env.namespaces[type_id.ns_id].higher_crate_name, s)
        });
    }

    match ref_mode {
        RefMode::None | RefMode::ByRefFake => {}
        RefMode::ByRef | RefMode::ByRefImmut => {
            rust_type = rust_type.map_any(|s| format!("&{}", s))
        }
        RefMode::ByRefMut => rust_type = rust_type.map_any(|s| format!("&mut {}", s)),
    }
    if *nullable && !skip_option {
        match ConversionType::of(&env.library, type_id) {
            ConversionType::Pointer | ConversionType::Scalar => {
                rust_type = rust_type.map_any(|s| format!("Option<{}>", s))
            }
            _ => (),
        }
    }

    rust_type
}

pub fn used_rust_type(env: &Env, type_id: library::TypeId) -> Result {
    use library::Type::*;
    match *env.library.type_(type_id) {
        Fundamental(library::Fundamental::Type) |
        Fundamental(library::Fundamental::Short) |
        Fundamental(library::Fundamental::UShort) |
        Fundamental(library::Fundamental::Long) |
        Fundamental(library::Fundamental::ULong) |
        Fundamental(library::Fundamental::Filename) |
        Alias(..) |
        Bitfield(..) |
        Record(..) |
        Union(..) |
        Class(..) |
        Enumeration(..) |
        Interface(..) => rust_type(env, type_id),
        List(inner_tid) | SList(inner_tid) | CArray(inner_tid) => used_rust_type(env, inner_tid),
        _ => Err(TypeError::Ignored("Don't need use".to_owned())),
    }
}

pub fn parameter_rust_type(
    env: &Env,
    type_id: library::TypeId,
    direction: library::ParameterDirection,
    nullable: Nullable,
    ref_mode: RefMode,
) -> Result {
    use library::Type::*;
    let type_ = env.library.type_(type_id);
    let rust_type = rust_type_full(env, type_id, nullable, ref_mode);
    match *type_ {
        Fundamental(fund) => {
            if fund == library::Fundamental::Utf8 || fund == library::Fundamental::Filename {
                if direction == library::ParameterDirection::InOut ||
                    (direction == library::ParameterDirection::Out &&
                         ref_mode == RefMode::ByRefMut)
                {
                    return Err(TypeError::Unimplemented(into_inner(rust_type)));
                }
            }
            rust_type.map_any(|s| format_parameter(s, direction))
        }

        Alias(ref alias) => {
            rust_type
                .and_then(|s| {
                    parameter_rust_type(env, alias.typ, direction, nullable, ref_mode)
                        .map_any(|_| s)
                })
                .map_any(|s| format_parameter(s, direction))
        }

        Enumeration(..) | Union(..) | Bitfield(..) => {
            rust_type.map_any(|s| format_parameter(s, direction))
        }

        Record(..) => {
            if direction == library::ParameterDirection::InOut {
                Err(TypeError::Unimplemented(into_inner(rust_type)))
            } else {
                rust_type
            }
        }

        Class(..) | Interface(..) => {
            match direction {
                library::ParameterDirection::In |
                library::ParameterDirection::Out |
                library::ParameterDirection::Return => rust_type,
                _ => Err(TypeError::Unimplemented(into_inner(rust_type))),
            }
        }

        List(..) | SList(..) => {
            match direction {
                library::ParameterDirection::In | library::ParameterDirection::Return => rust_type,
                _ => Err(TypeError::Unimplemented(into_inner(rust_type))),
            }
        }
        CArray(..) => {
            match direction {
                library::ParameterDirection::In |
                library::ParameterDirection::Out |
                library::ParameterDirection::Return => rust_type,
                _ => Err(TypeError::Unimplemented(into_inner(rust_type))),
            }
        }
        _ => Err(TypeError::Unimplemented(type_.get_name().to_owned())),
    }
}

#[inline]
fn format_parameter(rust_type: String, direction: library::ParameterDirection) -> String {
    if direction.is_out() {
        format!("&mut {}", rust_type)
    } else {
        rust_type
    }
}

//TODO: remove
fn implemented_in_main_namespace(library: &library::Library, type_id: library::TypeId) -> bool {
    type_id.full_name(library) == "GLib.Error"
}
<|endoftext|>


<commit_before>use std::result;

use env::Env;
use library::{self, Nullable};
use nameutil::crate_name;
use super::conversion_type::ConversionType;
use traits::*;

pub type Result = result::Result<String, String>;

impl AsStr for Result {
    #[inline]
    fn as_str(&self) -> &str {
        self.as_ref().unwrap_or_else(|s| s)
    }
}

pub fn rust_type(env: &Env, type_id: library::TypeId) -> Result {
    rust_type_full(env, type_id, Nullable(false), false)
}

fn rust_type_full(env: &Env, type_id: library::TypeId, nullable: Nullable, by_ref: bool) -> Result {
    use library::Type::*;
    use library::Fundamental::*;
    let mut skip_option = false;
    let type_ = env.library.type_(type_id);
    let mut rust_type = match *type_ {
        Fundamental(fund) => {
            let ok = |s: &str| Ok(s.into());
            let err = |s: &str| Err(s.into());
            match fund {
                None => err("()"),
                Boolean => ok("bool"),
                Int8 => ok("i8"),
                UInt8 => ok("u8"),
                Int16 => ok("i16"),
                UInt16 => ok("u16"),
                Int32 => ok("i32"),
                UInt32 => ok("u32"),
                Int64 => ok("i64"),
                UInt64 => ok("u64"),

                Int => ok("i32"),      //maybe dependent on target system
                UInt => ok("u32"),     //maybe dependent on target system

                Float => ok("f32"),
                Double => ok("f64"),

                Utf8 => if by_ref { ok("str") } else { ok("String") },
                Filename => if by_ref { ok("str") } else { ok("String") },

                Type => ok("glib::types::Type"),
                Unsupported => err("Unsupported"),
                _ => err(&format!("Fundamental: {:?}", fund)),
            }
        },

        Enumeration(ref enum_) => Ok(enum_.name.clone()),
        Bitfield(ref bitfield) => Ok(bitfield.name.clone()),
        Record(ref record) => Ok(record.name.clone()),
        Interface(ref interface) => Ok(interface.name.clone()),
        Class(ref klass) => Ok(klass.name.clone()),
        List(inner_tid) => {
            skip_option = true;
            rust_type(env, inner_tid)
                .map_any(|s| if by_ref { format!("[{}]", s) } else { format!("Vec<{}>", s) })
        }
        _ => Err(format!("Unknown rust type: {:?}", type_.get_name())),
        //TODO: check usage library::Type::get_name() when no _ in this
    };

    if type_id.ns_id != library::MAIN_NAMESPACE && type_id.ns_id != library::INTERNAL_NAMESPACE {
        let rust_type_with_prefix = rust_type.map(|s| format!("{}::{}",
            crate_name(&env.library.namespace(type_id.ns_id).name), s));
        if env.type_status(&type_id.full_name(&env.library)).ignored() {
            rust_type = Err(rust_type_with_prefix.as_str().into());
        } else {
            rust_type = rust_type_with_prefix;
        }
    }
    if by_ref {
        rust_type = rust_type.map_any(|s| format!("&{}", s));
    }
    if *nullable && !skip_option {
        match ConversionType::of(&env.library, type_id) {
            ConversionType::Pointer => rust_type = rust_type.map_any(|s| format!("Option<{}>", s)),
            _ => (),
        }
    }

    rust_type
}

pub fn used_rust_type(env: &Env, type_id: library::TypeId) -> Result {
    use library::Type::*;
    match *env.library.type_(type_id) {
        Fundamental(library::Fundamental::Type) |
            Bitfield(..) |
            Record(..) |
            Class(..) |
            Enumeration(..) |
            Interface(..) => rust_type(env, type_id),
        _ => Err("Don't need use".into()),
    }
}

pub fn parameter_rust_type(env: &Env, type_id:library::TypeId,
        direction: library::ParameterDirection, nullable: Nullable) -> Result {
    use library::Type::*;
    let type_ = env.library.type_(type_id);
    let by_ref = match *type_ {
        Fundamental(library::Fundamental::Utf8) |
            Fundamental(library::Fundamental::Filename) |
            Record(..) |
            Class(..) |
            List(..) => direction == library::ParameterDirection::In,
        _ => false,
    };
    let rust_type = rust_type_full(env, type_id, nullable, by_ref);
    match *type_ {
        Fundamental(fund) => {
            if fund == library::Fundamental::Utf8 || fund == library::Fundamental::Filename {
                match direction {
                    library::ParameterDirection::In |
                        library::ParameterDirection::Return => rust_type,
                    _ => Err(format!("/*Unimplemented*/{}", rust_type.as_str())),
                }
            } else {
                format_parameter(rust_type, direction)
            }
        },

        Enumeration(..) |
            Bitfield(..) => format_parameter(rust_type, direction),

        Record(..) => {
            match direction {
                library::ParameterDirection::In |
                    library::ParameterDirection::Return => rust_type,
                _ => Err(format!("/*Unimplemented*/{}", rust_type.as_str())),
            }
        }

        Class(..) => {
            match direction {
                library::ParameterDirection::In |
                    library::ParameterDirection::Return => rust_type,
                _ => Err(format!("/*Unimplemented*/{}", rust_type.as_str())),
            }
        }

        List(..) => {
            match direction {
                library::ParameterDirection::Return => rust_type,
                _ => Err(format!("/*Unimplemented*/{}", rust_type.as_str())),
            }
        }
        _ => Err(format!("Unknown rust type: {:?}", type_.get_name())),
        //TODO: check usage library::Type::get_name() when no _ in this
    }
}

#[inline]
fn format_parameter(rust_type: Result, direction: library::ParameterDirection) -> Result {
    if direction.is_out() {
        rust_type.map(|s| format!("&mut {}", s))
    } else {
        rust_type
    }
}
<commit_msg>Extract function use_by_ref<commit_after>use std::result;

use env::Env;
use library::{self, Nullable};
use nameutil::crate_name;
use super::conversion_type::ConversionType;
use traits::*;

pub type Result = result::Result<String, String>;

impl AsStr for Result {
    #[inline]
    fn as_str(&self) -> &str {
        self.as_ref().unwrap_or_else(|s| s)
    }
}

pub fn rust_type(env: &Env, type_id: library::TypeId) -> Result {
    rust_type_full(env, type_id, Nullable(false), false)
}

fn rust_type_full(env: &Env, type_id: library::TypeId, nullable: Nullable, by_ref: bool) -> Result {
    use library::Type::*;
    use library::Fundamental::*;
    let mut skip_option = false;
    let type_ = env.library.type_(type_id);
    let mut rust_type = match *type_ {
        Fundamental(fund) => {
            let ok = |s: &str| Ok(s.into());
            let err = |s: &str| Err(s.into());
            match fund {
                None => err("()"),
                Boolean => ok("bool"),
                Int8 => ok("i8"),
                UInt8 => ok("u8"),
                Int16 => ok("i16"),
                UInt16 => ok("u16"),
                Int32 => ok("i32"),
                UInt32 => ok("u32"),
                Int64 => ok("i64"),
                UInt64 => ok("u64"),

                Int => ok("i32"),      //maybe dependent on target system
                UInt => ok("u32"),     //maybe dependent on target system

                Float => ok("f32"),
                Double => ok("f64"),

                Utf8 => if by_ref { ok("str") } else { ok("String") },
                Filename => if by_ref { ok("str") } else { ok("String") },

                Type => ok("glib::types::Type"),
                Unsupported => err("Unsupported"),
                _ => err(&format!("Fundamental: {:?}", fund)),
            }
        },

        Enumeration(ref enum_) => Ok(enum_.name.clone()),
        Bitfield(ref bitfield) => Ok(bitfield.name.clone()),
        Record(ref record) => Ok(record.name.clone()),
        Interface(ref interface) => Ok(interface.name.clone()),
        Class(ref klass) => Ok(klass.name.clone()),
        List(inner_tid) => {
            skip_option = true;
            rust_type(env, inner_tid)
                .map_any(|s| if by_ref { format!("[{}]", s) } else { format!("Vec<{}>", s) })
        }
        _ => Err(format!("Unknown rust type: {:?}", type_.get_name())),
        //TODO: check usage library::Type::get_name() when no _ in this
    };

    if type_id.ns_id != library::MAIN_NAMESPACE && type_id.ns_id != library::INTERNAL_NAMESPACE {
        let rust_type_with_prefix = rust_type.map(|s| format!("{}::{}",
            crate_name(&env.library.namespace(type_id.ns_id).name), s));
        if env.type_status(&type_id.full_name(&env.library)).ignored() {
            rust_type = Err(rust_type_with_prefix.as_str().into());
        } else {
            rust_type = rust_type_with_prefix;
        }
    }
    if by_ref {
        rust_type = rust_type.map_any(|s| format!("&{}", s));
    }
    if *nullable && !skip_option {
        match ConversionType::of(&env.library, type_id) {
            ConversionType::Pointer => rust_type = rust_type.map_any(|s| format!("Option<{}>", s)),
            _ => (),
        }
    }

    rust_type
}

pub fn used_rust_type(env: &Env, type_id: library::TypeId) -> Result {
    use library::Type::*;
    match *env.library.type_(type_id) {
        Fundamental(library::Fundamental::Type) |
            Bitfield(..) |
            Record(..) |
            Class(..) |
            Enumeration(..) |
            Interface(..) => rust_type(env, type_id),
        _ => Err("Don't need use".into()),
    }
}

pub fn parameter_rust_type(env: &Env, type_id:library::TypeId,
        direction: library::ParameterDirection, nullable: Nullable) -> Result {
    use library::Type::*;
    let type_ = env.library.type_(type_id);
    let by_ref = use_by_ref(type_, direction);
    let rust_type = rust_type_full(env, type_id, nullable, by_ref);
    match *type_ {
        Fundamental(fund) => {
            if fund == library::Fundamental::Utf8 || fund == library::Fundamental::Filename {
                match direction {
                    library::ParameterDirection::In |
                        library::ParameterDirection::Return => rust_type,
                    _ => Err(format!("/*Unimplemented*/{}", rust_type.as_str())),
                }
            } else {
                format_parameter(rust_type, direction)
            }
        },

        Enumeration(..) |
            Bitfield(..) => format_parameter(rust_type, direction),

        Record(..) => {
            match direction {
                library::ParameterDirection::In |
                    library::ParameterDirection::Return => rust_type,
                _ => Err(format!("/*Unimplemented*/{}", rust_type.as_str())),
            }
        }

        Class(..) => {
            match direction {
                library::ParameterDirection::In |
                    library::ParameterDirection::Return => rust_type,
                _ => Err(format!("/*Unimplemented*/{}", rust_type.as_str())),
            }
        }

        List(..) => {
            match direction {
                library::ParameterDirection::Return => rust_type,
                _ => Err(format!("/*Unimplemented*/{}", rust_type.as_str())),
            }
        }
        _ => Err(format!("Unknown rust type: {:?}", type_.get_name())),
        //TODO: check usage library::Type::get_name() when no _ in this
    }
}

#[inline]
fn format_parameter(rust_type: Result, direction: library::ParameterDirection) -> Result {
    if direction.is_out() {
        rust_type.map(|s| format!("&mut {}", s))
    } else {
        rust_type
    }
}

#[inline]
fn use_by_ref(type_: &library::Type, direction: library::ParameterDirection) -> bool {
    use library::Type::*;
    match *type_ {
        Fundamental(library::Fundamental::Utf8) |
            Fundamental(library::Fundamental::Filename) |
            Record(..) |
            Class(..) |
            List(..) => direction == library::ParameterDirection::In,
        _ => false,
    }
}
<|endoftext|>


<commit_before>use std::collections::BTreeMap;
use std::mem;
use std::rc::Rc;

use serde::{Serialize, Serializer};

use core::interning::InternedString;
use core::{Dependency, PackageId, SourceId};
use semver::Version;

use util::CargoResult;

/// Subset of a `Manifest`. Contains only the most important information about
/// a package.
///
/// Summaries are cloned, and should not be mutated after creation
#[derive(Debug, Clone)]
pub struct Summary {
    inner: Rc<Inner>,
}

#[derive(Debug, Clone)]
struct Inner {
    package_id: PackageId,
    dependencies: Vec<Dependency>,
    features: FeatureMap,
    checksum: Option<String>,
    links: Option<InternedString>,
}

impl Summary {
    pub fn new(
        pkg_id: PackageId,
        dependencies: Vec<Dependency>,
        features: BTreeMap<String, Vec<String>>,
        links: Option<String>,
    ) -> CargoResult<Summary> {
        for dep in dependencies.iter() {
            if features.get(&*dep.name()).is_some() {
                bail!(
                    "Features and dependencies cannot have the \
                     same name: `{}`",
                    dep.name()
                )
            }
            if dep.is_optional() && !dep.is_transitive() {
                bail!(
                    "Dev-dependencies are not allowed to be optional: `{}`",
                    dep.name()
                )
            }
        }
        let feature_map = build_feature_map(features, &dependencies)?;
        Ok(Summary {
            inner: Rc::new(Inner {
                package_id: pkg_id,
                dependencies,
                features: feature_map,
                checksum: None,
                links: links.map(|l| InternedString::new(&l)),
            }),
        })
    }

    pub fn package_id(&self) -> &PackageId {
        &self.inner.package_id
    }
    pub fn name(&self) -> InternedString {
        self.package_id().name()
    }
    pub fn version(&self) -> &Version {
        self.package_id().version()
    }
    pub fn source_id(&self) -> &SourceId {
        self.package_id().source_id()
    }
    pub fn dependencies(&self) -> &[Dependency] {
        &self.inner.dependencies
    }
    pub fn features(&self) -> &FeatureMap {
        &self.inner.features
    }
    pub fn checksum(&self) -> Option<&str> {
        self.inner.checksum.as_ref().map(|s| &s[..])
    }
    pub fn links(&self) -> Option<InternedString> {
        self.inner.links
    }

    pub fn override_id(mut self, id: PackageId) -> Summary {
        Rc::make_mut(&mut self.inner).package_id = id;
        self
    }

    pub fn set_checksum(mut self, cksum: String) -> Summary {
        Rc::make_mut(&mut self.inner).checksum = Some(cksum);
        self
    }

    pub fn map_dependencies<F>(mut self, f: F) -> Summary
    where
        F: FnMut(Dependency) -> Dependency,
    {
        {
            let slot = &mut Rc::make_mut(&mut self.inner).dependencies;
            let deps = mem::replace(slot, Vec::new());
            *slot = deps.into_iter().map(f).collect();
        }
        self
    }

    pub fn map_source(self, to_replace: &SourceId, replace_with: &SourceId) -> Summary {
        let me = if self.package_id().source_id() == to_replace {
            let new_id = self.package_id().with_source_id(replace_with);
            self.override_id(new_id)
        } else {
            self
        };
        me.map_dependencies(|dep| dep.map_source(to_replace, replace_with))
    }
}

impl PartialEq for Summary {
    fn eq(&self, other: &Summary) -> bool {
        self.inner.package_id == other.inner.package_id
    }
}

// Checks features for errors, bailing out a CargoResult:Err if invalid,
// and creates FeatureValues for each feature.
fn build_feature_map(
    features: BTreeMap<String, Vec<String>>,
    dependencies: &[Dependency],
) -> CargoResult<FeatureMap> {
    use self::FeatureValue::*;
    let mut map = BTreeMap::new();
    for (feature, list) in features.iter() {
        let mut values = vec![];
        for dep in list {
            let val = FeatureValue::build(InternedString::new(dep), |fs| features.contains_key(fs));
            if let &Feature(_) = &val {
                values.push(val);
                continue;
            }

            // Find data for the referenced dependency...
            let dep_data = {
                let dep_name = match val {
                    Feature(_) => "",
                    Crate(ref dep_name) | CrateFeature(ref dep_name, _) => dep_name,
                };
                dependencies.iter().find(|d| *d.name() == *dep_name)
            };

            match (&val, dep_data) {
                (&Crate(ref dep), Some(d)) => {
                    if !d.is_optional() {
                        bail!(
                            "Feature `{}` depends on `{}` which is not an \
                             optional dependency.\nConsider adding \
                             `optional = true` to the dependency",
                            feature,
                            dep
                        )
                    }
                }
                (&CrateFeature(ref dep_name, _), None) => bail!(
                    "Feature `{}` requires a feature of `{}` which is not a \
                     dependency",
                    feature,
                    dep_name
                ),
                (&Crate(ref dep), None) => bail!(
                    "Feature `{}` includes `{}` which is neither \
                     a dependency nor another feature",
                    feature,
                    dep
                ),
                (&CrateFeature(_, _), Some(_)) | (&Feature(_), _) => {}
            }
            values.push(val);
        }
        map.insert(feature.clone(), values);
    }
    Ok(map)
}

/// FeatureValue represents the types of dependencies a feature can have:
///
/// * Another feature
/// * An optional dependency
/// * A feature in a depedency
///
/// The selection between these 3 things happens as part of the construction of the FeatureValue.
#[derive(Clone, Debug)]
pub enum FeatureValue {
    Feature(InternedString),
    Crate(InternedString),
    CrateFeature(InternedString, InternedString),
}

impl FeatureValue {
    fn build<T>(feature: InternedString, is_feature: T) -> FeatureValue
    where
        T: Fn(&str) -> bool,
    {
        match feature.find('/') {
            Some(pos) => {
                let (dep, dep_feat) = feature.split_at(pos);
                let dep_feat = &dep_feat[1..];
                FeatureValue::CrateFeature(InternedString::new(dep), InternedString::new(dep_feat))
            }
            None if is_feature(&feature) => FeatureValue::Feature(feature),
            None => FeatureValue::Crate(feature),
        }
    }

    pub fn new(feature: InternedString, s: &Summary) -> FeatureValue {
        Self::build(feature, |fs| s.features().contains_key(fs))
    }

    pub fn to_string(&self) -> String {
        use self::FeatureValue::*;
        match *self {
            Feature(ref f) => f.to_string(),
            Crate(ref c) => c.to_string(),
            CrateFeature(ref c, ref f) => [c.as_ref(), f.as_ref()].join("/"),
        }
    }
}

impl Serialize for FeatureValue {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: Serializer,
    {
        use self::FeatureValue::*;
        match *self {
            Feature(ref f) => serializer.serialize_str(f),
            Crate(ref c) => serializer.serialize_str(c),
            CrateFeature(ref c, ref f) => {
                serializer.serialize_str(&[c.as_ref(), f.as_ref()].join("/"))
            }
        }
    }
}

pub type FeatureMap = BTreeMap<String, Vec<FeatureValue>>;
<commit_msg>Simplify retrieval of dependency data<commit_after>use std::collections::BTreeMap;
use std::mem;
use std::rc::Rc;

use serde::{Serialize, Serializer};

use core::interning::InternedString;
use core::{Dependency, PackageId, SourceId};
use semver::Version;

use util::CargoResult;

/// Subset of a `Manifest`. Contains only the most important information about
/// a package.
///
/// Summaries are cloned, and should not be mutated after creation
#[derive(Debug, Clone)]
pub struct Summary {
    inner: Rc<Inner>,
}

#[derive(Debug, Clone)]
struct Inner {
    package_id: PackageId,
    dependencies: Vec<Dependency>,
    features: FeatureMap,
    checksum: Option<String>,
    links: Option<InternedString>,
}

impl Summary {
    pub fn new(
        pkg_id: PackageId,
        dependencies: Vec<Dependency>,
        features: BTreeMap<String, Vec<String>>,
        links: Option<String>,
    ) -> CargoResult<Summary> {
        for dep in dependencies.iter() {
            if features.get(&*dep.name()).is_some() {
                bail!(
                    "Features and dependencies cannot have the \
                     same name: `{}`",
                    dep.name()
                )
            }
            if dep.is_optional() && !dep.is_transitive() {
                bail!(
                    "Dev-dependencies are not allowed to be optional: `{}`",
                    dep.name()
                )
            }
        }
        let feature_map = build_feature_map(features, &dependencies)?;
        Ok(Summary {
            inner: Rc::new(Inner {
                package_id: pkg_id,
                dependencies,
                features: feature_map,
                checksum: None,
                links: links.map(|l| InternedString::new(&l)),
            }),
        })
    }

    pub fn package_id(&self) -> &PackageId {
        &self.inner.package_id
    }
    pub fn name(&self) -> InternedString {
        self.package_id().name()
    }
    pub fn version(&self) -> &Version {
        self.package_id().version()
    }
    pub fn source_id(&self) -> &SourceId {
        self.package_id().source_id()
    }
    pub fn dependencies(&self) -> &[Dependency] {
        &self.inner.dependencies
    }
    pub fn features(&self) -> &FeatureMap {
        &self.inner.features
    }
    pub fn checksum(&self) -> Option<&str> {
        self.inner.checksum.as_ref().map(|s| &s[..])
    }
    pub fn links(&self) -> Option<InternedString> {
        self.inner.links
    }

    pub fn override_id(mut self, id: PackageId) -> Summary {
        Rc::make_mut(&mut self.inner).package_id = id;
        self
    }

    pub fn set_checksum(mut self, cksum: String) -> Summary {
        Rc::make_mut(&mut self.inner).checksum = Some(cksum);
        self
    }

    pub fn map_dependencies<F>(mut self, f: F) -> Summary
    where
        F: FnMut(Dependency) -> Dependency,
    {
        {
            let slot = &mut Rc::make_mut(&mut self.inner).dependencies;
            let deps = mem::replace(slot, Vec::new());
            *slot = deps.into_iter().map(f).collect();
        }
        self
    }

    pub fn map_source(self, to_replace: &SourceId, replace_with: &SourceId) -> Summary {
        let me = if self.package_id().source_id() == to_replace {
            let new_id = self.package_id().with_source_id(replace_with);
            self.override_id(new_id)
        } else {
            self
        };
        me.map_dependencies(|dep| dep.map_source(to_replace, replace_with))
    }
}

impl PartialEq for Summary {
    fn eq(&self, other: &Summary) -> bool {
        self.inner.package_id == other.inner.package_id
    }
}

// Checks features for errors, bailing out a CargoResult:Err if invalid,
// and creates FeatureValues for each feature.
fn build_feature_map(
    features: BTreeMap<String, Vec<String>>,
    dependencies: &[Dependency],
) -> CargoResult<FeatureMap> {
    use self::FeatureValue::*;
    let mut map = BTreeMap::new();
    for (feature, list) in features.iter() {
        let mut values = vec![];
        for dep in list {
            let val = FeatureValue::build(InternedString::new(dep), |fs| features.contains_key(fs));
            if let &Feature(_) = &val {
                values.push(val);
                continue;
            }

            // Find data for the referenced dependency...
            let dep_data = {
                match val {
                    Feature(_) => None,
                    Crate(ref dep_name) | CrateFeature(ref dep_name, _) => {
                        dependencies.iter().find(|d| d.name() == *dep_name)
                    }
                }
            };

            match (&val, dep_data) {
                (&Crate(ref dep), Some(d)) => {
                    if !d.is_optional() {
                        bail!(
                            "Feature `{}` depends on `{}` which is not an \
                             optional dependency.\nConsider adding \
                             `optional = true` to the dependency",
                            feature,
                            dep
                        )
                    }
                }
                (&CrateFeature(ref dep_name, _), None) => bail!(
                    "Feature `{}` requires a feature of `{}` which is not a \
                     dependency",
                    feature,
                    dep_name
                ),
                (&Crate(ref dep), None) => bail!(
                    "Feature `{}` includes `{}` which is neither \
                     a dependency nor another feature",
                    feature,
                    dep
                ),
                (&CrateFeature(_, _), Some(_)) | (&Feature(_), _) => {}
            }
            values.push(val);
        }
        map.insert(feature.clone(), values);
    }
    Ok(map)
}

/// FeatureValue represents the types of dependencies a feature can have:
///
/// * Another feature
/// * An optional dependency
/// * A feature in a depedency
///
/// The selection between these 3 things happens as part of the construction of the FeatureValue.
#[derive(Clone, Debug)]
pub enum FeatureValue {
    Feature(InternedString),
    Crate(InternedString),
    CrateFeature(InternedString, InternedString),
}

impl FeatureValue {
    fn build<T>(feature: InternedString, is_feature: T) -> FeatureValue
    where
        T: Fn(&str) -> bool,
    {
        match feature.find('/') {
            Some(pos) => {
                let (dep, dep_feat) = feature.split_at(pos);
                let dep_feat = &dep_feat[1..];
                FeatureValue::CrateFeature(InternedString::new(dep), InternedString::new(dep_feat))
            }
            None if is_feature(&feature) => FeatureValue::Feature(feature),
            None => FeatureValue::Crate(feature),
        }
    }

    pub fn new(feature: InternedString, s: &Summary) -> FeatureValue {
        Self::build(feature, |fs| s.features().contains_key(fs))
    }

    pub fn to_string(&self) -> String {
        use self::FeatureValue::*;
        match *self {
            Feature(ref f) => f.to_string(),
            Crate(ref c) => c.to_string(),
            CrateFeature(ref c, ref f) => [c.as_ref(), f.as_ref()].join("/"),
        }
    }
}

impl Serialize for FeatureValue {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: Serializer,
    {
        use self::FeatureValue::*;
        match *self {
            Feature(ref f) => serializer.serialize_str(f),
            Crate(ref c) => serializer.serialize_str(c),
            CrateFeature(ref c, ref f) => {
                serializer.serialize_str(&[c.as_ref(), f.as_ref()].join("/"))
            }
        }
    }
}

pub type FeatureMap = BTreeMap<String, Vec<FeatureValue>>;
<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

//! This module contains APIs for the `profile` crate used generically in the
//! rest of Servo. These APIs are here instead of in `profile` so that these
//! modules won't have to depend on `profile`.

#![feature(box_syntax)]
#![feature(plugin, proc_macro)]
#![plugin(plugins)]

#![deny(unsafe_code)]

extern crate ipc_channel;
#[macro_use]
extern crate log;
extern crate serde;
#[macro_use]
extern crate serde_derive;
extern crate signpost;
extern crate util;

pub mod energy;
pub mod mem;
pub mod time;
<commit_msg>Allow unsafe code for energy module (build failure otherwise)<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

//! This module contains APIs for the `profile` crate used generically in the
//! rest of Servo. These APIs are here instead of in `profile` so that these
//! modules won't have to depend on `profile`.

#![feature(box_syntax)]
#![feature(plugin, proc_macro)]
#![plugin(plugins)]

#![deny(unsafe_code)]

extern crate ipc_channel;
#[macro_use]
extern crate log;
extern crate serde;
#[macro_use]
extern crate serde_derive;
extern crate signpost;
extern crate util;

#[allow(unsafe_code)]
pub mod energy;
pub mod mem;
pub mod time;
<|endoftext|>


<commit_before>extern crate syn;

#[macro_use]
extern crate quote;

use std::env;
use std::path::{PathBuf, Path as FilePath};

use syn::*;
use quote::Tokens;

struct Context {
    location_ident: Ident,
    location_expr: Expr,
    location_type: Ty,
    deref_self_expr: Expr,
}

impl Context {
    pub fn new() -> Self {
        Context {
            location_ident: Ident::from("location"),

            location_expr: Expr::from(ExprKind::Path(None, Path::from("location"))),

            location_type: Ty::Path(None, Path {
                global: false,
                segments: vec![PathSegment {
                    ident: Ident::from("Option"),
                    parameters: PathParameters::AngleBracketed(AngleBracketedParameterData {
                        lifetimes: vec![],
                        types: vec![Ty::Path(None, Path::from("Span"))],
                        bindings: vec![]
                    })
                }]
            }),

            deref_self_expr: Expr::from(ExprKind::Unary(
                UnOp::Deref,
                Box::new(Expr::from(ExprKind::Path(None, Path::from("self"))))
            ))
        }
    }

    fn expand_tracking_ref_data(&self, path: Path, data: &VariantData, mutability: Mutability) -> Arm {
        let location_pat = Pat::Ident(BindingMode::ByRef(mutability), self.location_ident.clone(), None);

        let (pat, field) = match *data {
            VariantData::Struct(_) => (
                Pat::Struct(path, vec![FieldPat {
                    ident: self.location_ident.clone(),
                    pat: Box::new(location_pat),
                    is_shorthand: true
                }], true),
                data.fields().iter().find(|field| field.ident.as_ref() == Some(&self.location_ident)).unwrap()
            ),
            VariantData::Tuple(ref fields) => (
                Pat::TupleStruct(path, {
                    let mut v = Vec::with_capacity(fields.len());
                    v.push(location_pat);
                    v.extend(std::iter::repeat(Pat::Wild).take(fields.len() - 1));
                    v
                }, None),
                &data.fields()[0]
            ),
            VariantData::Unit => panic!("Empty unit is not trackable")
        };

        let expr = self.location_expr.clone();

        Arm {
            attrs: vec![],
            pats: vec![pat],
            guard: None,
            body: Box::new(if field.ty == self.location_type {
                expr
            } else {
                Expr::from(ExprKind::MethodCall(
                    Ident::from(if mutability == Mutability::Immutable { "tracking_ref" } else { "tracking_mut" }),
                    vec![],
                    vec![expr]
                ))
            })
        }
    }

    pub fn expand_tracking_ref(&self, ast: &MacroInput, mutability: Mutability) -> Tokens {
        // Helper is provided for handling complex generic types correctly and effortlessly
        let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl();

        let (impl_name, method) = if mutability == Mutability::Immutable {
            (Ident::from("TrackingRef"), quote! {
                tracking_ref(&self) -> &Option
            })
        } else {
            (Ident::from("TrackingMut"), quote! {
                tracking_mut(&mut self) -> &mut Option
            })
        };

        // Used in the quasi-quotation below as `#name`
        let name = &ast.ident;

        let body = Expr::from(ExprKind::Match(
            Box::new(self.deref_self_expr.clone()),
            match ast.body {
                Body::Struct(ref data) => {
                    vec![self.expand_tracking_ref_data(Path::from(name.clone()), data, mutability)]
                },
                Body::Enum(ref variants) => {
                    variants.iter().map(|var| {
                        let path = Path {
                            global: false,
                            segments: vec![
                                PathSegment::from(name.clone()),
                                PathSegment::from(var.ident.clone())
                            ]
                        };
                        self.expand_tracking_ref_data(path, &var.data, mutability)
                    }).collect()
                }
            }
        ));

        quote! {
            // The generated impl
            impl #impl_generics #impl_name for #name #ty_generics #where_clause {
                fn #method {
                    #body
                }
            }
        }
    }

    fn expand_untrack_data(&self, path: Path, data: &VariantData) -> Arm {
        let (pat, idents) = match *data {
            VariantData::Struct(ref fields) => {
                let mut field_pats = Vec::with_capacity(fields.len());
                let mut idents = Vec::with_capacity(fields.len());

                for field in fields {
                    let ident = field.ident.as_ref().unwrap();

                    field_pats.push(FieldPat {
                        ident: ident.clone(),
                        pat: Box::new(Pat::Ident(BindingMode::ByRef(Mutability::Mutable), ident.clone(), None)),
                        is_shorthand: true
                    });

                    idents.push(ident.clone());
                }

                (Pat::Struct(path, field_pats, false), idents)
            },
            VariantData::Tuple(ref fields) => {
                let mut pats = Vec::with_capacity(fields.len());
                let mut idents = Vec::with_capacity(fields.len());

                for i in 0..fields.len() {
                    let ident = Ident::from(format!("_f{}", i));

                    pats.push(Pat::Ident(BindingMode::ByRef(Mutability::Mutable), ident.clone(), None));

                    idents.push(ident);
                }

                (Pat::TupleStruct(path, pats, None), idents)
            },
            VariantData::Unit => {
                (Pat::Path(None, path), vec![])
            }
        };

        let expr = Expr::from(ExprKind::Block(BlockCheckMode::Default, Block {
            stmts: idents.into_iter().map(|ident| {
                Stmt::Semi(Box::new(Expr::from(ExprKind::MethodCall(
                    Ident::from("untrack"),
                    vec![],
                    vec![Expr::from(ExprKind::Path(None, Path::from(ident)))]
                ))))
            }).collect()
        }));

        Arm {
            attrs: vec![],
            pats: vec![pat],
            guard: None,
            body: Box::new(expr)
        }
    }

    pub fn expand_untrack(&self, ast: &MacroInput) -> Tokens {
        let mut generics = ast.generics.clone();

        let bound = TyParamBound::Trait(PolyTraitRef {
            bound_lifetimes: vec![],
            trait_ref: Path::from("Untrack")
        }, TraitBoundModifier::None);

        for ty in &mut generics.ty_params {
            ty.bounds.push(bound.clone());
        }

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

        let name = &ast.ident;

        let body = Expr::from(ExprKind::Match(
            Box::new(self.deref_self_expr.clone()),
            match ast.body {
                Body::Struct(ref data) => {
                    vec![self.expand_untrack_data(Path::from(name.clone()), data)]
                },
                Body::Enum(ref variants) => {
                    variants.iter().map(|var| {
                        let path = Path {
                            global: false,
                            segments: vec![
                                PathSegment::from(name.clone()),
                                PathSegment::from(var.ident.clone())
                            ]
                        };
                        self.expand_untrack_data(path, &var.data)
                    }).collect()
                }
            }
        ));

        quote! {
            // The generated impl
            impl #impl_generics Untrack for #name #ty_generics #where_clause {
                fn untrack(&mut self) {
                    #body
                }
            }
        }
    }
}

fn main() {
    let out_dir = env::var_os("OUT_DIR").unwrap();
    let out_path = FilePath::new(&out_dir);

    let src_path = {
        let mut buf = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
        buf.push("src");
        buf
    };

    let context = Context::new();

    let registry = {
        let mut registry = Registry::new();

        registry.add_derive("TrackingRef", |input| {
            let tokens = context.expand_tracking_ref(&input, Mutability::Immutable);
            Ok(Expanded {
                new_items: parse_items(&tokens.to_string())?,
                original: Some(input),
            })
        });

        registry.add_derive("TrackingMut", |input| {
            let tokens = context.expand_tracking_ref(&input, Mutability::Mutable);
            Ok(Expanded {
                new_items: parse_items(&tokens.to_string())?,
                original: Some(input),
            })
        });

        registry.add_derive("Untrack", |input| {
            let tokens = context.expand_untrack(&input);
            Ok(Expanded {
                new_items: parse_items(&tokens.to_string())?,
                original: Some(input),
            })
        });

        registry
    };

    for entry in src_path.read_dir().unwrap() {
        let path = entry.unwrap().path();
        let file_name = path.file_name().unwrap();
        if file_name != "lib.rs" {
            let dest = out_path.join(file_name);
            registry.expand_file(&path, &dest).unwrap();
        }
    }
}<commit_msg>Require struct .location to always be Option<commit_after>extern crate syn;

#[macro_use]
extern crate quote;

use std::env;
use std::path::{PathBuf, Path as FilePath};

use syn::*;
use quote::Tokens;

struct Context {
    location_ident: Ident,
    location_expr: Expr,
    location_type: Ty,
    deref_self_expr: Expr,
}

impl Context {
    pub fn new() -> Self {
        Context {
            location_ident: Ident::from("location"),

            location_expr: Expr::from(ExprKind::Path(None, Path::from("location"))),

            location_type: Ty::Path(None, Path {
                global: false,
                segments: vec![PathSegment {
                    ident: Ident::from("Option"),
                    parameters: PathParameters::AngleBracketed(AngleBracketedParameterData {
                        lifetimes: vec![],
                        types: vec![Ty::Path(None, Path::from("Span"))],
                        bindings: vec![]
                    })
                }]
            }),

            deref_self_expr: Expr::from(ExprKind::Unary(
                UnOp::Deref,
                Box::new(Expr::from(ExprKind::Path(None, Path::from("self"))))
            ))
        }
    }

    fn expand_tracking_ref_data(&self, path: Path, data: &VariantData, mutability: Mutability) -> Arm {
        let location_pat = Pat::Ident(BindingMode::ByRef(mutability), self.location_ident.clone(), None);

        let expr = self.location_expr.clone();

        let (pat, expr) = match *data {
            VariantData::Struct(_) => (
                Pat::Struct(path, vec![FieldPat {
                    ident: self.location_ident.clone(),
                    pat: Box::new(location_pat),
                    is_shorthand: true
                }], true),
                if data.fields().iter().any(|field| field.ident.as_ref() == Some(&self.location_ident) && field.ty == self.location_type) {
                    expr
                } else {
                    panic!("Struct does not containt `location: Option<Span>`")
                }
            ),
            VariantData::Tuple(ref fields) => (
                Pat::TupleStruct(path, {
                    let mut v = Vec::with_capacity(fields.len());
                    v.push(location_pat);
                    v.extend(std::iter::repeat(Pat::Wild).take(fields.len() - 1));
                    v
                }, None),
                if data.fields()[0].ty == self.location_type {
                    expr
                } else {
                    Expr::from(ExprKind::MethodCall(
                        Ident::from(if mutability == Mutability::Immutable { "tracking_ref" } else { "tracking_mut" }),
                        vec![],
                        vec![expr]
                    ))
                }
            ),
            VariantData::Unit => panic!("Empty unit is not trackable")
        };

        Arm {
            attrs: vec![],
            pats: vec![pat],
            guard: None,
            body: Box::new(expr)
        }
    }

    pub fn expand_tracking_ref(&self, ast: &MacroInput, mutability: Mutability) -> Tokens {
        // Helper is provided for handling complex generic types correctly and effortlessly
        let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl();

        let (impl_name, method) = if mutability == Mutability::Immutable {
            (Ident::from("TrackingRef"), quote! {
                tracking_ref(&self) -> &Option
            })
        } else {
            (Ident::from("TrackingMut"), quote! {
                tracking_mut(&mut self) -> &mut Option
            })
        };

        // Used in the quasi-quotation below as `#name`
        let name = &ast.ident;

        let body = Expr::from(ExprKind::Match(
            Box::new(self.deref_self_expr.clone()),
            match ast.body {
                Body::Struct(ref data) => {
                    vec![self.expand_tracking_ref_data(Path::from(name.clone()), data, mutability)]
                },
                Body::Enum(ref variants) => {
                    variants.iter().map(|var| {
                        let path = Path {
                            global: false,
                            segments: vec![
                                PathSegment::from(name.clone()),
                                PathSegment::from(var.ident.clone())
                            ]
                        };
                        self.expand_tracking_ref_data(path, &var.data, mutability)
                    }).collect()
                }
            }
        ));

        quote! {
            // The generated impl
            impl #impl_generics #impl_name for #name #ty_generics #where_clause {
                fn #method {
                    #body
                }
            }
        }
    }

    fn expand_untrack_data(&self, path: Path, data: &VariantData) -> Arm {
        let (pat, idents) = match *data {
            VariantData::Struct(ref fields) => {
                let mut field_pats = Vec::with_capacity(fields.len());
                let mut idents = Vec::with_capacity(fields.len());

                for field in fields {
                    let ident = field.ident.as_ref().unwrap();

                    field_pats.push(FieldPat {
                        ident: ident.clone(),
                        pat: Box::new(Pat::Ident(BindingMode::ByRef(Mutability::Mutable), ident.clone(), None)),
                        is_shorthand: true
                    });

                    idents.push(ident.clone());
                }

                (Pat::Struct(path, field_pats, false), idents)
            },
            VariantData::Tuple(ref fields) => {
                let mut pats = Vec::with_capacity(fields.len());
                let mut idents = Vec::with_capacity(fields.len());

                for i in 0..fields.len() {
                    let ident = Ident::from(format!("_f{}", i));

                    pats.push(Pat::Ident(BindingMode::ByRef(Mutability::Mutable), ident.clone(), None));

                    idents.push(ident);
                }

                (Pat::TupleStruct(path, pats, None), idents)
            },
            VariantData::Unit => {
                (Pat::Path(None, path), vec![])
            }
        };

        let expr = Expr::from(ExprKind::Block(BlockCheckMode::Default, Block {
            stmts: idents.into_iter().map(|ident| {
                Stmt::Semi(Box::new(Expr::from(ExprKind::MethodCall(
                    Ident::from("untrack"),
                    vec![],
                    vec![Expr::from(ExprKind::Path(None, Path::from(ident)))]
                ))))
            }).collect()
        }));

        Arm {
            attrs: vec![],
            pats: vec![pat],
            guard: None,
            body: Box::new(expr)
        }
    }

    pub fn expand_untrack(&self, ast: &MacroInput) -> Tokens {
        let mut generics = ast.generics.clone();

        let bound = TyParamBound::Trait(PolyTraitRef {
            bound_lifetimes: vec![],
            trait_ref: Path::from("Untrack")
        }, TraitBoundModifier::None);

        for ty in &mut generics.ty_params {
            ty.bounds.push(bound.clone());
        }

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

        let name = &ast.ident;

        let body = Expr::from(ExprKind::Match(
            Box::new(self.deref_self_expr.clone()),
            match ast.body {
                Body::Struct(ref data) => {
                    vec![self.expand_untrack_data(Path::from(name.clone()), data)]
                },
                Body::Enum(ref variants) => {
                    variants.iter().map(|var| {
                        let path = Path {
                            global: false,
                            segments: vec![
                                PathSegment::from(name.clone()),
                                PathSegment::from(var.ident.clone())
                            ]
                        };
                        self.expand_untrack_data(path, &var.data)
                    }).collect()
                }
            }
        ));

        quote! {
            // The generated impl
            impl #impl_generics Untrack for #name #ty_generics #where_clause {
                fn untrack(&mut self) {
                    #body
                }
            }
        }
    }
}

fn main() {
    let out_dir = env::var_os("OUT_DIR").unwrap();
    let out_path = FilePath::new(&out_dir);

    let src_path = {
        let mut buf = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
        buf.push("src");
        buf
    };

    let context = Context::new();

    let registry = {
        let mut registry = Registry::new();

        registry.add_derive("TrackingRef", |input| {
            let tokens = context.expand_tracking_ref(&input, Mutability::Immutable);
            Ok(Expanded {
                new_items: parse_items(&tokens.to_string())?,
                original: Some(input),
            })
        });

        registry.add_derive("TrackingMut", |input| {
            let tokens = context.expand_tracking_ref(&input, Mutability::Mutable);
            Ok(Expanded {
                new_items: parse_items(&tokens.to_string())?,
                original: Some(input),
            })
        });

        registry.add_derive("Untrack", |input| {
            let tokens = context.expand_untrack(&input);
            Ok(Expanded {
                new_items: parse_items(&tokens.to_string())?,
                original: Some(input),
            })
        });

        registry
    };

    for entry in src_path.read_dir().unwrap() {
        let path = entry.unwrap().path();
        let file_name = path.file_name().unwrap();
        if file_name != "lib.rs" {
            let dest = out_path.join(file_name);
            registry.expand_file(&path, &dest).unwrap();
        }
    }
}<|endoftext|>


<commit_before>//! This module provides the Blinn Microfacet Distribution
//! TODO: Wikipedia link or something?

use std::f32;

use bxdf;
use linalg::{self, Vector};
use bxdf::microfacet::MicrofacetDistribution;

/// Struct providing the Blinn microfacet distribution
pub struct Blinn {
    exponent: f32,
}

impl Blinn {
    /// Create the Blinn microfacet distribution with some exponent
    pub fn new(e: f32) -> Blinn {
        Blinn { exponent: e }
    }
}

impl MicrofacetDistribution for Blinn {
    fn eval(&self, w_h: &Vector) -> f32 {
        (self.exponent + 2.0) * 1.0 / f32::consts::PI * 2.0
            * f32::powf(f32::abs(bxdf::cos_theta(w_h)), self.exponent)
    }
    fn sample(&self, w_o: &Vector, samples: &(f32, f32)) -> (Vector, f32) {
        // Sample a direction on the hemisphere for the half-vector
        let cos_theta = f32::powf(samples.0, 1.0 / (self.exponent + 1.0));
        let sin_theta = f32::sqrt(f32::max(0.0, 1.0 - cos_theta * cos_theta));
        let phi = f32::consts::PI * 2.0 * samples.1;
        let mut w_h = linalg::spherical_dir(sin_theta, cos_theta, phi);
        if !bxdf::same_hemisphere(w_o, &w_h) {
            w_h = -w_h;
        }
        // The sampled incident direction is the outgoing direction reflected about the half-vector
        let w_i = -*w_o + 2.0 * linalg::dot(w_o, &w_h) * w_h;
        let d = linalg::dot(w_o, &w_h);
        if d <= 0.0 {
            (w_i, 0.0)
        } else {
            let pdf_val = ((self.exponent + 1.0) * f32::powf(cos_theta, self.exponent))
                        / (f32::consts::PI * 2.0 * 4.0 * d);
            (w_i, pdf_val)
        }
    }
    fn pdf(&self, w_o: &Vector, w_i: &Vector) -> f32 {
        let w_h = (*w_o + *w_i).normalized();
        let cos_theta = f32::abs(bxdf::cos_theta(&w_h));
        let d = linalg::dot(w_o, &w_h);
        if d <= 0.0 {
            0.0
        } else {
            ((self.exponent + 1.0) * f32::powf(cos_theta, self.exponent))
                / (f32::consts::PI * 2.0 * 4.0 * d)
        }
    }
}

<commit_msg>Fix bug with multiplying by 2/pi instead of by 1/(2 pi)<commit_after>//! This module provides the Blinn Microfacet Distribution
//! TODO: Wikipedia link or something?

use std::f32;

use bxdf;
use linalg::{self, Vector};
use bxdf::microfacet::MicrofacetDistribution;

/// Struct providing the Blinn microfacet distribution
pub struct Blinn {
    exponent: f32,
}

impl Blinn {
    /// Create the Blinn microfacet distribution with some exponent
    pub fn new(e: f32) -> Blinn {
        if e > 10000.0 || f32::is_nan(e) {
            Blinn { exponent: 10000.0 }
        } else {
            Blinn { exponent: e }
        }
    }
}

impl MicrofacetDistribution for Blinn {
    fn eval(&self, w_h: &Vector) -> f32 {
        (self.exponent + 2.0) * (1.0 / (f32::consts::PI * 2.0))
            * f32::powf(f32::abs(bxdf::cos_theta(w_h)), self.exponent)
    }
    fn sample(&self, w_o: &Vector, samples: &(f32, f32)) -> (Vector, f32) {
        // Sample a direction on the hemisphere for the half-vector
        let cos_theta = f32::powf(samples.0, 1.0 / (self.exponent + 1.0));
        let sin_theta = f32::sqrt(f32::max(0.0, 1.0 - cos_theta * cos_theta));
        let phi = f32::consts::PI * 2.0 * samples.1;
        let mut w_h = linalg::spherical_dir(sin_theta, cos_theta, phi);
        if !bxdf::same_hemisphere(w_o, &w_h) {
            w_h = -w_h;
        }
        // The sampled incident direction is the outgoing direction reflected about the half-vector
        let w_i = -*w_o + 2.0 * linalg::dot(w_o, &w_h) * w_h;
        let d = linalg::dot(w_o, &w_h);
        if d <= 0.0 {
            (w_i, 0.0)
        } else {
            let pdf_val = ((self.exponent + 1.0) * f32::powf(cos_theta, self.exponent))
                        / (f32::consts::PI * 2.0 * 4.0 * d);
            (w_i, pdf_val)
        }
    }
    fn pdf(&self, w_o: &Vector, w_i: &Vector) -> f32 {
        let w_h = (*w_o + *w_i).normalized();
        let cos_theta = f32::abs(bxdf::cos_theta(&w_h));
        let d = linalg::dot(w_o, &w_h);
        if d <= 0.0 {
            0.0
        } else {
            ((self.exponent + 1.0) * f32::powf(cos_theta, self.exponent))
                / (f32::consts::PI * 2.0 * 4.0 * d)
        }
    }
}

<|endoftext|>


<commit_before>// Copyright 2020 Google LLC
//
// Use of this source code is governed by an MIT-style license that can be found
// in the LICENSE file or at https://opensource.org/licenses/MIT.

//! A handler and associated types for the list directory action.
//!
//! A list directory action collects basic metadata for all files in the
//! provided directory.
//!
//! Note that this includes only entries directly under the specified directory.
//! For more sophisticated recursive traversals, please look into the [timeline]
//! action.
//!
//! [timeline]: ../timeline/index.html

use crate::session::{self, Session};

use std::fs::{Metadata};
use std::path::{PathBuf};

/// A request type for the list directory action.
pub struct Request {
    /// A path to the directory ought to be listed.
    path: PathBuf,
}

/// A response type for the list directory action.
pub struct Response {
    /// A full path to a particular file within the listed directory.
    path: PathBuf,
    /// Metadata about a particular file within the listed directory.
    metadata: Metadata,
}

/// An error type for failures that can occur during the list directory action.
#[derive(Debug)]
enum Error {
    /// A failure occurred during the attempt to list a directory.
    ListDir(std::io::Error),
}

impl std::error::Error for Error {

    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
        use Error::*;

        match *self {
            ListDir(ref error) => Some(error),
        }
    }
}

impl std::fmt::Display for Error {

    fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
        use Error::*;

        match *self {
            ListDir(ref error) => {
                write!(fmt, "unable to list directory: {}", error)
            }
        }
    }
}

impl From<Error> for session::Error {

    fn from(error: Error) -> session::Error {
        session::Error::action(error)
    }
}

/// Handles requests for the list directory action.
pub fn handle<S>(session: &mut S, request: Request) -> session::Result<()>
where
    S: Session,
{
    let entries = crate::fs::list_dir(&request.path)
        .map_err(Error::ListDir)?;

    for entry in entries {
        session.reply(Response {
            path: entry.path,
            metadata: entry.metadata,
        })?;
    }

    Ok(())
}

impl super::Request for Request {

    type Proto = rrg_proto::ListDirRequest;

    fn from_proto(proto: Self::Proto) -> Result<Request, session::ParseError> {
        use std::convert::TryInto as _;

        let path = proto.pathspec
            .ok_or(session::MissingFieldError::new("path spec"))?
            .try_into().map_err(session::ParseError::malformed)?;

        Ok(Request {
            path: path,
        })
    }
}

impl super::Response for Response {

    const RDF_NAME: Option<&'static str> = Some("StatEntry");

    type Proto = rrg_proto::StatEntry;

    fn into_proto(self) -> Self::Proto {
        use rrg_proto::convert::IntoLossy as _;

        rrg_proto::StatEntry {
            pathspec: Some(self.path.into()),
            ..self.metadata.into_lossy()
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use std::time::SystemTime;
    use tempfile::tempdir;

    #[test]
    fn test_empty_dir() {
        let dir = tempdir().unwrap();
        let request = super::Request {
            path: PathBuf::from(dir.path()),
        };
        let mut session = session::test::Fake::new();
        assert!(handle(&mut session, request).is_ok());
        assert_eq!(session.reply_count(), 0);
    }

    #[test]
    fn test_nonexistent_path() {
        let dir = tempdir().unwrap();
        let request = super::Request {
            path: PathBuf::from(dir.path().join("nonexistent_subdir")),
        };
        let mut session = session::test::Fake::new();
        assert!(handle(&mut session, request).is_err());
    }

    #[test]
    fn test_lexicographical_order() {
        let dir = tempdir().unwrap();
        let dir_path = dir.path();
        std::fs::File::create(dir_path.join("юникод")).unwrap();
        std::fs::File::create(dir_path.join("unicode")).unwrap();
        std::fs::File::create(dir_path.join("file")).unwrap();
        std::fs::File::create(dir_path.join("afile")).unwrap();
        std::fs::File::create(dir_path.join("Datei")).unwrap();
        std::fs::File::create(dir_path.join("snake_case")).unwrap();
        std::fs::File::create(dir_path.join("CamelCase")).unwrap();
        let request = super::Request {
            path: PathBuf::from(&dir_path),
        };
        let mut session = session::test::Fake::new();
        assert!(handle(&mut session, request).is_ok());

        let mut replies = session.replies().collect::<Vec<&Response>>();
        replies.sort_by_key(|reply| reply.path.clone());

        assert_eq!(replies.len(), 7);
        assert_eq!(&replies[0].path,
                   &dir_path.join("CamelCase"));
        assert_eq!(&replies[1].path,
                   &dir_path.join("Datei"));
        assert_eq!(&replies[2].path,
                   &dir_path.join("afile"));
        assert_eq!(&replies[3].path,
                   &dir_path.join("file"));
        assert_eq!(&replies[4].path,
                   &dir_path.join("snake_case"));
        assert_eq!(&replies[5].path,
                   &dir_path.join("unicode"));
        assert_eq!(&replies[6].path,
                   &dir_path.join("юникод"));
    }

    #[test]
    #[cfg(target_os = "linux")]
    fn test_dir_response() {
        use std::os::unix::fs::MetadataExt as _;

        let dir = tempdir().unwrap();
        let dir_path = dir.path();
        let inner_dir_path = &dir_path.join("dir");
        std::fs::create_dir(&inner_dir_path).unwrap();
        let request = super::Request {
            path: PathBuf::from(&dir_path),
        };
        let mut session = session::test::Fake::new();
        handle(&mut session, request).unwrap();
        assert_eq!(session.reply_count(), 1);
        let inner_dir = &session.reply::<Response>(0);
        assert_eq!(&inner_dir.path, inner_dir_path);
        assert_eq!(inner_dir.metadata.uid(), users::get_current_uid());
        assert_eq!(inner_dir.metadata.gid(), users::get_current_gid());
        assert_eq!(inner_dir.metadata.dev(),
                   dir_path.metadata().unwrap().dev());
        assert_eq!(inner_dir.metadata.mode() & 0o40000, 0o40000);
        assert_eq!(inner_dir.metadata.nlink(), 2);

        assert!(inner_dir.metadata.accessed().unwrap() <= SystemTime::now());
        assert!(inner_dir.metadata.modified().unwrap() <= SystemTime::now());
        assert!(inner_dir.metadata.created().unwrap() <= SystemTime::now());
    }

    #[test]
    #[cfg(target_os = "linux")]
    fn test_symlink_response() {
        use std::os::unix::fs::MetadataExt as _;

        let dir = tempdir().unwrap();
        let dir_path = dir.path();
        let file_path = dir_path.join("file");
        std::fs::File::create(&file_path).unwrap();
        let sl_path = dir_path.join("symlink");
        std::os::unix::fs::symlink(&file_path, &sl_path).unwrap();
        let request = super::Request {
            path: PathBuf::from(&dir_path),
        };
        let mut session = session::test::Fake::new();
        assert!(handle(&mut session, request).is_ok());

        let mut replies = session.replies().collect::<Vec<&Response>>();
        replies.sort_by_key(|reply| reply.path.clone());

        assert_eq!(replies.len(), 2);
        let symlink = replies[1];
        assert_eq!(&symlink.path, &sl_path);
        assert_eq!(symlink.metadata.mode() & 0o120000, 0o120000);
        assert_eq!(symlink.metadata.nlink(), 1);
        assert!(symlink.metadata.accessed().unwrap() <= SystemTime::now());
        assert!(symlink.metadata.modified().unwrap() <= SystemTime::now());
        assert!(symlink.metadata.created().unwrap() <= SystemTime::now());
    }

    #[test]
    #[cfg(target_os = "linux")]
    fn test_file_response_linux() {
        use std::os::unix::fs::MetadataExt as _;
        use std::os::unix::fs::PermissionsExt;

        let dir = tempdir().unwrap();
        let dir_path = dir.path();
        let file_path = dir_path.join("file");
        std::fs::File::create(&file_path).unwrap();
        std::fs::set_permissions(&file_path,
                                 PermissionsExt::from_mode(0o664)).unwrap();
        let request = super::Request {
            path: PathBuf::from(&dir_path),
        };
        let mut session = session::test::Fake::new();
        assert!(handle(&mut session, request).is_ok());
        assert_eq!(session.reply_count(), 1);
        let file = &session.reply::<Response>(0);
        assert_eq!(file.path, file_path);
        assert_eq!(file.metadata.size(), 0);
        assert_eq!(file.metadata.mode(), 0o100664);
        assert_eq!(file.metadata.uid(), users::get_current_uid());
        assert_eq!(file.metadata.gid(), users::get_current_gid());
        assert_eq!(file.metadata.dev(),
                   dir_path.metadata().unwrap().dev());
        assert_eq!(file.metadata.nlink(), 1);
        assert!(file.metadata.accessed().unwrap() <= SystemTime::now());
        assert!(file.metadata.modified().unwrap() <= SystemTime::now());
        assert!(file.metadata.created().unwrap() <= SystemTime::now());
    }

    #[test]
    #[cfg(target_os = "linux")]
    fn test_file_response() {
        use std::os::unix::fs::MetadataExt as _;

        let dir = tempdir().unwrap();
        let dir_path = dir.path();
        let file_path = dir_path.join("file");
        std::fs::File::create(&file_path).unwrap();
        let request = super::Request {
            path: PathBuf::from(&dir_path),
        };
        let mut session = session::test::Fake::new();
        assert!(handle(&mut session, request).is_ok());
        assert_eq!(session.reply_count(), 1);
        let file = &session.reply::<Response>(0);
        assert_eq!(file.path, file_path);
        assert_eq!(file.metadata.size(), 0);
        assert!(file.metadata.accessed().unwrap() <= SystemTime::now());
        assert!(file.metadata.modified().unwrap() <= SystemTime::now());
        assert!(file.metadata.created().unwrap() <= SystemTime::now());
    }

    #[test]
    #[cfg(target_os = "linux")]
    fn test_st_flags_linux() {
        let dir = tempdir().unwrap();
        let dir_path = dir.path();
        let file_path = dir_path.join("file");
        std::fs::File::create(&file_path).unwrap();
        let request = super::Request {
            path: PathBuf::from(&dir_path),
        };
        let mut session = session::test::Fake::new();
        assert!(handle(&mut session, request).is_ok());
        assert_eq!(session.reply_count(), 1);
        let file = &session.reply::<Response>(0);
        assert_eq!(file.path, file_path);
    }

    #[test]
    #[cfg(target_os = "linux")]
    fn test_unicode_paths() {
        use std::os::unix::fs::MetadataExt as _;

        let dir = tempdir().unwrap();
        let dir_path = dir.path();
        std::fs::File::create(dir_path.join("❤ℝℝG❤")).unwrap();
        std::fs::File::create(dir_path.join("файл")).unwrap();
        std::fs::File::create(dir_path.join("ファイル")).unwrap();
        std::fs::File::create(dir_path.join("αρχείο")).unwrap();
        std::fs::File::create(dir_path.join("फ़ाइल")).unwrap();
        let request = super::Request {
            path: PathBuf::from(&dir_path),
        };
        let mut session = session::test::Fake::new();
        assert!(handle(&mut session, request).is_ok());
        assert_eq!(session.reply_count(), 5);
        let file = &session.reply::<Response>(0);
        assert_eq!(file.metadata.size(), 0);
        assert!(file.metadata.accessed().unwrap() <= SystemTime::now());
        assert!(file.metadata.modified().unwrap() <= SystemTime::now());
        assert!(file.metadata.created().unwrap() <= SystemTime::now());
        let file = &session.reply::<Response>(1);
        assert_eq!(file.metadata.size(), 0);
        assert!(file.metadata.accessed().unwrap() <= SystemTime::now());
        assert!(file.metadata.modified().unwrap() <= SystemTime::now());
        assert!(file.metadata.created().unwrap() <= SystemTime::now());
        let file = &session.reply::<Response>(2);
        assert_eq!(file.metadata.size(), 0);
        assert!(file.metadata.accessed().unwrap() <= SystemTime::now());
        assert!(file.metadata.modified().unwrap() <= SystemTime::now());
        assert!(file.metadata.created().unwrap() <= SystemTime::now());
        let file = &session.reply::<Response>(3);
        assert_eq!(file.metadata.size(), 0);
        assert!(file.metadata.accessed().unwrap() <= SystemTime::now());
        assert!(file.metadata.modified().unwrap() <= SystemTime::now());
        assert!(file.metadata.created().unwrap() <= SystemTime::now());
        let file = &session.reply::<Response>(4);
        assert_eq!(file.metadata.size(), 0);
        assert!(file.metadata.accessed().unwrap() <= SystemTime::now());
        assert!(file.metadata.modified().unwrap() <= SystemTime::now());
        assert!(file.metadata.created().unwrap() <= SystemTime::now());
    }
}
<commit_msg>Simplify tests for the list directory action.<commit_after>// Copyright 2020 Google LLC
//
// Use of this source code is governed by an MIT-style license that can be found
// in the LICENSE file or at https://opensource.org/licenses/MIT.

//! A handler and associated types for the list directory action.
//!
//! A list directory action collects basic metadata for all files in the
//! provided directory.
//!
//! Note that this includes only entries directly under the specified directory.
//! For more sophisticated recursive traversals, please look into the [timeline]
//! action.
//!
//! [timeline]: ../timeline/index.html

use crate::session::{self, Session};

use std::fs::{Metadata};
use std::path::{PathBuf};

/// A request type for the list directory action.
pub struct Request {
    /// A path to the directory ought to be listed.
    path: PathBuf,
}

/// A response type for the list directory action.
pub struct Response {
    /// A full path to a particular file within the listed directory.
    path: PathBuf,
    /// Metadata about a particular file within the listed directory.
    metadata: Metadata,
}

/// An error type for failures that can occur during the list directory action.
#[derive(Debug)]
enum Error {
    /// A failure occurred during the attempt to list a directory.
    ListDir(std::io::Error),
}

impl std::error::Error for Error {

    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
        use Error::*;

        match *self {
            ListDir(ref error) => Some(error),
        }
    }
}

impl std::fmt::Display for Error {

    fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
        use Error::*;

        match *self {
            ListDir(ref error) => {
                write!(fmt, "unable to list directory: {}", error)
            }
        }
    }
}

impl From<Error> for session::Error {

    fn from(error: Error) -> session::Error {
        session::Error::action(error)
    }
}

/// Handles requests for the list directory action.
pub fn handle<S>(session: &mut S, request: Request) -> session::Result<()>
where
    S: Session,
{
    let entries = crate::fs::list_dir(&request.path)
        .map_err(Error::ListDir)?;

    for entry in entries {
        session.reply(Response {
            path: entry.path,
            metadata: entry.metadata,
        })?;
    }

    Ok(())
}

impl super::Request for Request {

    type Proto = rrg_proto::ListDirRequest;

    fn from_proto(proto: Self::Proto) -> Result<Request, session::ParseError> {
        use std::convert::TryInto as _;

        let path = proto.pathspec
            .ok_or(session::MissingFieldError::new("path spec"))?
            .try_into().map_err(session::ParseError::malformed)?;

        Ok(Request {
            path: path,
        })
    }
}

impl super::Response for Response {

    const RDF_NAME: Option<&'static str> = Some("StatEntry");

    type Proto = rrg_proto::StatEntry;

    fn into_proto(self) -> Self::Proto {
        use rrg_proto::convert::IntoLossy as _;

        rrg_proto::StatEntry {
            pathspec: Some(self.path.into()),
            ..self.metadata.into_lossy()
        }
    }
}

#[cfg(test)]
mod tests {

    use std::fs::File;

    use super::*;

    #[test]
    fn test_non_existent_dir() {
        let tempdir = tempfile::tempdir().unwrap();

        let request = Request {
            path: tempdir.path().join("foo").to_path_buf(),
        };

        let mut session = session::test::Fake::new();
        assert!(handle(&mut session, request).is_err());
    }

    #[test]
    fn test_empty_dir() {
        let tempdir = tempfile::tempdir().unwrap();

        let request = Request {
            path: tempdir.path().to_path_buf(),
        };

        let mut session = session::test::Fake::new();
        assert!(handle(&mut session, request).is_ok());

        assert_eq!(session.reply_count(), 0);
    }

    #[test]
    fn test_dir_with_files() {
        let tempdir = tempfile::tempdir().unwrap();
        File::create(tempdir.path().join("abc")).unwrap();
        File::create(tempdir.path().join("def")).unwrap();
        File::create(tempdir.path().join("ghi")).unwrap();

        let request = Request {
            path: tempdir.path().to_path_buf(),
        };

        let mut session = session::test::Fake::new();
        assert!(handle(&mut session, request).is_ok());

        let mut replies = session.replies().collect::<Vec<&Response>>();
        replies.sort_by_key(|reply| reply.path.clone());

        assert_eq!(replies.len(), 3);

        assert_eq!(replies[0].path, tempdir.path().join("abc"));
        assert!(replies[0].metadata.is_file());

        assert_eq!(replies[1].path, tempdir.path().join("def"));
        assert!(replies[1].metadata.is_file());

        assert_eq!(replies[2].path, tempdir.path().join("ghi"));
        assert!(replies[2].metadata.is_file());
    }

    #[test]
    fn test_dir_with_dirs() {
        let tempdir = tempfile::tempdir().unwrap();
        std::fs::create_dir(tempdir.path().join("abc")).unwrap();
        std::fs::create_dir(tempdir.path().join("def")).unwrap();

        let request = Request {
            path: tempdir.path().to_path_buf(),
        };

        let mut session = session::test::Fake::new();
        assert!(handle(&mut session, request).is_ok());

        let mut replies = session.replies().collect::<Vec<&Response>>();
        replies.sort_by_key(|reply| reply.path.clone());

        assert_eq!(replies.len(), 2);

        assert_eq!(replies[0].path, tempdir.path().join("abc"));
        assert!(replies[0].metadata.is_dir());

        assert_eq!(replies[1].path, tempdir.path().join("def"));
        assert!(replies[1].metadata.is_dir());
    }

    #[test]
    fn test_dir_with_nested_dirs() {
        let tempdir = tempfile::tempdir().unwrap();
        let path = tempdir.path().join("abc").join("def").join("ghi");

        std::fs::create_dir_all(path).unwrap();

        let request = Request {
            path: tempdir.path().to_path_buf(),
        };

        let mut session = session::test::Fake::new();
        assert!(handle(&mut session, request).is_ok());

        assert_eq!(session.reply_count(), 1);
    }

    // Symlinking is supported only on Unix-like systems.
    #[cfg(target_family = "unix")]
    #[test]
    fn test_list_dir_with_links() {
        let tempdir = tempfile::tempdir().unwrap();
        let source = tempdir.path().join("abc");
        let target = tempdir.path().join("def");

        File::create(&source).unwrap();
        std::os::unix::fs::symlink(&source, &target).unwrap();

        let request = Request {
            path: tempdir.path().to_path_buf(),
        };

        let mut session = session::test::Fake::new();
        assert!(handle(&mut session, request).is_ok());

        let mut replies = session.replies().collect::<Vec<&Response>>();
        replies.sort_by_key(|reply| reply.path.clone());

        assert_eq!(replies.len(), 2);

        assert_eq!(replies[0].path, tempdir.path().join("abc"));
        assert!(replies[0].metadata.file_type().is_file());

        assert_eq!(replies[1].path, tempdir.path().join("def"));
        assert!(replies[1].metadata.file_type().is_symlink());
    }

    // macOS mangles Unicode-specific characters in filenames.
    #[cfg_attr(target_os = "macos", ignore)]
    #[test]
    fn test_dir_with_unicode_files() {
        let tempdir = tempfile::tempdir().unwrap();
        File::create(tempdir.path().join("zażółć gęślą jaźń")).unwrap();
        File::create(tempdir.path().join("што й па мору")).unwrap();

        let request = Request {
            path: tempdir.path().to_path_buf(),
        };

        let mut session = session::test::Fake::new();
        assert!(handle(&mut session, request).is_ok());

        let mut replies = session.replies().collect::<Vec<&Response>>();
        replies.sort_by_key(|reply| reply.path.clone());

        assert_eq!(replies.len(), 2);
        assert_eq!(replies[0].path, tempdir.path().join("zażółć gęślą jaźń"));
        assert_eq!(replies[1].path, tempdir.path().join("што й па мору"));
    }
}
<|endoftext|>