<commit_before>use std::{fmt::Write, mem};

use proc_macro2::TokenStream;
use quote::ToTokens;
use rustc_hash::FxHashSet;
use smol_str::SmolStr;
use syn::spanned::Spanned;

use crate::{
    cpp::{fmt_write_err_map, map_any_err_to_our_err, CppForeignMethodSignature, MergeCTypesFlags},
    error::{panic_on_syn_error, DiagnosticError},
    file_cache::FileWriteCache,
    source_registry::SourceId,
    typemap::{ast::DisplayToTokens, CType, CTypes, TypeMap, FROM_VAR_TEMPLATE},
    types::{ForeignEnumInfo, ForeignerClassInfo},
};

pub(in crate::cpp) fn doc_comments_to_c_comments(
    doc_comments: &[String],
    class_comments: bool,
) -> String {
    use std::fmt::Write;
    let mut comments = String::new();
    for (i, comment) in doc_comments.iter().enumerate() {
        if i != 0 {
            comments.push('\n');
        }
        if !class_comments {
            comments.push_str("    ");
        }
        write!(&mut comments, "//{}", comment.trim()).unwrap();
    }
    comments
}

pub(in crate::cpp) fn c_generate_args_with_types(
    f_method: &CppForeignMethodSignature,
    append_comma_if_not_empty: bool,
) -> Result<String, String> {
    use std::fmt::Write;

    let mut buf = String::new();
    for (i, f_type_info) in f_method.input.iter().enumerate() {
        if i > 0 {
            write!(&mut buf, ", ").map_err(fmt_write_err_map)?;
        }
        write!(&mut buf, "{} a_{}", f_type_info.as_ref().name, i).map_err(fmt_write_err_map)?;
    }
    if !buf.is_empty() && append_comma_if_not_empty {
        write!(&mut buf, ", ").map_err(fmt_write_err_map)?;
    }
    Ok(buf)
}

pub(in crate::cpp) fn c_class_type(class: &ForeignerClassInfo) -> String {
    format!("{}Opaque", class.name)
}

pub(in crate::cpp) fn cpp_generate_args_with_types(
    f_method: &CppForeignMethodSignature,
) -> Result<String, String> {
    use std::fmt::Write;
    let mut ret = String::new();
    for (i, f_type_info) in f_method.input.iter().enumerate() {
        if i > 0 {
            write!(&mut ret, ", ").map_err(fmt_write_err_map)?;
        }

        write!(
            &mut ret,
            "{} a_{}",
            if let Some(conv) = f_type_info.cpp_converter.as_ref() {
                conv.typename.clone()
            } else {
                f_type_info.as_ref().name.clone()
            },
            i
        )
        .map_err(fmt_write_err_map)?;
    }
    Ok(ret)
}

pub(in crate::cpp) fn cpp_generate_args_to_call_c(
    f_method: &CppForeignMethodSignature,
) -> Result<String, String> {
    use std::fmt::Write;
    let mut ret = String::new();
    for (i, f_type_info) in f_method.input.iter().enumerate() {
        if i > 0 {
            write!(&mut ret, ", ").map_err(fmt_write_err_map)?;
        }
        if let Some(conv) = f_type_info.cpp_converter.as_ref() {
            let arg_name = format!("a_{}", i);
            let conv_arg = conv
                .converter
                .as_str()
                .replace(FROM_VAR_TEMPLATE, &arg_name);
            write!(&mut ret, "{}", conv_arg)
        } else {
            write!(&mut ret, "a_{}", i)
        }
        .map_err(fmt_write_err_map)?;
    }
    Ok(ret)
}

pub(in crate::cpp) fn cpp_header_name(class: &ForeignerClassInfo) -> String {
    format!("{}.hpp", class.name)
}

pub(in crate::cpp) fn c_header_name(class: &ForeignerClassInfo) -> String {
    format!("c_{}.h", class.name)
}

pub(in crate::cpp) fn cpp_header_name_for_enum(enum_info: &ForeignEnumInfo) -> String {
    format!("c_{}.h", enum_info.name)
}

pub(in crate::cpp) fn cpp_list_required_includes(
    methods: &mut [CppForeignMethodSignature],
) -> Vec<SmolStr> {
    let mut includes = FxHashSet::<SmolStr>::default();
    for m in methods {
        for p in &mut m.input {
            includes.extend(mem::replace(&mut p.provides_by_module, Vec::new()).into_iter());
        }
        includes.extend(mem::replace(&mut m.output.provides_by_module, Vec::new()).into_iter());
    }

    let mut ret: Vec<_> = includes.into_iter().collect();
    ret.sort();
    ret
}

pub(in crate::cpp) fn generate_c_type(
    tmap: &TypeMap,
    c_types: &CTypes,
    flags: MergeCTypesFlags,
    src_id: SourceId,
    out: &mut FileWriteCache,
) -> Result<Vec<TokenStream>, DiagnosticError> {
    let mut rust_code = vec![];

    for c_type in &c_types.types {
        let ctype: &CTypeDescriptor = match c_type {
            CType::Struct(ref s) => s,
            CType::Union(ref u) => u,
        };
        do_generate_c_type(
            tmap,
            flags,
            src_id,
            c_types.header_name.as_str(),
            ctype,
            out,
            &mut rust_code,
        )?;
    }

    Ok(rust_code)
}

trait CTypeDescriptor: ToTokens {
    fn c_type_prefix(&self) -> &'static str;
    fn name(&self) -> &syn::Ident;
    fn fields(&self) -> Result<&syn::FieldsNamed, syn::Error>;
}

impl CTypeDescriptor for syn::ItemStruct {
    fn c_type_prefix(&self) -> &'static str {
        "struct"
    }
    fn name(&self) -> &syn::Ident {
        &self.ident
    }
    fn fields(&self) -> Result<&syn::FieldsNamed, syn::Error> {
        match self.fields {
            syn::Fields::Named(ref x) => Ok(&x),
            _ => Err(syn::Error::new(
                self.fields.span(),
                "only fields with names accepted in this context",
            )),
        }
    }
}

impl CTypeDescriptor for syn::ItemUnion {
    fn c_type_prefix(&self) -> &'static str {
        "union"
    }
    fn name(&self) -> &syn::Ident {
        &self.ident
    }
    fn fields(&self) -> Result<&syn::FieldsNamed, syn::Error> {
        Ok(&self.fields)
    }
}

fn do_generate_c_type(
    tmap: &TypeMap,
    flags: MergeCTypesFlags,
    src_id: SourceId,
    c_type_header_name: &str,
    ctype: &CTypeDescriptor,
    file_out: &mut FileWriteCache,
    rust_code: &mut Vec<TokenStream>,
) -> Result<(), DiagnosticError> {
    use std::io::Write;

    let s_id = format!("{} {}", ctype.c_type_prefix(), ctype.name());
    if file_out.is_item_defined(&s_id) {
        return Ok(());
    }
    let mut rust_layout_test = format!(
        r#"
#[allow(non_snake_case)]
#[test]
fn test_{name}_layout() {{
#[repr(C)]
{c_type_prefix} My{name} {{
"#,
        c_type_prefix = ctype.c_type_prefix(),
        name = ctype.name(),
    );
    let mut mem_out = Vec::<u8>::new();
    writeln!(&mut mem_out, "{} {{", s_id).map_err(map_any_err_to_our_err)?;

    let mut includes = FxHashSet::<&str>::default();

    let mut fields_asserts_code = String::new();
    let fields = &ctype
        .fields()
        .map_err(|err| DiagnosticError::from_syn_err(src_id, err))?
        .named;
    for f in fields {
        let id = f.ident.as_ref().ok_or_else(|| {
            DiagnosticError::new(
                src_id,
                f.span(),
                "only fields with names accepted in this context",
            )
        })?;
        let field_rty = tmap.ty_to_rust_type_checked(&f.ty).ok_or_else(|| {
            DiagnosticError::new(
                src_id,
                f.ty.span(),
                format!("unknown Rust type: '{}'", DisplayToTokens(&f.ty)),
            )
        })?;
        let field_fty = tmap
            .find_foreign_type_related_to_rust_ty(field_rty.to_idx())
            .ok_or_else(|| {
                DiagnosticError::new(src_id, f.ty.span(), "no foreign type related to this type")
            })?;
        for inc in &tmap[field_fty].provides_by_module {
            includes.insert(inc.as_str());
        }

        writeln!(
            &mut mem_out,
            "    {} {};",
            tmap[field_fty].name.as_str(),
            id
        )
        .map_err(map_any_err_to_our_err)?;
        writeln!(&mut rust_layout_test, "{}: {},", id, DisplayToTokens(&f.ty))
            .map_err(map_any_err_to_our_err)?;

        writeln!(
                        &mut fields_asserts_code,
                        r#"
#[allow(dead_code)]
fn check_{struct_name}_{field_name}_type_fn(s: &{struct_name}) -> &{field_type} {{
    &s.{field_name}
}}
    let offset_our = ((&our_s.{field_name} as *const {field_type}) as usize) - ((&our_s as *const My{struct_name}) as usize);
    let offset_user = ((&user_s.{field_name} as *const {field_type}) as usize) - ((&user_s as *const {struct_name}) as usize);
    assert_eq!(offset_our, offset_user);
"#,
                        struct_name = ctype.name(),
                        field_name = id,
                        field_type = DisplayToTokens(&f.ty),
                    )
                    .map_err(map_any_err_to_our_err)?;
    }
    mem_out.write_all(b"};\n").map_err(map_any_err_to_our_err)?;

    writeln!(
        &mut rust_layout_test,
        r#"}}
    assert_eq!(::std::mem::size_of::<My{name}>(), ::std::mem::size_of::<{name}>());
    assert_eq!(::std::mem::align_of::<My{name}>(), ::std::mem::align_of::<{name}>());
    let our_s: My{name} = unsafe {{ ::std::mem::zeroed() }};
    let user_s: {name} = unsafe {{ ::std::mem::zeroed() }};

{fields_asserts}
}}
"#,
        name = ctype.name(),
        fields_asserts = fields_asserts_code,
    )
    .map_err(map_any_err_to_our_err)?;
    match flags {
        MergeCTypesFlags::DefineOnlyCType => {
            let tt: TokenStream = syn::parse_str(&rust_layout_test).unwrap_or_else(|err| {
                panic_on_syn_error("Internal: layout unit test", rust_layout_test, err)
            });
            rust_code.push(tt);
        }
        MergeCTypesFlags::DefineAlsoRustType => {
            rust_code.push(ctype.into_token_stream());
        }
    }
    let self_inc = format!("\"{}\"", c_type_header_name);
    for inc in &includes {
        if self_inc != *inc {
            writeln!(file_out, "#include {}", inc).map_err(map_any_err_to_our_err)?;
        }
    }
    file_out
        .write_all(
            br##"
#ifdef __cplusplus
extern "C" {
#endif
"##,
        )
        .map_err(map_any_err_to_our_err)?;
    file_out
        .write_all(&mem_out)
        .map_err(map_any_err_to_our_err)?;
    file_out
        .write_all(
            br##"
#ifdef __cplusplus
} // extern "C" {
#endif
"##,
        )
        .map_err(map_any_err_to_our_err)?;
    file_out.define_item(s_id);
    Ok(())
}
<commit_msg>c++/refactoring: remove unnecessary use statements<commit_after>use std::{fmt::Write, mem};

use proc_macro2::TokenStream;
use quote::ToTokens;
use rustc_hash::FxHashSet;
use smol_str::SmolStr;
use syn::spanned::Spanned;

use crate::{
    cpp::{fmt_write_err_map, map_any_err_to_our_err, CppForeignMethodSignature, MergeCTypesFlags},
    error::{panic_on_syn_error, DiagnosticError},
    file_cache::FileWriteCache,
    source_registry::SourceId,
    typemap::{ast::DisplayToTokens, CType, CTypes, TypeMap, FROM_VAR_TEMPLATE},
    types::{ForeignEnumInfo, ForeignerClassInfo},
};

pub(in crate::cpp) fn doc_comments_to_c_comments(
    doc_comments: &[String],
    class_comments: bool,
) -> String {
    let mut comments = String::new();
    for (i, comment) in doc_comments.iter().enumerate() {
        if i != 0 {
            comments.push('\n');
        }
        if !class_comments {
            comments.push_str("    ");
        }
        write!(&mut comments, "//{}", comment.trim()).unwrap();
    }
    comments
}

pub(in crate::cpp) fn c_generate_args_with_types(
    f_method: &CppForeignMethodSignature,
    append_comma_if_not_empty: bool,
) -> Result<String, String> {
    let mut buf = String::new();
    for (i, f_type_info) in f_method.input.iter().enumerate() {
        if i > 0 {
            write!(&mut buf, ", ").map_err(fmt_write_err_map)?;
        }
        write!(&mut buf, "{} a_{}", f_type_info.as_ref().name, i).map_err(fmt_write_err_map)?;
    }
    if !buf.is_empty() && append_comma_if_not_empty {
        write!(&mut buf, ", ").map_err(fmt_write_err_map)?;
    }
    Ok(buf)
}

pub(in crate::cpp) fn c_class_type(class: &ForeignerClassInfo) -> String {
    format!("{}Opaque", class.name)
}

pub(in crate::cpp) fn cpp_generate_args_with_types(
    f_method: &CppForeignMethodSignature,
) -> Result<String, String> {
    let mut ret = String::new();
    for (i, f_type_info) in f_method.input.iter().enumerate() {
        if i > 0 {
            write!(&mut ret, ", ").map_err(fmt_write_err_map)?;
        }

        write!(
            &mut ret,
            "{} a_{}",
            if let Some(conv) = f_type_info.cpp_converter.as_ref() {
                conv.typename.clone()
            } else {
                f_type_info.as_ref().name.clone()
            },
            i
        )
        .map_err(fmt_write_err_map)?;
    }
    Ok(ret)
}

pub(in crate::cpp) fn cpp_generate_args_to_call_c(
    f_method: &CppForeignMethodSignature,
) -> Result<String, String> {
    let mut ret = String::new();
    for (i, f_type_info) in f_method.input.iter().enumerate() {
        if i > 0 {
            write!(&mut ret, ", ").map_err(fmt_write_err_map)?;
        }
        if let Some(conv) = f_type_info.cpp_converter.as_ref() {
            let arg_name = format!("a_{}", i);
            let conv_arg = conv
                .converter
                .as_str()
                .replace(FROM_VAR_TEMPLATE, &arg_name);
            write!(&mut ret, "{}", conv_arg)
        } else {
            write!(&mut ret, "a_{}", i)
        }
        .map_err(fmt_write_err_map)?;
    }
    Ok(ret)
}

pub(in crate::cpp) fn cpp_header_name(class: &ForeignerClassInfo) -> String {
    format!("{}.hpp", class.name)
}

pub(in crate::cpp) fn c_header_name(class: &ForeignerClassInfo) -> String {
    format!("c_{}.h", class.name)
}

pub(in crate::cpp) fn cpp_header_name_for_enum(enum_info: &ForeignEnumInfo) -> String {
    format!("c_{}.h", enum_info.name)
}

pub(in crate::cpp) fn cpp_list_required_includes(
    methods: &mut [CppForeignMethodSignature],
) -> Vec<SmolStr> {
    let mut includes = FxHashSet::<SmolStr>::default();
    for m in methods {
        for p in &mut m.input {
            includes.extend(mem::replace(&mut p.provides_by_module, Vec::new()).into_iter());
        }
        includes.extend(mem::replace(&mut m.output.provides_by_module, Vec::new()).into_iter());
    }

    let mut ret: Vec<_> = includes.into_iter().collect();
    ret.sort();
    ret
}

pub(in crate::cpp) fn generate_c_type(
    tmap: &TypeMap,
    c_types: &CTypes,
    flags: MergeCTypesFlags,
    src_id: SourceId,
    out: &mut FileWriteCache,
) -> Result<Vec<TokenStream>, DiagnosticError> {
    let mut rust_code = vec![];

    for c_type in &c_types.types {
        let ctype: &CTypeDescriptor = match c_type {
            CType::Struct(ref s) => s,
            CType::Union(ref u) => u,
        };
        do_generate_c_type(
            tmap,
            flags,
            src_id,
            c_types.header_name.as_str(),
            ctype,
            out,
            &mut rust_code,
        )?;
    }

    Ok(rust_code)
}

trait CTypeDescriptor: ToTokens {
    fn c_type_prefix(&self) -> &'static str;
    fn name(&self) -> &syn::Ident;
    fn fields(&self) -> Result<&syn::FieldsNamed, syn::Error>;
}

impl CTypeDescriptor for syn::ItemStruct {
    fn c_type_prefix(&self) -> &'static str {
        "struct"
    }
    fn name(&self) -> &syn::Ident {
        &self.ident
    }
    fn fields(&self) -> Result<&syn::FieldsNamed, syn::Error> {
        match self.fields {
            syn::Fields::Named(ref x) => Ok(&x),
            _ => Err(syn::Error::new(
                self.fields.span(),
                "only fields with names accepted in this context",
            )),
        }
    }
}

impl CTypeDescriptor for syn::ItemUnion {
    fn c_type_prefix(&self) -> &'static str {
        "union"
    }
    fn name(&self) -> &syn::Ident {
        &self.ident
    }
    fn fields(&self) -> Result<&syn::FieldsNamed, syn::Error> {
        Ok(&self.fields)
    }
}

fn do_generate_c_type(
    tmap: &TypeMap,
    flags: MergeCTypesFlags,
    src_id: SourceId,
    c_type_header_name: &str,
    ctype: &CTypeDescriptor,
    file_out: &mut FileWriteCache,
    rust_code: &mut Vec<TokenStream>,
) -> Result<(), DiagnosticError> {
    use std::io::Write;

    let s_id = format!("{} {}", ctype.c_type_prefix(), ctype.name());
    if file_out.is_item_defined(&s_id) {
        return Ok(());
    }
    let mut rust_layout_test = format!(
        r#"
#[allow(non_snake_case)]
#[test]
fn test_{name}_layout() {{
#[repr(C)]
{c_type_prefix} My{name} {{
"#,
        c_type_prefix = ctype.c_type_prefix(),
        name = ctype.name(),
    );
    let mut mem_out = Vec::<u8>::new();
    writeln!(&mut mem_out, "{} {{", s_id).map_err(map_any_err_to_our_err)?;

    let mut includes = FxHashSet::<&str>::default();

    let mut fields_asserts_code = String::new();
    let fields = &ctype
        .fields()
        .map_err(|err| DiagnosticError::from_syn_err(src_id, err))?
        .named;
    for f in fields {
        let id = f.ident.as_ref().ok_or_else(|| {
            DiagnosticError::new(
                src_id,
                f.span(),
                "only fields with names accepted in this context",
            )
        })?;
        let field_rty = tmap.ty_to_rust_type_checked(&f.ty).ok_or_else(|| {
            DiagnosticError::new(
                src_id,
                f.ty.span(),
                format!("unknown Rust type: '{}'", DisplayToTokens(&f.ty)),
            )
        })?;
        let field_fty = tmap
            .find_foreign_type_related_to_rust_ty(field_rty.to_idx())
            .ok_or_else(|| {
                DiagnosticError::new(src_id, f.ty.span(), "no foreign type related to this type")
            })?;
        for inc in &tmap[field_fty].provides_by_module {
            includes.insert(inc.as_str());
        }

        writeln!(
            &mut mem_out,
            "    {} {};",
            tmap[field_fty].name.as_str(),
            id
        )
        .map_err(map_any_err_to_our_err)?;
        writeln!(&mut rust_layout_test, "{}: {},", id, DisplayToTokens(&f.ty))
            .map_err(map_any_err_to_our_err)?;

        writeln!(
                        &mut fields_asserts_code,
                        r#"
#[allow(dead_code)]
fn check_{struct_name}_{field_name}_type_fn(s: &{struct_name}) -> &{field_type} {{
    &s.{field_name}
}}
    let offset_our = ((&our_s.{field_name} as *const {field_type}) as usize) - ((&our_s as *const My{struct_name}) as usize);
    let offset_user = ((&user_s.{field_name} as *const {field_type}) as usize) - ((&user_s as *const {struct_name}) as usize);
    assert_eq!(offset_our, offset_user);
"#,
                        struct_name = ctype.name(),
                        field_name = id,
                        field_type = DisplayToTokens(&f.ty),
                    )
                    .map_err(map_any_err_to_our_err)?;
    }
    mem_out.write_all(b"};\n").map_err(map_any_err_to_our_err)?;

    writeln!(
        &mut rust_layout_test,
        r#"}}
    assert_eq!(::std::mem::size_of::<My{name}>(), ::std::mem::size_of::<{name}>());
    assert_eq!(::std::mem::align_of::<My{name}>(), ::std::mem::align_of::<{name}>());
    let our_s: My{name} = unsafe {{ ::std::mem::zeroed() }};
    let user_s: {name} = unsafe {{ ::std::mem::zeroed() }};

{fields_asserts}
}}
"#,
        name = ctype.name(),
        fields_asserts = fields_asserts_code,
    )
    .map_err(map_any_err_to_our_err)?;
    match flags {
        MergeCTypesFlags::DefineOnlyCType => {
            let tt: TokenStream = syn::parse_str(&rust_layout_test).unwrap_or_else(|err| {
                panic_on_syn_error("Internal: layout unit test", rust_layout_test, err)
            });
            rust_code.push(tt);
        }
        MergeCTypesFlags::DefineAlsoRustType => {
            rust_code.push(ctype.into_token_stream());
        }
    }
    let self_inc = format!("\"{}\"", c_type_header_name);
    for inc in &includes {
        if self_inc != *inc {
            writeln!(file_out, "#include {}", inc).map_err(map_any_err_to_our_err)?;
        }
    }
    file_out
        .write_all(
            br##"
#ifdef __cplusplus
extern "C" {
#endif
"##,
        )
        .map_err(map_any_err_to_our_err)?;
    file_out
        .write_all(&mem_out)
        .map_err(map_any_err_to_our_err)?;
    file_out
        .write_all(
            br##"
#ifdef __cplusplus
} // extern "C" {
#endif
"##,
        )
        .map_err(map_any_err_to_our_err)?;
    file_out.define_item(s_id);
    Ok(())
}
<|endoftext|>


<commit_before>#![feature(test)] // compiletest_rs requires this attribute
#![cfg_attr(feature = "deny-warnings", deny(warnings))]
#![warn(rust_2018_idioms, unused_lifetimes)]

use compiletest_rs as compiletest;
use compiletest_rs::common::Mode as TestMode;

use std::collections::HashMap;
use std::env::{self, remove_var, set_var, var_os};
use std::ffi::{OsStr, OsString};
use std::fs;
use std::io;
use std::path::{Path, PathBuf};

mod cargo;

// whether to run internal tests or not
const RUN_INTERNAL_TESTS: bool = cfg!(feature = "internal-lints");

/// All crates used in UI tests are listed here
static TEST_DEPENDENCIES: &[&str] = &[
    "clippy_utils",
    "derive_new",
    "if_chain",
    "itertools",
    "quote",
    "regex",
    "serde",
    "serde_derive",
    "syn",
];

// Test dependencies may need an `extern crate` here to ensure that they show up
// in the depinfo file (otherwise cargo thinks they are unused)
#[allow(unused_extern_crates)]
extern crate clippy_utils;
#[allow(unused_extern_crates)]
extern crate derive_new;
#[allow(unused_extern_crates)]
extern crate if_chain;
#[allow(unused_extern_crates)]
extern crate itertools;
#[allow(unused_extern_crates)]
extern crate quote;
#[allow(unused_extern_crates)]
extern crate syn;

/// Produces a string with an `--extern` flag for all UI test crate
/// dependencies.
///
/// The dependency files are located by parsing the depinfo file for this test
/// module. This assumes the `-Z binary-dep-depinfo` flag is enabled. All test
/// dependencies must be added to Cargo.toml at the project root. Test
/// dependencies that are not *directly* used by this test module require an
/// `extern crate` declaration.
fn extern_flags() -> String {
    let current_exe_depinfo = {
        let mut path = env::current_exe().unwrap();
        path.set_extension("d");
        std::fs::read_to_string(path).unwrap()
    };
    let mut crates: HashMap<&str, &str> = HashMap::with_capacity(TEST_DEPENDENCIES.len());
    for line in current_exe_depinfo.lines() {
        // each dependency is expected to have a Makefile rule like `/path/to/crate-hash.rlib:`
        let parse_name_path = || {
            if line.starts_with(char::is_whitespace) {
                return None;
            }
            let path_str = line.strip_suffix(':')?;
            let path = Path::new(path_str);
            if !matches!(path.extension()?.to_str()?, "rlib" | "so" | "dylib" | "dll") {
                return None;
            }
            let (name, _hash) = path.file_stem()?.to_str()?.rsplit_once('-')?;
            // the "lib" prefix is not present for dll files
            let name = name.strip_prefix("lib").unwrap_or(name);
            Some((name, path_str))
        };
        if let Some((name, path)) = parse_name_path() {
            if TEST_DEPENDENCIES.contains(&name) {
                // A dependency may be listed twice if it is available in sysroot,
                // and the sysroot dependencies are listed first. As of the writing,
                // this only seems to apply to if_chain.
                crates.insert(name, path);
            }
        }
    }
    let not_found: Vec<&str> = TEST_DEPENDENCIES
        .iter()
        .copied()
        .filter(|n| !crates.contains_key(n))
        .collect();
    assert!(
        not_found.is_empty(),
        "dependencies not found in depinfo: {:?}",
        not_found
    );
    crates
        .into_iter()
        .map(|(name, path)| format!(" --extern {}={}", name, path))
        .collect()
}

fn default_config() -> compiletest::Config {
    let mut config = compiletest::Config::default();

    if let Ok(filters) = env::var("TESTNAME") {
        config.filters = filters.split(',').map(std::string::ToString::to_string).collect();
    }

    if let Some(path) = option_env!("RUSTC_LIB_PATH") {
        let path = PathBuf::from(path);
        config.run_lib_path = path.clone();
        config.compile_lib_path = path;
    }
    let current_exe_path = std::env::current_exe().unwrap();
    let deps_path = current_exe_path.parent().unwrap();
    let profile_path = deps_path.parent().unwrap();

    // Using `-L dependency={}` enforces that external dependencies are added with `--extern`.
    // This is valuable because a) it allows us to monitor what external dependencies are used
    // and b) it ensures that conflicting rlibs are resolved properly.
    let host_libs = option_env!("HOST_LIBS")
        .map(|p| format!(" -L dependency={}", Path::new(p).join("deps").display()))
        .unwrap_or_default();
    config.target_rustcflags = Some(format!(
        "--emit=metadata -Dwarnings -Zui-testing -L dependency={}{}{}",
        deps_path.display(),
        host_libs,
        extern_flags(),
    ));

    config.build_base = profile_path.join("test");
    config.rustc_path = profile_path.join(if cfg!(windows) {
        "clippy-driver.exe"
    } else {
        "clippy-driver"
    });
    config
}

fn run_ui(cfg: &mut compiletest::Config) {
    cfg.mode = TestMode::Ui;
    cfg.src_base = Path::new("tests").join("ui");
    // use tests/clippy.toml
    let _g = VarGuard::set("CARGO_MANIFEST_DIR", std::fs::canonicalize("tests").unwrap());
    compiletest::run_tests(cfg);
}

fn run_internal_tests(cfg: &mut compiletest::Config) {
    // only run internal tests with the internal-tests feature
    if !RUN_INTERNAL_TESTS {
        return;
    }
    cfg.mode = TestMode::Ui;
    cfg.src_base = Path::new("tests").join("ui-internal");
    compiletest::run_tests(cfg);
}

fn run_ui_toml(config: &mut compiletest::Config) {
    fn run_tests(config: &compiletest::Config, mut tests: Vec<tester::TestDescAndFn>) -> Result<bool, io::Error> {
        let mut result = true;
        let opts = compiletest::test_opts(config);
        for dir in fs::read_dir(&config.src_base)? {
            let dir = dir?;
            if !dir.file_type()?.is_dir() {
                continue;
            }
            let dir_path = dir.path();
            let _g = VarGuard::set("CARGO_MANIFEST_DIR", &dir_path);
            for file in fs::read_dir(&dir_path)? {
                let file = file?;
                let file_path = file.path();
                if file.file_type()?.is_dir() {
                    continue;
                }
                if file_path.extension() != Some(OsStr::new("rs")) {
                    continue;
                }
                let paths = compiletest::common::TestPaths {
                    file: file_path,
                    base: config.src_base.clone(),
                    relative_dir: dir_path.file_name().unwrap().into(),
                };
                let test_name = compiletest::make_test_name(config, &paths);
                let index = tests
                    .iter()
                    .position(|test| test.desc.name == test_name)
                    .expect("The test should be in there");
                result &= tester::run_tests_console(&opts, vec![tests.swap_remove(index)])?;
            }
        }
        Ok(result)
    }

    config.mode = TestMode::Ui;
    config.src_base = Path::new("tests").join("ui-toml").canonicalize().unwrap();

    let tests = compiletest::make_tests(config);

    let res = run_tests(config, tests);
    match res {
        Ok(true) => {},
        Ok(false) => panic!("Some tests failed"),
        Err(e) => {
            panic!("I/O failure during tests: {:?}", e);
        },
    }
}

fn run_ui_cargo(config: &mut compiletest::Config) {
    fn run_tests(
        config: &compiletest::Config,
        filters: &[String],
        mut tests: Vec<tester::TestDescAndFn>,
    ) -> Result<bool, io::Error> {
        let mut result = true;
        let opts = compiletest::test_opts(config);

        for dir in fs::read_dir(&config.src_base)? {
            let dir = dir?;
            if !dir.file_type()?.is_dir() {
                continue;
            }

            // Use the filter if provided
            let dir_path = dir.path();
            for filter in filters {
                if !dir_path.ends_with(filter) {
                    continue;
                }
            }

            for case in fs::read_dir(&dir_path)? {
                let case = case?;
                if !case.file_type()?.is_dir() {
                    continue;
                }

                let src_path = case.path().join("src");

                // When switching between branches, if the previous branch had a test
                // that the current branch does not have, the directory is not removed
                // because an ignored Cargo.lock file exists.
                if !src_path.exists() {
                    continue;
                }

                env::set_current_dir(&src_path)?;
                for file in fs::read_dir(&src_path)? {
                    let file = file?;
                    if file.file_type()?.is_dir() {
                        continue;
                    }

                    // Search for the main file to avoid running a test for each file in the project
                    let file_path = file.path();
                    match file_path.file_name().and_then(OsStr::to_str) {
                        Some("main.rs") => {},
                        _ => continue,
                    }
                    let _g = VarGuard::set("CLIPPY_CONF_DIR", case.path());
                    let paths = compiletest::common::TestPaths {
                        file: file_path,
                        base: config.src_base.clone(),
                        relative_dir: src_path.strip_prefix(&config.src_base).unwrap().into(),
                    };
                    let test_name = compiletest::make_test_name(config, &paths);
                    let index = tests
                        .iter()
                        .position(|test| test.desc.name == test_name)
                        .expect("The test should be in there");
                    result &= tester::run_tests_console(&opts, vec![tests.swap_remove(index)])?;
                }
            }
        }
        Ok(result)
    }

    if cargo::is_rustc_test_suite() {
        return;
    }

    config.mode = TestMode::Ui;
    config.src_base = Path::new("tests").join("ui-cargo").canonicalize().unwrap();

    let tests = compiletest::make_tests(config);

    let current_dir = env::current_dir().unwrap();
    let res = run_tests(config, &config.filters, tests);
    env::set_current_dir(current_dir).unwrap();

    match res {
        Ok(true) => {},
        Ok(false) => panic!("Some tests failed"),
        Err(e) => {
            panic!("I/O failure during tests: {:?}", e);
        },
    }
}

fn prepare_env() {
    set_var("CLIPPY_DISABLE_DOCS_LINKS", "true");
    set_var("__CLIPPY_INTERNAL_TESTS", "true");
    //set_var("RUST_BACKTRACE", "0");
}

#[test]
fn compile_test() {
    prepare_env();
    let mut config = default_config();
    run_ui(&mut config);
    run_ui_toml(&mut config);
    run_ui_cargo(&mut config);
    run_internal_tests(&mut config);
}

/// Restores an env var on drop
#[must_use]
struct VarGuard {
    key: &'static str,
    value: Option<OsString>,
}

impl VarGuard {
    fn set(key: &'static str, val: impl AsRef<OsStr>) -> Self {
        let value = var_os(key);
        set_var(key, val);
        Self { key, value }
    }
}

impl Drop for VarGuard {
    fn drop(&mut self) {
        match self.value.as_deref() {
            None => remove_var(self.key),
            Some(value) => set_var(self.key, value),
        }
    }
}
<commit_msg>Improve depinfo error<commit_after>#![feature(test)] // compiletest_rs requires this attribute
#![cfg_attr(feature = "deny-warnings", deny(warnings))]
#![warn(rust_2018_idioms, unused_lifetimes)]

use compiletest_rs as compiletest;
use compiletest_rs::common::Mode as TestMode;

use std::collections::HashMap;
use std::env::{self, remove_var, set_var, var_os};
use std::ffi::{OsStr, OsString};
use std::fs;
use std::io;
use std::path::{Path, PathBuf};

mod cargo;

// whether to run internal tests or not
const RUN_INTERNAL_TESTS: bool = cfg!(feature = "internal-lints");

/// All crates used in UI tests are listed here
static TEST_DEPENDENCIES: &[&str] = &[
    "clippy_utils",
    "derive_new",
    "if_chain",
    "itertools",
    "quote",
    "regex",
    "serde",
    "serde_derive",
    "syn",
];

// Test dependencies may need an `extern crate` here to ensure that they show up
// in the depinfo file (otherwise cargo thinks they are unused)
#[allow(unused_extern_crates)]
extern crate clippy_utils;
#[allow(unused_extern_crates)]
extern crate derive_new;
#[allow(unused_extern_crates)]
extern crate if_chain;
#[allow(unused_extern_crates)]
extern crate itertools;
#[allow(unused_extern_crates)]
extern crate quote;
#[allow(unused_extern_crates)]
extern crate syn;

/// Produces a string with an `--extern` flag for all UI test crate
/// dependencies.
///
/// The dependency files are located by parsing the depinfo file for this test
/// module. This assumes the `-Z binary-dep-depinfo` flag is enabled. All test
/// dependencies must be added to Cargo.toml at the project root. Test
/// dependencies that are not *directly* used by this test module require an
/// `extern crate` declaration.
fn extern_flags() -> String {
    let current_exe_depinfo = {
        let mut path = env::current_exe().unwrap();
        path.set_extension("d");
        std::fs::read_to_string(path).unwrap()
    };
    let mut crates: HashMap<&str, &str> = HashMap::with_capacity(TEST_DEPENDENCIES.len());
    for line in current_exe_depinfo.lines() {
        // each dependency is expected to have a Makefile rule like `/path/to/crate-hash.rlib:`
        let parse_name_path = || {
            if line.starts_with(char::is_whitespace) {
                return None;
            }
            let path_str = line.strip_suffix(':')?;
            let path = Path::new(path_str);
            if !matches!(path.extension()?.to_str()?, "rlib" | "so" | "dylib" | "dll") {
                return None;
            }
            let (name, _hash) = path.file_stem()?.to_str()?.rsplit_once('-')?;
            // the "lib" prefix is not present for dll files
            let name = name.strip_prefix("lib").unwrap_or(name);
            Some((name, path_str))
        };
        if let Some((name, path)) = parse_name_path() {
            if TEST_DEPENDENCIES.contains(&name) {
                // A dependency may be listed twice if it is available in sysroot,
                // and the sysroot dependencies are listed first. As of the writing,
                // this only seems to apply to if_chain.
                crates.insert(name, path);
            }
        }
    }
    let not_found: Vec<&str> = TEST_DEPENDENCIES
        .iter()
        .copied()
        .filter(|n| !crates.contains_key(n))
        .collect();
    assert!(
        not_found.is_empty(),
        "dependencies not found in depinfo: {:?}\n\
        help: Make sure the `-Z binary-dep-depinfo` rust flag is enabled\n\
        help: Try adding to dev-dependencies in Cargo.toml",
        not_found
    );
    crates
        .into_iter()
        .map(|(name, path)| format!(" --extern {}={}", name, path))
        .collect()
}

fn default_config() -> compiletest::Config {
    let mut config = compiletest::Config::default();

    if let Ok(filters) = env::var("TESTNAME") {
        config.filters = filters.split(',').map(std::string::ToString::to_string).collect();
    }

    if let Some(path) = option_env!("RUSTC_LIB_PATH") {
        let path = PathBuf::from(path);
        config.run_lib_path = path.clone();
        config.compile_lib_path = path;
    }
    let current_exe_path = std::env::current_exe().unwrap();
    let deps_path = current_exe_path.parent().unwrap();
    let profile_path = deps_path.parent().unwrap();

    // Using `-L dependency={}` enforces that external dependencies are added with `--extern`.
    // This is valuable because a) it allows us to monitor what external dependencies are used
    // and b) it ensures that conflicting rlibs are resolved properly.
    let host_libs = option_env!("HOST_LIBS")
        .map(|p| format!(" -L dependency={}", Path::new(p).join("deps").display()))
        .unwrap_or_default();
    config.target_rustcflags = Some(format!(
        "--emit=metadata -Dwarnings -Zui-testing -L dependency={}{}{}",
        deps_path.display(),
        host_libs,
        extern_flags(),
    ));

    config.build_base = profile_path.join("test");
    config.rustc_path = profile_path.join(if cfg!(windows) {
        "clippy-driver.exe"
    } else {
        "clippy-driver"
    });
    config
}

fn run_ui(cfg: &mut compiletest::Config) {
    cfg.mode = TestMode::Ui;
    cfg.src_base = Path::new("tests").join("ui");
    // use tests/clippy.toml
    let _g = VarGuard::set("CARGO_MANIFEST_DIR", std::fs::canonicalize("tests").unwrap());
    compiletest::run_tests(cfg);
}

fn run_internal_tests(cfg: &mut compiletest::Config) {
    // only run internal tests with the internal-tests feature
    if !RUN_INTERNAL_TESTS {
        return;
    }
    cfg.mode = TestMode::Ui;
    cfg.src_base = Path::new("tests").join("ui-internal");
    compiletest::run_tests(cfg);
}

fn run_ui_toml(config: &mut compiletest::Config) {
    fn run_tests(config: &compiletest::Config, mut tests: Vec<tester::TestDescAndFn>) -> Result<bool, io::Error> {
        let mut result = true;
        let opts = compiletest::test_opts(config);
        for dir in fs::read_dir(&config.src_base)? {
            let dir = dir?;
            if !dir.file_type()?.is_dir() {
                continue;
            }
            let dir_path = dir.path();
            let _g = VarGuard::set("CARGO_MANIFEST_DIR", &dir_path);
            for file in fs::read_dir(&dir_path)? {
                let file = file?;
                let file_path = file.path();
                if file.file_type()?.is_dir() {
                    continue;
                }
                if file_path.extension() != Some(OsStr::new("rs")) {
                    continue;
                }
                let paths = compiletest::common::TestPaths {
                    file: file_path,
                    base: config.src_base.clone(),
                    relative_dir: dir_path.file_name().unwrap().into(),
                };
                let test_name = compiletest::make_test_name(config, &paths);
                let index = tests
                    .iter()
                    .position(|test| test.desc.name == test_name)
                    .expect("The test should be in there");
                result &= tester::run_tests_console(&opts, vec![tests.swap_remove(index)])?;
            }
        }
        Ok(result)
    }

    config.mode = TestMode::Ui;
    config.src_base = Path::new("tests").join("ui-toml").canonicalize().unwrap();

    let tests = compiletest::make_tests(config);

    let res = run_tests(config, tests);
    match res {
        Ok(true) => {},
        Ok(false) => panic!("Some tests failed"),
        Err(e) => {
            panic!("I/O failure during tests: {:?}", e);
        },
    }
}

fn run_ui_cargo(config: &mut compiletest::Config) {
    fn run_tests(
        config: &compiletest::Config,
        filters: &[String],
        mut tests: Vec<tester::TestDescAndFn>,
    ) -> Result<bool, io::Error> {
        let mut result = true;
        let opts = compiletest::test_opts(config);

        for dir in fs::read_dir(&config.src_base)? {
            let dir = dir?;
            if !dir.file_type()?.is_dir() {
                continue;
            }

            // Use the filter if provided
            let dir_path = dir.path();
            for filter in filters {
                if !dir_path.ends_with(filter) {
                    continue;
                }
            }

            for case in fs::read_dir(&dir_path)? {
                let case = case?;
                if !case.file_type()?.is_dir() {
                    continue;
                }

                let src_path = case.path().join("src");

                // When switching between branches, if the previous branch had a test
                // that the current branch does not have, the directory is not removed
                // because an ignored Cargo.lock file exists.
                if !src_path.exists() {
                    continue;
                }

                env::set_current_dir(&src_path)?;
                for file in fs::read_dir(&src_path)? {
                    let file = file?;
                    if file.file_type()?.is_dir() {
                        continue;
                    }

                    // Search for the main file to avoid running a test for each file in the project
                    let file_path = file.path();
                    match file_path.file_name().and_then(OsStr::to_str) {
                        Some("main.rs") => {},
                        _ => continue,
                    }
                    let _g = VarGuard::set("CLIPPY_CONF_DIR", case.path());
                    let paths = compiletest::common::TestPaths {
                        file: file_path,
                        base: config.src_base.clone(),
                        relative_dir: src_path.strip_prefix(&config.src_base).unwrap().into(),
                    };
                    let test_name = compiletest::make_test_name(config, &paths);
                    let index = tests
                        .iter()
                        .position(|test| test.desc.name == test_name)
                        .expect("The test should be in there");
                    result &= tester::run_tests_console(&opts, vec![tests.swap_remove(index)])?;
                }
            }
        }
        Ok(result)
    }

    if cargo::is_rustc_test_suite() {
        return;
    }

    config.mode = TestMode::Ui;
    config.src_base = Path::new("tests").join("ui-cargo").canonicalize().unwrap();

    let tests = compiletest::make_tests(config);

    let current_dir = env::current_dir().unwrap();
    let res = run_tests(config, &config.filters, tests);
    env::set_current_dir(current_dir).unwrap();

    match res {
        Ok(true) => {},
        Ok(false) => panic!("Some tests failed"),
        Err(e) => {
            panic!("I/O failure during tests: {:?}", e);
        },
    }
}

fn prepare_env() {
    set_var("CLIPPY_DISABLE_DOCS_LINKS", "true");
    set_var("__CLIPPY_INTERNAL_TESTS", "true");
    //set_var("RUST_BACKTRACE", "0");
}

#[test]
fn compile_test() {
    prepare_env();
    let mut config = default_config();
    run_ui(&mut config);
    run_ui_toml(&mut config);
    run_ui_cargo(&mut config);
    run_internal_tests(&mut config);
}

/// Restores an env var on drop
#[must_use]
struct VarGuard {
    key: &'static str,
    value: Option<OsString>,
}

impl VarGuard {
    fn set(key: &'static str, val: impl AsRef<OsStr>) -> Self {
        let value = var_os(key);
        set_var(key, val);
        Self { key, value }
    }
}

impl Drop for VarGuard {
    fn drop(&mut self) {
        match self.value.as_deref() {
            None => remove_var(self.key),
            Some(value) => set_var(self.key, value),
        }
    }
}
<|endoftext|>


<commit_before>extern crate nanoguirustsdl;
extern crate nanovg;

use nanoguirustsdl::widget::WidgetObjRef;
use nanoguirustsdl::theme::Theme;
use std::rc::Rc;
use std::cell::RefCell;
use std::ops::DerefMut;

#[test]
fn push_child_test() {
    let widget_one = WidgetObjRef::new("one".to_string(), None);
    let widget_two = WidgetObjRef::new("two".to_string(), None);
    let widget_three = WidgetObjRef::new("three".to_string(), None);

    {
        widget_one.push_child(widget_two.0.clone());
        assert_eq!(widget_one.id(), "one".to_string());
        assert_eq!(widget_one.children().len(), 1usize);

        match widget_two.parent().unwrap().upgrade() {
            Some(ref val) => {
                assert_eq!(val.borrow().id(), widget_one.id());
            },
            None => panic!("Parent should have value")
        };
    }
    {
        let widget_one_children = widget_one.children();
        assert_eq!(widget_one_children[0].borrow().id(), "two".to_string());
        let ref first_child_borrowed = widget_one_children[0].borrow();
        let widget_parent = first_child_borrowed.parent();
        let ref widget_parent_upgrade = match *widget_parent {
            Some(ref val) => val.upgrade().unwrap(),
            _ => {panic!("Parent should contain value")}
        };
        let ref widget_parent_borrowed = widget_parent_upgrade.borrow();
        assert_eq!(widget_parent_borrowed.id(), "one".to_string());
    }

    widget_three.push_child(widget_two.0.clone());
    assert_eq!(widget_one.children().len(), 0usize);
    {
        let widget_three_children = widget_three.children();
        assert_eq!(widget_three_children.len(), 1usize);
        assert_eq!(widget_three_children[0].borrow().id(), "two".to_string());

        match widget_two.parent().unwrap().upgrade() {
            Some(ref val) => {
                assert_eq!(val.borrow().id(), widget_three.id());
            },
            None => panic!("Parent should have value")
        };
    }
}

#[test]
fn remove_child_test() {
    let widget_one = WidgetObjRef::new("one".to_string(), None);
    let widget_two = WidgetObjRef::new("two".to_string(), None);

    {
        widget_one.push_child(widget_two.0.clone());
        assert_eq!(widget_one.id(), "one".to_string());
        assert_eq!(widget_one.children().len(), 1usize);
    }

    widget_one.remove_child_by_id(widget_two.id());
    assert_eq!(widget_one.children().len(), 0usize);
    match widget_two.parent() {
        Some(_) => panic!("Parent should be empty"),
        _ => {}
    }

    match widget_one.remove_child_by_id(widget_two.id()) {
        Some(_) => panic!("No child should be present"),
        _ => {}
    }
}

#[test]
fn parent_test() {
    let widget_one = WidgetObjRef::new("one".to_string(), None);
    let widget_two = WidgetObjRef::new("two".to_string(), Some(widget_one.0.clone()));

    match widget_two.parent().unwrap().upgrade() {
        Some(ref val) => {
            assert_eq!(val.borrow().id(), widget_one.id());
        },
        None => panic!("Parent should have value")
    };
}

#[test]
fn theme_test() {
    let nanovg_context = nanovg::Context::create_gl3(nanovg::ANTIALIAS | nanovg::STENCIL_STROKES);
    let theme = Rc::new(RefCell::new(Theme::new(&nanovg_context)));
    theme.borrow_mut().deref_mut().set_standard_font_size(20);
    let widget_one = WidgetObjRef::new("one".to_string(), None);
    widget_one.set_theme(Some(theme.clone()));

    match widget_one.theme() {
        Some(ref val) => {
            assert_eq!(val.borrow().standard_font_size(), theme.borrow().standard_font_size());
        },
        None => panic!("Parent should have value")
    };
}
<commit_msg>Fix unit test for theme<commit_after>extern crate nanoguirustsdl;
extern crate nanovg;
extern crate gl;
extern crate sdl2;

use nanoguirustsdl::widget::WidgetObjRef;
use nanoguirustsdl::theme::Theme;
use std::rc::Rc;
use std::cell::RefCell;
use std::ops::DerefMut;

use sdl2::video::{GLProfile, WindowRef, GLContext};

#[test]
fn push_child_test() {
    let widget_one = WidgetObjRef::new("one".to_string(), None);
    let widget_two = WidgetObjRef::new("two".to_string(), None);
    let widget_three = WidgetObjRef::new("three".to_string(), None);

    {
        widget_one.push_child(widget_two.0.clone());
        assert_eq!(widget_one.id(), "one".to_string());
        assert_eq!(widget_one.children().len(), 1usize);

        match widget_two.parent().unwrap().upgrade() {
            Some(ref val) => {
                assert_eq!(val.borrow().id(), widget_one.id());
            },
            None => panic!("Parent should have value")
        };
    }
    {
        let widget_one_children = widget_one.children();
        assert_eq!(widget_one_children[0].borrow().id(), "two".to_string());
        let ref first_child_borrowed = widget_one_children[0].borrow();
        let widget_parent = first_child_borrowed.parent();
        let ref widget_parent_upgrade = match *widget_parent {
            Some(ref val) => val.upgrade().unwrap(),
            _ => {panic!("Parent should contain value")}
        };
        let ref widget_parent_borrowed = widget_parent_upgrade.borrow();
        assert_eq!(widget_parent_borrowed.id(), "one".to_string());
    }

    widget_three.push_child(widget_two.0.clone());
    assert_eq!(widget_one.children().len(), 0usize);
    {
        let widget_three_children = widget_three.children();
        assert_eq!(widget_three_children.len(), 1usize);
        assert_eq!(widget_three_children[0].borrow().id(), "two".to_string());

        match widget_two.parent().unwrap().upgrade() {
            Some(ref val) => {
                assert_eq!(val.borrow().id(), widget_three.id());
            },
            None => panic!("Parent should have value")
        };
    }
}

#[test]
fn remove_child_test() {
    let widget_one = WidgetObjRef::new("one".to_string(), None);
    let widget_two = WidgetObjRef::new("two".to_string(), None);

    {
        widget_one.push_child(widget_two.0.clone());
        assert_eq!(widget_one.id(), "one".to_string());
        assert_eq!(widget_one.children().len(), 1usize);
    }

    widget_one.remove_child_by_id(widget_two.id());
    assert_eq!(widget_one.children().len(), 0usize);
    match widget_two.parent() {
        Some(_) => panic!("Parent should be empty"),
        _ => {}
    }

    match widget_one.remove_child_by_id(widget_two.id()) {
        Some(_) => panic!("No child should be present"),
        _ => {}
    }
}

#[test]
fn parent_test() {
    let widget_one = WidgetObjRef::new("one".to_string(), None);
    let widget_two = WidgetObjRef::new("two".to_string(), Some(widget_one.0.clone()));

    match widget_two.parent().unwrap().upgrade() {
        Some(ref val) => {
            assert_eq!(val.borrow().id(), widget_one.id());
        },
        None => panic!("Parent should have value")
    };
}

#[test]
fn theme_test() {

    fn init_gl(window: &WindowRef) -> GLContext {
        unsafe {gl::FrontFace(gl::CCW)};
        unsafe {gl::Enable(gl::DEPTH_TEST)};
        unsafe {gl::Enable(gl::SCISSOR_TEST)};
        unsafe {gl::DepthFunc(gl::LEQUAL)};
        unsafe {gl::FrontFace(gl::CCW)};
        unsafe {gl::Enable(gl::CULL_FACE)};
        unsafe {gl::CullFace(gl::BACK)};

        let gl_context = window.gl_create_context().unwrap();
        match window.gl_make_current(&gl_context) {
            Err(val) => {
                println!("make_current error: {}", val);
                panic!("err");
            },
            _ => {}
        }

        gl_context
    }

    let sdl_context = sdl2::init().unwrap();
    let video_subsystem = sdl_context.video().unwrap();

    let gl_attr = video_subsystem.gl_attr();
    gl_attr.set_context_profile(GLProfile::Core);
    gl_attr.set_context_flags().debug().forward_compatible().set();
    gl_attr.set_context_version(3, 2);
    gl_attr.set_stencil_size(8);

    let window = video_subsystem.window("rust-sdl2 demo: Video", 800, 600)
        .position_centered()
        .opengl()
        .build()
        .unwrap();

    gl::load_with(|name| video_subsystem.gl_get_proc_address(name) as *const _);
    let gl_context = init_gl(&window);

        //let mut event_pump = sdl_context.event_pump().unwrap();

    let nanovg_context = nanovg::Context::create_gl3(nanovg::ANTIALIAS | nanovg::STENCIL_STROKES);
    let theme = Rc::new(RefCell::new(Theme::new(&nanovg_context)));
    theme.borrow_mut().deref_mut().set_standard_font_size(20);
    let widget_one = WidgetObjRef::new("one".to_string(), None);
    widget_one.set_theme(Some(theme.clone()));

    match widget_one.theme() {
        Some(ref val) => {
            assert_eq!(val.borrow().standard_font_size(), theme.borrow().standard_font_size());
        },
        None => panic!("Parent should have value")
    };
}
<|endoftext|>


<commit_before>// Copyright (c) The Libra Core Contributors
// SPDX-License-Identifier: Apache-2.0

//! Provides an mpsc (multi-producer single-consumer) channel wrapped in an
//! [`IntGauge`](metrics::IntGauge)
//!
//! The original futures mpsc channels has the behavior that each cloned sender gets a guaranteed
//! slot. There are cases in our codebase that senders need to be cloned to work with combinators
//! like `buffer_unordered`. The bounded mpsc channels turn to be unbounded in this way.  There are
//! great discussions in this [PR](https://github.com/rust-lang-nursery/futures-rs/pull/984). The
//! argument of the current behavior is to have only local limit on each sender, and relies on
//! global coordination for the number of cloned senders.  However, this isn't really feasible in
//! some cases. One solution that came up from the discussion is to have poll_flush call poll_ready
//! (instead of a noop) to make sure the current sender task isn't parked.  For the case that a new
//! cloned sender tries to send a message to a full channel, send executes poll_ready, start_send
//! and poll_flush. The first poll_ready would return Ready because maybe_parked initiated as
//! false. start_send then pushes the message to the internal message queue and parks the sender
//! task.  poll_flush calls poll_ready again, and this time, it would return Pending because the
//! sender task is parked. So the send will block until the receiver removes more messages from the
//! queue and that sender's task is unparked.
//! [This PR](https://github.com/rust-lang-nursery/futures-rs/pull/1671) is supposed to fix this in
//! futures 0.3. It'll be consistent once it's merged.
//!
//! This change does have some implications though.
//! 1. When the channel size is 0, it becomes synchronous. `send` won't finish until the item is
//! taken from the receiver.
//! 2. `send` may fail if the receiver drops after receiving the item.
//!
//! let (tx, rx) = channel::new_test(1);
//! let f_tx = async move {
//!     block_on(tx.send(1)).unwrap();
//! };
//! let f_rx = async move {
//!     let item = block_on(rx.next()).unwrap();
//!     assert_eq!(item, 1);
//! };
//! block_on(join(f_tx, f_rx)).unwrap();
//!
//! For the example above, `tx.send` could fail. Because send has three steps - poll_ready,
//! start_send and poll_flush. After start_send, the rx can receive the item, but if rx gets
//! dropped before poll_flush, it'll trigger disconnected send error. That's why the disconnected
//! error is converted to an Ok in poll_flush.

use futures::{
    channel::mpsc,
    sink::Sink,
    stream::{FusedStream, Stream},
    task::{Context, Poll},
};
use metrics::IntGauge;
use std::pin::Pin;

#[cfg(test)]
mod test;

/// Wrapper around a value with an `IntGauge`
/// It is used to gauge the number of elements in a `mpsc::channel`
#[derive(Clone)]
pub struct WithGauge<T> {
    gauge: IntGauge,
    value: T,
}

/// Similar to `mpsc::Sender`, but with an `IntGauge`
pub type Sender<T> = WithGauge<mpsc::Sender<T>>;
/// Similar to `mpsc::Receiver`, but with an `IntGauge`
pub type Receiver<T> = WithGauge<mpsc::Receiver<T>>;

/// `Sender` implements `Sink` in the same way as `mpsc::Sender`, but it increments the
/// associated `IntGauge` when it sends a message successfully.
impl<T> Sink<T> for Sender<T> {
    type Error = mpsc::SendError;

    fn poll_ready(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
        (*self).value.poll_ready(cx)
    }

    fn start_send(mut self: Pin<&mut Self>, msg: T) -> Result<(), Self::Error> {
        self.gauge.inc();
        (*self).value.start_send(msg).map_err(|e| {
            self.gauge.dec();
            e
        })?;
        Ok(())
    }

    // `poll_flush` would block if `poll_ready` returns pending, which means the channel is at
    // capacity and the sender task is parked.
    fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
        match (*self).value.poll_ready(cx) {
            Poll::Ready(Err(ref e)) if e.is_disconnected() => {
                // If the receiver disconnected, we consider the sink to be flushed.
                Poll::Ready(Ok(()))
            }
            x => x,
        }
    }

    fn poll_close(mut self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
        self.value.disconnect();
        Poll::Ready(Ok(()))
    }
}

impl<T> FusedStream for Receiver<T> {
    fn is_terminated(&self) -> bool {
        self.value.is_terminated()
    }
}

/// `Receiver` implements `Stream` in the same way as `mpsc::Stream`, but it decrements the
/// associated `IntGauge` when it gets polled successfully.
impl<T> Stream for Receiver<T> {
    type Item = T;

    fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<T>> {
        let poll = Pin::new(&mut self.value).poll_next(cx);
        if let Poll::Ready(Some(_)) = poll {
            self.gauge.dec();
        }
        poll
    }
}

/// Similar to `mpsc::channel`, `new` creates a pair of `Sender` and `Receiver`
pub fn new<T>(size: usize, gauge: &IntGauge) -> (Sender<T>, Receiver<T>) {
    gauge.set(0);
    let (sender, receiver) = mpsc::channel(size);
    (
        WithGauge {
            gauge: gauge.clone(),
            value: sender,
        },
        WithGauge {
            gauge: gauge.clone(),
            value: receiver,
        },
    )
}

lazy_static::lazy_static! {
    pub static ref TEST_COUNTER: IntGauge =
        IntGauge::new("TEST_COUNTER", "Counter of network tests").unwrap();
}

pub fn new_test<T>(size: usize) -> (Sender<T>, Receiver<T>) {
    new(size, &TEST_COUNTER)
}
<commit_msg>[channel] clean up channel logic and comment<commit_after>// Copyright (c) The Libra Core Contributors
// SPDX-License-Identifier: Apache-2.0

//! Provides an mpsc (multi-producer single-consumer) channel wrapped in an
//! [`IntGauge`](metrics::IntGauge)

use futures::{
    channel::mpsc,
    sink::Sink,
    stream::{FusedStream, Stream},
    task::{Context, Poll},
};
use metrics::IntGauge;
use std::pin::Pin;

#[cfg(test)]
mod test;

/// Wrapper around a value with an `IntGauge`
/// It is used to gauge the number of elements in a `mpsc::channel`
#[derive(Clone)]
pub struct WithGauge<T> {
    gauge: IntGauge,
    value: T,
}

/// Similar to `mpsc::Sender`, but with an `IntGauge`
pub type Sender<T> = WithGauge<mpsc::Sender<T>>;
/// Similar to `mpsc::Receiver`, but with an `IntGauge`
pub type Receiver<T> = WithGauge<mpsc::Receiver<T>>;

/// `Sender` implements `Sink` in the same way as `mpsc::Sender`, but it increments the
/// associated `IntGauge` when it sends a message successfully.
impl<T> Sink<T> for Sender<T> {
    type Error = mpsc::SendError;

    fn poll_ready(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
        (*self).value.poll_ready(cx)
    }

    fn start_send(mut self: Pin<&mut Self>, msg: T) -> Result<(), Self::Error> {
        self.gauge.inc();
        (*self).value.start_send(msg).map_err(|e| {
            self.gauge.dec();
            e
        })?;
        Ok(())
    }

    fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
        Pin::new(&mut self.value).poll_flush(cx)
    }

    fn poll_close(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
        Pin::new(&mut self.value).poll_close(cx)
    }
}

impl<T> FusedStream for Receiver<T> {
    fn is_terminated(&self) -> bool {
        self.value.is_terminated()
    }
}

/// `Receiver` implements `Stream` in the same way as `mpsc::Stream`, but it decrements the
/// associated `IntGauge` when it gets polled successfully.
impl<T> Stream for Receiver<T> {
    type Item = T;

    fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<T>> {
        let poll = Pin::new(&mut self.value).poll_next(cx);
        if let Poll::Ready(Some(_)) = poll {
            self.gauge.dec();
        }
        poll
    }
}

/// Similar to `mpsc::channel`, `new` creates a pair of `Sender` and `Receiver`
pub fn new<T>(size: usize, gauge: &IntGauge) -> (Sender<T>, Receiver<T>) {
    gauge.set(0);
    let (sender, receiver) = mpsc::channel(size);
    (
        WithGauge {
            gauge: gauge.clone(),
            value: sender,
        },
        WithGauge {
            gauge: gauge.clone(),
            value: receiver,
        },
    )
}

lazy_static::lazy_static! {
    pub static ref TEST_COUNTER: IntGauge =
        IntGauge::new("TEST_COUNTER", "Counter of network tests").unwrap();
}

pub fn new_test<T>(size: usize) -> (Sender<T>, Receiver<T>) {
    new(size, &TEST_COUNTER)
}
<|endoftext|>


<commit_before>/*
 * Copyright 2015-2017 Two Pore Guys, Inc.
 * All rights reserved
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted providing that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 */

extern crate libc;
extern crate block;
use std::fmt;
use std::ffi::{CString, CStr};
use std::collections::hash_map::HashMap;
use std::os::raw::{c_char, c_void};
use std::ptr::{null, null_mut};
use std::mem::transmute;
use std::cell::RefCell;
use std::rc::Weak;
use libc::free;
use block::{Block, ConcreteBlock};

macro_rules! to_cstr {
    ($e:expr) => (CString::new($e).unwrap())
}

macro_rules! null_block {
    () => (transmute::<*mut c_void, _>(null_mut()))
}

#[repr(C)]
#[derive(Debug)]
pub enum RawType
{
    Null,
    Bool,
    Uint64,
    Int64,
    Double,
    Date,
    String,
    Binary,
    Fd,
    Dictionary,
    Array,
    Error,
}

#[repr(C)]
#[derive(Debug)]
pub enum CallStatus
{
    InProgress,
    MoreAvailable,
    Done,
    Error,
    Aborted,
    Ended
}

pub enum RawObject {}
pub enum RawConnection {}
pub enum RawClient {}
pub enum RawCall {}

pub struct Object
{
    value: *mut RawObject,
}

pub struct Connection
{
    value: *mut RawConnection
}

pub struct Client
{
    value: *mut RawClient,
    connection: Connection
}

pub struct Call<'a>
{
    connection: &'a Connection,
    value: *mut RawCall
}

pub struct Instance<'a>
{
    connection: &'a Connection,
    path: String
}

pub struct Interface<'a>
{
    instance: &'a Instance<'a>,
    name: String
}

#[derive(Clone, Debug)]
pub enum Value
{
    Null,
    Bool(bool),
    Uint64(u64),
    Int64(i64),
    Double(f64),
    Date(u64),
    String(String),
    Binary(Vec<u8>),
    Array(Vec<Value>),
    Dictionary(HashMap<String, Value>),
    Object(Object),
    Fd(i32),
    Error(Error)
}

#[derive(Clone, Debug)]
pub struct Error
{
    code: u32,
    message: String,
    stack_trace: Box<Value>,
    extra: Box<Value>
}

#[link(name = "rpc")]
extern {
    /* rpc/object.h */
    pub fn rpc_get_type(value: *mut RawObject) -> RawType;
    pub fn rpc_hash(value: *mut RawObject) -> u32;
    pub fn rpc_null_create() -> *mut RawObject;
    pub fn rpc_bool_create(value: bool) -> *mut RawObject;
    pub fn rpc_bool_get_value(value: *mut RawObject) -> bool;
    pub fn rpc_uint64_create(value: u64) -> *mut RawObject;
    pub fn rpc_uint64_get_value(value: *mut RawObject) -> u64;
    pub fn rpc_int64_create(value: i64) -> *mut RawObject;
    pub fn rpc_int64_get_value(value: *mut RawObject) -> i64;
    pub fn rpc_double_create(value: f64) -> *mut RawObject;
    pub fn rpc_double_get_value(value: *mut RawObject) -> f64;
    pub fn rpc_date_create(value: u64) -> *mut RawObject;
    pub fn rpc_date_get_value(obj: *mut RawObject) -> u64;
    pub fn rpc_string_create(value: *const c_char) -> *mut RawObject;
    pub fn rpc_string_get_string_ptr(value: *mut RawObject) -> *const c_char;
    pub fn rpc_data_create(ptr: *const u8, len: usize, dtor: *const c_void) -> *mut RawObject;
    pub fn rpc_array_create() -> *mut RawObject;
    pub fn rpc_dictionary_create() -> *mut RawObject;
    pub fn rpc_array_append_value(obj: *mut RawObject, value: *mut RawObject);
    pub fn rpc_dictionary_set_value(obj: *mut RawObject, key: *const c_char, value: *mut RawObject);
    pub fn rpc_fd_create(value: i32) -> *mut RawObject;
    pub fn rpc_fd_get_value(obj: *mut RawObject) -> i32;
    pub fn rpc_copy_description(value: *mut RawObject) -> *mut c_char;
    pub fn rpc_retain(value: *mut RawObject) -> *mut RawObject;
    pub fn rpc_release_impl(value: *mut RawObject);

    /* rpc/connection.h */
    pub fn rpc_connection_call(conn: *mut RawConnection, path: *const c_char,
                               interface: *const c_char, name: *const c_char,
                               args: *const RawObject,
                               callback: &Block<(*mut RawCall,), bool>) -> *mut RawCall;

    pub fn rpc_call_status(call: *mut RawCall) -> CallStatus;
    pub fn rpc_call_result(call: *mut RawCall) -> *mut RawObject;
    pub fn rpc_call_continue(call: *mut RawCall);
    pub fn rpc_call_abort(call: *mut RawCall);
    pub fn rpc_call_wait(call: *mut RawCall);

    /* rpc/client.h */
    pub fn rpc_client_create(uri: *const c_char, params: *const RawObject) -> *mut RawClient;
    pub fn rpc_client_get_connection(client: *mut RawClient) -> *mut RawConnection;
}

pub trait Create<T> {
    fn create(value: T) -> Object;
}

impl Clone for Object {
    fn clone(&self) -> Object {
        unsafe {
            return Object { value: rpc_retain(self.value) }
        }
    }
}

impl Drop for Object {
    fn drop(&mut self) {
        unsafe {
            rpc_release_impl(self.value)
        }
    }
}

impl<T> Create<T> for Object where Value: std::convert::From<T> {
    fn create(value: T) -> Object {
        Object::new(Value::from(value))
    }
}

impl From<bool> for Value {
    fn from(value: bool) -> Value {
        Value::Bool(value)
    }
}

impl From<u64> for Value {
    fn from(value: u64) -> Value {
        Value::Uint64(value)
    }
}

impl From<i64> for Value {
    fn from(value: i64) -> Value {
        Value::Int64(value)
    }
}

impl From<f64> for Value {
    fn from(value: f64) -> Value {
        Value::Double(value)
    }
}

impl<'a> From<&'a str> for Value {
    fn from(value: &str) -> Value {
        Value::String(String::from(value))
    }
}

impl From<String> for Value {
    fn from(value: String) -> Value {
        Value::String(value)
    }
}

impl From<Vec<u8>> for Value {
    fn from(value: Vec<u8>) -> Value {
        Value::Binary(value)
    }
}

impl<'a> From<&'a [Value]> for Value {
    fn from(value: &[Value]) -> Value {
        Value::Array(value.to_vec())
    }
}

impl From<Vec<Value>> for Value {
    fn from(value: Vec<Value>) -> Value {
        Value::Array(value)
    }
}

impl<'a> From<HashMap<&'a str, Value>> for Value {
    fn from(value: HashMap<&str, Value>) -> Value {
        Value::Dictionary(value.iter().map( | ( & k, v) |
            (String::from(k), v.clone())
        ).collect())
    }
}

impl From<HashMap<String, Value>> for Value {
    fn from(value: HashMap<String, Value>) -> Value {
        Value::Dictionary(value)
    }
}

impl Object {
    pub fn new(value: Value) -> Object {
        unsafe {
            let obj = match value {
                Value::Null => rpc_null_create(),
                Value::Bool(val) => rpc_bool_create(val),
                Value::Uint64(val) => rpc_uint64_create(val),
                Value::Int64(val) => rpc_int64_create(val),
                Value::Double(val) => rpc_double_create(val),
                Value::Date(val) => rpc_date_create(val),
                Value::Fd(val) => rpc_fd_create(val),
                Value::Binary(ref val) => rpc_data_create(val.as_ptr(), val.len(), null()),
                Value::Object(ref val) => rpc_retain(val.value),
                Value::String(ref val) => {
                    let c_val = to_cstr!(val.as_str());
                    rpc_string_create(c_val.as_ptr())
                },
                Value::Array(val) => {
                    let arr = rpc_array_create();
                    for i in val {
                        rpc_array_append_value(arr, Object::new(i).value);
                    }

                    arr
                },
                Value::Dictionary(val) => {
                    let dict = rpc_dictionary_create();
                    for (k, v) in val {
                        let c_key = to_cstr!(k.as_str());
                        rpc_dictionary_set_value(dict, c_key.as_ptr(), Object::new(v).value);
                    }

                    dict
                },
                Value::Error(val) => {
                    rpc_null_create()
                }
            };

            return Object { value: obj };
        }
    }

    pub fn get_raw_type(&self) -> RawType {
        unsafe {
            rpc_get_type(self.value)
        }
    }

    pub fn unpack(&self) -> Value {
        unsafe {
            match self.get_raw_type() {
                RawType::Null => Value::Null,
                RawType::Bool => Value::Bool(rpc_bool_get_value(self.value)),
                RawType::Uint64 => Value::Uint64(rpc_uint64_get_value(self.value)),
                RawType::Int64 => Value::Int64(rpc_int64_get_value(self.value)),
                RawType::Double => Value::Double(rpc_double_get_value(self.value)),
                RawType::String => Value::String(String::from(CStr::from_ptr(
                    rpc_string_get_string_ptr(self.value)).to_str().unwrap())),
                RawType::Date => Value::Date(rpc_date_get_value(self.value)),
                RawType::Binary => Value::Null,
                RawType::Fd => Value::Fd(rpc_fd_get_value(self.value)),
                RawType::Array => Value::Null,
                RawType::Dictionary => Value::Null,
                RawType::Error => Value::Null,
            }
        }
    }
}

impl std::hash::Hash for Object {
    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {

    }
}

impl fmt::Debug for Object {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        unsafe {
            let descr = rpc_copy_description(self.value);
            let str = CString::from_raw(descr);
            let result = f.write_str(str.to_str().unwrap());
            free(descr as *mut libc::c_void);

            result
        }
    }
}

impl<'a> Call<'a> {
    pub fn result(&self) -> Option<Value> {
        unsafe {
            let result = rpc_call_result(self.value);

            match result.is_null() {
                true => Option::None,
                false => Option::Some(Object { value: result }.unpack())
            }
        }
    }

    pub fn status(&self) -> CallStatus {
        unsafe {
            rpc_call_status(self.value)
        }
    }

    pub fn abort(&mut self) {
        unsafe {
            rpc_call_abort(self.value);
        }
    }

    pub fn resume(&mut self) {

    }

    pub fn wait(&mut self) {
        unsafe {
            rpc_call_wait(self.value);
        }
    }
}

impl Connection {
    pub fn call(&self, name: &str, path: &str, interface: &str, args: &[Value]) -> Call {
        unsafe {
            let c_path = to_cstr!(path);
            let c_interface = to_cstr!(interface);
            let c_name = to_cstr!(name);
            let call = rpc_connection_call(
                self.value, c_path.as_ptr(), c_interface.as_ptr(), c_name.as_ptr(),
                Object::create(args).value, null_block!()
            );

            Call { value: call, connection: self }
        }
    }

    pub fn call_sync(&self, name: &str, path: &str, interface: &str,
                     args: &[Value]) -> Option<Value> {
        let mut c = self.call(name, path, interface, args);
        c.wait();
        c.result()
    }

    pub fn call_async(&self, name: &str, path: &str, interface: &str, args: &[Value],
                      callback: Box<Fn(&Call) -> bool>) {
        unsafe {
            let c_path = to_cstr!(path);
            let c_interface = to_cstr!(interface);
            let c_name = to_cstr!(name);
            let block = ConcreteBlock::new(move |raw_call| {
                let call = Call { connection: self, value: raw_call };
                callback(&call)
            });

            rpc_connection_call(
                self.value, c_path.as_ptr(), c_interface.as_ptr(), c_name.as_ptr(),
                Object::create(args).value, &block
            );
        }
    }
}

impl Client {
    pub fn connect(uri: &str) -> Client {
        unsafe {
            let c_uri = to_cstr!(uri);
            let client = rpc_client_create(c_uri.as_ptr(), null());

            Client {
                value: client,
                connection: Connection { value: rpc_client_get_connection(client)}
            }
        }
    }

    pub fn connection(&self) -> &Connection {
        &self.connection
    }

    pub fn instance(&self, path: &str) -> Instance {
        Instance { connection: &self.connection(), path: String::from(path) }
    }
}

impl<'a> Instance<'a> {
    /*pub fn interfaces(&self) -> HashMap<String, Interface> {
        self.connection.call_sync("get_interfaces", self.path.as_str(), "com.twoporeguys.momd.Introspectable", &[][..]).unwrap()
    }*/

    pub fn interface(&self, name: &str) -> Interface {
        Interface { instance: self, name: String::from(name) }
    }
}

/*
impl Interface {
    pub fn call(method: &str, args: &[&Value]) -> Call {

    }

    pub fn call_sync(method: &str, args: &[&Value]) -> Result<Value> {

    }

    pub fn get(property: &str) -> Result<Value> {

    }

    pub fn set(property: &str, value: &Value) -> Result<()> {

    }
}*/
<commit_msg>Implement interfaces getter in Rust.<commit_after>/*
 * Copyright 2015-2017 Two Pore Guys, Inc.
 * All rights reserved
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted providing that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 */

extern crate libc;
extern crate block;
use std::fmt;
use std::ffi::{CString, CStr};
use std::collections::hash_map::HashMap;
use std::os::raw::{c_char, c_void};
use std::ptr::{null, null_mut};
use std::mem::transmute;
use std::cell::RefCell;
use std::rc::Weak;
use libc::free;
use block::{Block, ConcreteBlock};

macro_rules! to_cstr {
    ($e:expr) => (CString::new($e).unwrap())
}

macro_rules! null_block {
    () => (transmute::<*mut c_void, _>(null_mut()))
}

#[repr(C)]
#[derive(Debug)]
pub enum RawType
{
    Null,
    Bool,
    Uint64,
    Int64,
    Double,
    Date,
    String,
    Binary,
    Fd,
    Dictionary,
    Array,
    Error,
}

#[repr(C)]
#[derive(Debug)]
pub enum CallStatus
{
    InProgress,
    MoreAvailable,
    Done,
    Error,
    Aborted,
    Ended
}

pub enum RawObject {}
pub enum RawConnection {}
pub enum RawClient {}
pub enum RawCall {}

pub struct Object
{
    value: *mut RawObject,
}

pub struct Connection
{
    value: *mut RawConnection
}

pub struct Client
{
    value: *mut RawClient,
    connection: Connection
}

pub struct Call<'a>
{
    connection: &'a Connection,
    value: *mut RawCall
}

pub struct Instance<'a>
{
    connection: &'a Connection,
    path: String
}

pub struct Interface<'a>
{
    instance: &'a Instance<'a>,
    name: String
}

#[derive(Clone, Debug)]
pub enum Value
{
    Null,
    Bool(bool),
    Uint64(u64),
    Int64(i64),
    Double(f64),
    Date(u64),
    String(String),
    Binary(Vec<u8>),
    Array(Vec<Value>),
    Dictionary(HashMap<String, Value>),
    Object(Object),
    Fd(i32),
    Error(Error)
}

#[derive(Clone, Debug)]
pub struct Error
{
    code: u32,
    message: String,
    stack_trace: Box<Value>,
    extra: Box<Value>
}

#[link(name = "rpc")]
extern {
    /* rpc/object.h */
    pub fn rpc_get_type(value: *mut RawObject) -> RawType;
    pub fn rpc_hash(value: *mut RawObject) -> u32;
    pub fn rpc_null_create() -> *mut RawObject;
    pub fn rpc_bool_create(value: bool) -> *mut RawObject;
    pub fn rpc_bool_get_value(value: *mut RawObject) -> bool;
    pub fn rpc_uint64_create(value: u64) -> *mut RawObject;
    pub fn rpc_uint64_get_value(value: *mut RawObject) -> u64;
    pub fn rpc_int64_create(value: i64) -> *mut RawObject;
    pub fn rpc_int64_get_value(value: *mut RawObject) -> i64;
    pub fn rpc_double_create(value: f64) -> *mut RawObject;
    pub fn rpc_double_get_value(value: *mut RawObject) -> f64;
    pub fn rpc_date_create(value: u64) -> *mut RawObject;
    pub fn rpc_date_get_value(obj: *mut RawObject) -> u64;
    pub fn rpc_string_create(value: *const c_char) -> *mut RawObject;
    pub fn rpc_string_get_string_ptr(value: *mut RawObject) -> *const c_char;
    pub fn rpc_data_create(ptr: *const u8, len: usize, dtor: *const c_void) -> *mut RawObject;
    pub fn rpc_array_create() -> *mut RawObject;
    pub fn rpc_dictionary_create() -> *mut RawObject;
    pub fn rpc_array_append_value(obj: *mut RawObject, value: *mut RawObject);
    pub fn rpc_dictionary_set_value(obj: *mut RawObject, key: *const c_char, value: *mut RawObject);
    pub fn rpc_fd_create(value: i32) -> *mut RawObject;
    pub fn rpc_fd_get_value(obj: *mut RawObject) -> i32;
    pub fn rpc_copy_description(value: *mut RawObject) -> *mut c_char;
    pub fn rpc_retain(value: *mut RawObject) -> *mut RawObject;
    pub fn rpc_release_impl(value: *mut RawObject);

    /* rpc/connection.h */
    pub fn rpc_connection_call(conn: *mut RawConnection, path: *const c_char,
                               interface: *const c_char, name: *const c_char,
                               args: *const RawObject,
                               callback: &Block<(*mut RawCall,), bool>) -> *mut RawCall;

    pub fn rpc_call_status(call: *mut RawCall) -> CallStatus;
    pub fn rpc_call_result(call: *mut RawCall) -> *mut RawObject;
    pub fn rpc_call_continue(call: *mut RawCall);
    pub fn rpc_call_abort(call: *mut RawCall);
    pub fn rpc_call_wait(call: *mut RawCall);

    /* rpc/client.h */
    pub fn rpc_client_create(uri: *const c_char, params: *const RawObject) -> *mut RawClient;
    pub fn rpc_client_get_connection(client: *mut RawClient) -> *mut RawConnection;
}

pub trait Create<T> {
    fn create(value: T) -> Object;
}

impl Clone for Object {
    fn clone(&self) -> Object {
        unsafe {
            return Object { value: rpc_retain(self.value) }
        }
    }
}

impl Drop for Object {
    fn drop(&mut self) {
        unsafe {
            rpc_release_impl(self.value)
        }
    }
}

impl<T> Create<T> for Object where Value: std::convert::From<T> {
    fn create(value: T) -> Object {
        Object::new(Value::from(value))
    }
}

impl From<bool> for Value {
    fn from(value: bool) -> Value {
        Value::Bool(value)
    }
}

impl From<u64> for Value {
    fn from(value: u64) -> Value {
        Value::Uint64(value)
    }
}

impl From<i64> for Value {
    fn from(value: i64) -> Value {
        Value::Int64(value)
    }
}

impl From<f64> for Value {
    fn from(value: f64) -> Value {
        Value::Double(value)
    }
}

impl<'a> From<&'a str> for Value {
    fn from(value: &str) -> Value {
        Value::String(String::from(value))
    }
}

impl From<String> for Value {
    fn from(value: String) -> Value {
        Value::String(value)
    }
}

impl From<Vec<u8>> for Value {
    fn from(value: Vec<u8>) -> Value {
        Value::Binary(value)
    }
}

impl<'a> From<&'a [Value]> for Value {
    fn from(value: &[Value]) -> Value {
        Value::Array(value.to_vec())
    }
}

impl From<Vec<Value>> for Value {
    fn from(value: Vec<Value>) -> Value {
        Value::Array(value)
    }
}

impl<'a> From<HashMap<&'a str, Value>> for Value {
    fn from(value: HashMap<&str, Value>) -> Value {
        Value::Dictionary(value.iter().map( | ( & k, v) |
            (String::from(k), v.clone())
        ).collect())
    }
}

impl From<HashMap<String, Value>> for Value {
    fn from(value: HashMap<String, Value>) -> Value {
        Value::Dictionary(value)
    }
}

impl Object {
    pub fn new(value: Value) -> Object {
        unsafe {
            let obj = match value {
                Value::Null => rpc_null_create(),
                Value::Bool(val) => rpc_bool_create(val),
                Value::Uint64(val) => rpc_uint64_create(val),
                Value::Int64(val) => rpc_int64_create(val),
                Value::Double(val) => rpc_double_create(val),
                Value::Date(val) => rpc_date_create(val),
                Value::Fd(val) => rpc_fd_create(val),
                Value::Binary(ref val) => rpc_data_create(val.as_ptr(), val.len(), null()),
                Value::Object(ref val) => rpc_retain(val.value),
                Value::String(ref val) => {
                    let c_val = to_cstr!(val.as_str());
                    rpc_string_create(c_val.as_ptr())
                },
                Value::Array(val) => {
                    let arr = rpc_array_create();
                    for i in val {
                        rpc_array_append_value(arr, Object::new(i).value);
                    }

                    arr
                },
                Value::Dictionary(val) => {
                    let dict = rpc_dictionary_create();
                    for (k, v) in val {
                        let c_key = to_cstr!(k.as_str());
                        rpc_dictionary_set_value(dict, c_key.as_ptr(), Object::new(v).value);
                    }

                    dict
                },
                Value::Error(val) => {
                    rpc_null_create()
                }
            };

            return Object { value: obj };
        }
    }

    pub fn get_raw_type(&self) -> RawType {
        unsafe {
            rpc_get_type(self.value)
        }
    }

    pub fn unpack(&self) -> Value {
        unsafe {
            match self.get_raw_type() {
                RawType::Null => Value::Null,
                RawType::Bool => Value::Bool(rpc_bool_get_value(self.value)),
                RawType::Uint64 => Value::Uint64(rpc_uint64_get_value(self.value)),
                RawType::Int64 => Value::Int64(rpc_int64_get_value(self.value)),
                RawType::Double => Value::Double(rpc_double_get_value(self.value)),
                RawType::String => Value::String(String::from(CStr::from_ptr(
                    rpc_string_get_string_ptr(self.value)).to_str().unwrap())),
                RawType::Date => Value::Date(rpc_date_get_value(self.value)),
                RawType::Binary => Value::Null,
                RawType::Fd => Value::Fd(rpc_fd_get_value(self.value)),
                RawType::Array => Value::Null,
                RawType::Dictionary => Value::Null,
                RawType::Error => Value::Null,
            }
        }
    }
}

impl std::hash::Hash for Object {
    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {

    }
}

impl fmt::Debug for Object {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        unsafe {
            let descr = rpc_copy_description(self.value);
            let str = CString::from_raw(descr);
            let result = f.write_str(str.to_str().unwrap());
            free(descr as *mut libc::c_void);

            result
        }
    }
}

impl<'a> Call<'a> {
    pub fn result(&self) -> Option<Value> {
        unsafe {
            let result = rpc_call_result(self.value);

            match result.is_null() {
                true => Option::None,
                false => Option::Some(Object { value: result }.unpack())
            }
        }
    }

    pub fn status(&self) -> CallStatus {
        unsafe {
            rpc_call_status(self.value)
        }
    }

    pub fn abort(&mut self) {
        unsafe {
            rpc_call_abort(self.value);
        }
    }

    pub fn resume(&mut self) {

    }

    pub fn wait(&mut self) {
        unsafe {
            rpc_call_wait(self.value);
        }
    }
}

impl Connection {
    pub fn call(&self, name: &str, path: &str, interface: &str, args: &[Value]) -> Call {
        unsafe {
            let c_path = to_cstr!(path);
            let c_interface = to_cstr!(interface);
            let c_name = to_cstr!(name);
            let call = rpc_connection_call(
                self.value, c_path.as_ptr(), c_interface.as_ptr(), c_name.as_ptr(),
                Object::create(args).value, null_block!()
            );

            Call { value: call, connection: self }
        }
    }

    pub fn call_sync(&self, name: &str, path: &str, interface: &str,
                     args: &[Value]) -> Option<Value> {
        let mut c = self.call(name, path, interface, args);
        c.wait();
        c.result()
    }

    pub fn call_async(&self, name: &str, path: &str, interface: &str, args: &[Value],
                      callback: Box<Fn(&Call) -> bool>) {
        unsafe {
            let c_path = to_cstr!(path);
            let c_interface = to_cstr!(interface);
            let c_name = to_cstr!(name);
            let block = ConcreteBlock::new(move |raw_call| {
                let call = Call { connection: self, value: raw_call };
                callback(&call)
            });

            rpc_connection_call(
                self.value, c_path.as_ptr(), c_interface.as_ptr(), c_name.as_ptr(),
                Object::create(args).value, &block
            );
        }
    }
}

impl Client {
    pub fn connect(uri: &str) -> Client {
        unsafe {
            let c_uri = to_cstr!(uri);
            let client = rpc_client_create(c_uri.as_ptr(), null());

            Client {
                value: client,
                connection: Connection { value: rpc_client_get_connection(client)}
            }
        }
    }

    pub fn connection(&self) -> &Connection {
        &self.connection
    }

    pub fn instance(&self, path: &str) -> Instance {
        Instance { connection: &self.connection(), path: String::from(path) }
    }
}

impl<'a> Instance<'a> {
    pub fn interfaces(&self) -> HashMap<String, Interface> {
        self.connection.call_sync(
            "get_interfaces",
            self.path.as_str(),
            "com.twoporeguys.librpc.Introspectable",
            &[][..]
        ).unwrap()
    }

    pub fn interface(&self, name: &str) -> Interface {
        Interface { instance: self, name: String::from(name) }
    }
}

impl Interface {
    pub fn call(method: &str, args: &[&Value]) -> Call {

    }

    pub fn call_sync(method: &str, args: &[&Value]) -> Result<Value> {

    }

    pub fn get(property: &str) -> Result<Value> {

    }

    pub fn set(property: &str, value: &Value) -> Result<()> {

    }
}
<|endoftext|>


<commit_before>// Copyright 2017 Serde Developers
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

#![feature(lang_items, start, compiler_builtins_lib)]
#![no_std]

extern crate libc;

#[start]
fn start(_argc: isize, _argv: *const *const u8) -> isize {
    0
}

#[lang = "eh_personality"]
#[no_mangle]
pub extern fn rust_eh_personality() {}

#[lang = "eh_unwind_resume"]
#[no_mangle]
pub extern fn rust_eh_unwind_resume() {}

#[lang = "panic_fmt"]
#[no_mangle]
pub extern fn rust_begin_panic(_msg: core::fmt::Arguments,
                               _file: &'static str,
                               _line: u32) -> ! {
    unsafe {
        libc::abort()
    }
}

//////////////////////////////////////////////////////////////////////////////

#[macro_use]
extern crate serde_derive;

#[derive(Serialize, Deserialize)]
struct Unit;

#[derive(Serialize, Deserialize)]
struct Newtype(u8);

#[derive(Serialize, Deserialize)]
struct Tuple(u8, u8);

#[derive(Serialize, Deserialize)]
struct Struct { f: u8 }

#[derive(Serialize, Deserialize)]
enum Enum {
    Unit,
    Newtype(u8),
    Tuple(u8, u8),
    Struct { f: u8 },
}
<commit_msg>Remove eh_unwind_resume lang item<commit_after>// Copyright 2017 Serde Developers
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

#![feature(lang_items, start, compiler_builtins_lib)]
#![no_std]

extern crate libc;

#[start]
fn start(_argc: isize, _argv: *const *const u8) -> isize {
    0
}

#[lang = "eh_personality"]
#[no_mangle]
pub extern fn rust_eh_personality() {}

#[lang = "panic_fmt"]
#[no_mangle]
pub extern fn rust_begin_panic(_msg: core::fmt::Arguments,
                               _file: &'static str,
                               _line: u32) -> ! {
    unsafe {
        libc::abort()
    }
}

//////////////////////////////////////////////////////////////////////////////

#[macro_use]
extern crate serde_derive;

#[derive(Serialize, Deserialize)]
struct Unit;

#[derive(Serialize, Deserialize)]
struct Newtype(u8);

#[derive(Serialize, Deserialize)]
struct Tuple(u8, u8);

#[derive(Serialize, Deserialize)]
struct Struct { f: u8 }

#[derive(Serialize, Deserialize)]
enum Enum {
    Unit,
    Newtype(u8),
    Tuple(u8, u8),
    Struct { f: u8 },
}
<|endoftext|>