<commit_before>use hir::{MacroDef, ModuleDef, Name, PathResolution, ScopeDef, SemanticsScope};
use ra_ide_db::{
    defs::{classify_name_ref, Definition, NameRefClass},
    RootDatabase,
};
use ra_syntax::{ast, match_ast, AstNode, SyntaxNode, SyntaxToken, T};

use crate::{
    assist_context::{AssistBuilder, AssistContext, Assists},
    AssistId, AssistKind,
};

// Assist: expand_glob_import
//
// Expands glob imports.
//
// ```
// mod foo {
//     pub struct Bar;
//     pub struct Baz;
// }
//
// use foo::*<|>;
//
// fn qux(bar: Bar, baz: Baz) {}
// ```
// ->
// ```
// mod foo {
//     pub struct Bar;
//     pub struct Baz;
// }
//
// use foo::{Baz, Bar};
//
// fn qux(bar: Bar, baz: Baz) {}
// ```
pub(crate) fn expand_glob_import(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
    let star = ctx.find_token_at_offset(T![*])?;
    let mod_path = find_mod_path(&star)?;

    let source_file = ctx.source_file();
    let scope = ctx.sema.scope_at_offset(source_file.syntax(), ctx.offset());

    let defs_in_mod = find_defs_in_mod(ctx, scope, &mod_path)?;
    let name_refs_in_source_file =
        source_file.syntax().descendants().filter_map(ast::NameRef::cast).collect();
    let used_names = find_used_names(ctx, defs_in_mod, name_refs_in_source_file);

    let parent = star.parent().parent()?;
    acc.add(
        AssistId("expand_glob_import", AssistKind::RefactorRewrite),
        "Expand glob import",
        parent.text_range(),
        |builder| {
            replace_ast(builder, &parent, mod_path, used_names);
        },
    )
}

fn find_mod_path(star: &SyntaxToken) -> Option<ast::Path> {
    star.ancestors().find_map(|n| ast::UseTree::cast(n).and_then(|u| u.path()))
}

#[derive(PartialEq)]
enum Def {
    ModuleDef(ModuleDef),
    MacroDef(MacroDef),
}

impl Def {
    fn name(&self, db: &RootDatabase) -> Option<Name> {
        match self {
            Def::ModuleDef(def) => def.name(db),
            Def::MacroDef(def) => def.name(db),
        }
    }
}

fn find_defs_in_mod(
    ctx: &AssistContext,
    from: SemanticsScope<'_>,
    path: &ast::Path,
) -> Option<Vec<Def>> {
    let hir_path = ctx.sema.lower_path(&path)?;
    let module = if let Some(PathResolution::Def(ModuleDef::Module(module))) =
        from.resolve_hir_path_qualifier(&hir_path)
    {
        module
    } else {
        return None;
    };

    let module_scope = module.scope(ctx.db(), from.module());

    let mut defs = vec![];
    for (_, def) in module_scope {
        match def {
            ScopeDef::ModuleDef(def) => defs.push(Def::ModuleDef(def)),
            ScopeDef::MacroDef(def) => defs.push(Def::MacroDef(def)),
            _ => continue,
        }
    }

    Some(defs)
}

fn find_used_names(
    ctx: &AssistContext,
    defs_in_mod: Vec<Def>,
    name_refs_in_source_file: Vec<ast::NameRef>,
) -> Vec<Name> {
    let defs_in_source_file = name_refs_in_source_file
        .iter()
        .filter_map(|r| classify_name_ref(&ctx.sema, r))
        .filter_map(|rc| match rc {
            NameRefClass::Definition(Definition::ModuleDef(def)) => Some(Def::ModuleDef(def)),
            NameRefClass::Definition(Definition::Macro(def)) => Some(Def::MacroDef(def)),
            _ => None,
        })
        .collect::<Vec<Def>>();

    defs_in_mod
        .iter()
        .filter(|d| defs_in_source_file.contains(d))
        .filter_map(|d| d.name(ctx.db()))
        .collect()
}

fn replace_ast(
    builder: &mut AssistBuilder,
    node: &SyntaxNode,
    path: ast::Path,
    used_names: Vec<Name>,
) {
    let new_use_tree_list = ast::make::use_tree_list(used_names.iter().map(|n| {
        ast::make::use_tree(ast::make::path_from_text(&n.to_string()), None, None, false)
    }));

    match_ast! {
        match node {
            ast::UseTree(use_tree) => {
                builder.replace_ast(use_tree, make_use_tree(path, new_use_tree_list));
            },
            ast::UseTreeList(use_tree_list) => {
                builder.replace_ast(use_tree_list, new_use_tree_list);
            },
            ast::Use(use_item) => {
                builder.replace_ast(use_item, ast::make::use_item(make_use_tree(path, new_use_tree_list)));
            },
            _ => {},
        }
    }

    fn make_use_tree(path: ast::Path, use_tree_list: ast::UseTreeList) -> ast::UseTree {
        ast::make::use_tree(path, Some(use_tree_list), None, false)
    }
}

#[cfg(test)]
mod tests {
    use crate::tests::{check_assist, check_assist_not_applicable};

    use super::*;

    #[test]
    fn expanding_glob_import() {
        check_assist(
            expand_glob_import,
            r"
mod foo {
    pub struct Bar;
    pub struct Baz;
    pub struct Qux;

    pub fn f() {}
}

use foo::*<|>;

fn qux(bar: Bar, baz: Baz) {
    f();
}
",
            r"
mod foo {
    pub struct Bar;
    pub struct Baz;
    pub struct Qux;

    pub fn f() {}
}

use foo::{Baz, Bar, f};

fn qux(bar: Bar, baz: Baz) {
    f();
}
",
        )
    }

    #[test]
    fn expanding_glob_import_with_existing_explicit_names() {
        check_assist(
            expand_glob_import,
            r"
mod foo {
    pub struct Bar;
    pub struct Baz;
    pub struct Qux;

    pub fn f() {}
}

use foo::{*<|>, f};

fn qux(bar: Bar, baz: Baz) {
    f();
}
",
            r"
mod foo {
    pub struct Bar;
    pub struct Baz;
    pub struct Qux;

    pub fn f() {}
}

use foo::{Baz, Bar, f};

fn qux(bar: Bar, baz: Baz) {
    f();
}
",
        )
    }

    #[test]
    fn expanding_nested_glob_import() {
        check_assist(
            expand_glob_import,
            r"
mod foo {
    mod bar {
        pub struct Bar;
        pub struct Baz;
        pub struct Qux;

        pub fn f() {}
    }

    mod baz {
        pub fn g() {}
    }
}

use foo::{bar::{*<|>, f}, baz::*};

fn qux(bar: Bar, baz: Baz) {
    f();
    g();
}
",
            r"
mod foo {
    mod bar {
        pub struct Bar;
        pub struct Baz;
        pub struct Qux;

        pub fn f() {}
    }

    mod baz {
        pub fn g() {}
    }
}

use foo::{bar::{Baz, Bar, f}, baz::*};

fn qux(bar: Bar, baz: Baz) {
    f();
    g();
}
",
        )
    }

    #[test]
    fn expanding_glob_import_with_macro_defs() {
        check_assist(
            expand_glob_import,
            r"
//- /lib.rs crate:foo
#[macro_export]
macro_rules! bar {
    () => ()
}

pub fn baz() {}

//- /main.rs crate:main deps:foo
use foo::*<|>;

fn main() {
    bar!();
    baz();
}
",
            r"
use foo::{bar, baz};

fn main() {
    bar!();
    baz();
}
",
        )
    }

    #[test]
    fn expanding_is_not_applicable_if_cursor_is_not_in_star_token() {
        check_assist_not_applicable(
            expand_glob_import,
            r"
    mod foo {
        pub struct Bar;
        pub struct Baz;
        pub struct Qux;
    }

    use foo::Bar<|>;

    fn qux(bar: Bar, baz: Baz) {}
    ",
        )
    }
}
<commit_msg>Look for trait methods in expand glob import assist<commit_after>use hir::{AssocItem, MacroDef, ModuleDef, Name, PathResolution, ScopeDef, SemanticsScope};
use ra_ide_db::{
    defs::{classify_name_ref, Definition, NameRefClass},
    RootDatabase,
};
use ra_syntax::{algo, ast, match_ast, AstNode, SyntaxNode, SyntaxToken, T};

use crate::{
    assist_context::{AssistBuilder, AssistContext, Assists},
    AssistId, AssistKind,
};

use either::Either;

// Assist: expand_glob_import
//
// Expands glob imports.
//
// ```
// mod foo {
//     pub struct Bar;
//     pub struct Baz;
// }
//
// use foo::*<|>;
//
// fn qux(bar: Bar, baz: Baz) {}
// ```
// ->
// ```
// mod foo {
//     pub struct Bar;
//     pub struct Baz;
// }
//
// use foo::{Baz, Bar};
//
// fn qux(bar: Bar, baz: Baz) {}
// ```
pub(crate) fn expand_glob_import(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
    let star = ctx.find_token_at_offset(T![*])?;
    let mod_path = find_mod_path(&star)?;

    let source_file = ctx.source_file();
    let scope = ctx.sema.scope_at_offset(source_file.syntax(), ctx.offset());

    let defs_in_mod = find_defs_in_mod(ctx, scope, &mod_path)?;
    let name_refs_in_source_file =
        source_file.syntax().descendants().filter_map(ast::NameRef::cast).collect();
    let used_names = find_used_names(ctx, defs_in_mod, name_refs_in_source_file);

    let parent = star.parent().parent()?;
    acc.add(
        AssistId("expand_glob_import", AssistKind::RefactorRewrite),
        "Expand glob import",
        parent.text_range(),
        |builder| {
            replace_ast(builder, &parent, mod_path, used_names);
        },
    )
}

fn find_mod_path(star: &SyntaxToken) -> Option<ast::Path> {
    star.ancestors().find_map(|n| ast::UseTree::cast(n).and_then(|u| u.path()))
}

#[derive(PartialEq)]
enum Def {
    ModuleDef(ModuleDef),
    MacroDef(MacroDef),
}

impl Def {
    fn name(&self, db: &RootDatabase) -> Option<Name> {
        match self {
            Def::ModuleDef(def) => def.name(db),
            Def::MacroDef(def) => def.name(db),
        }
    }
}

fn find_defs_in_mod(
    ctx: &AssistContext,
    from: SemanticsScope<'_>,
    path: &ast::Path,
) -> Option<Vec<Def>> {
    let hir_path = ctx.sema.lower_path(&path)?;
    let module = if let Some(PathResolution::Def(ModuleDef::Module(module))) =
        from.resolve_hir_path_qualifier(&hir_path)
    {
        module
    } else {
        return None;
    };

    let module_scope = module.scope(ctx.db(), from.module());

    let mut defs = vec![];
    for (_, def) in module_scope {
        match def {
            ScopeDef::ModuleDef(def) => defs.push(Def::ModuleDef(def)),
            ScopeDef::MacroDef(def) => defs.push(Def::MacroDef(def)),
            _ => continue,
        }
    }

    Some(defs)
}

fn find_used_names(
    ctx: &AssistContext,
    defs_in_mod: Vec<Def>,
    name_refs_in_source_file: Vec<ast::NameRef>,
) -> Vec<Name> {
    let defs_in_source_file = name_refs_in_source_file
        .iter()
        .filter_map(|r| classify_name_ref(&ctx.sema, r))
        .filter_map(|rc| match rc {
            NameRefClass::Definition(Definition::ModuleDef(def)) => Some(Def::ModuleDef(def)),
            NameRefClass::Definition(Definition::Macro(def)) => Some(Def::MacroDef(def)),
            _ => None,
        })
        .collect::<Vec<Def>>();

    defs_in_mod
        .iter()
        .filter(|def| {
            if let Def::ModuleDef(ModuleDef::Trait(tr)) = def {
                for item in tr.items(ctx.db()) {
                    if let AssocItem::Function(f) = item {
                        if defs_in_source_file.contains(&Def::ModuleDef(ModuleDef::Function(f))) {
                            return true;
                        }
                    }
                }
            }

            defs_in_source_file.contains(def)
        })
        .filter_map(|d| d.name(ctx.db()))
        .collect()
}

fn replace_ast(
    builder: &mut AssistBuilder,
    node: &SyntaxNode,
    path: ast::Path,
    used_names: Vec<Name>,
) {
    let replacement: Either<ast::UseTree, ast::UseTreeList> = if used_names.len() == 1 {
        Either::Left(ast::make::use_tree(
            ast::make::path_from_text(&format!("{}::{}", path, used_names.first().unwrap())),
            None,
            None,
            false,
        ))
    } else {
        Either::Right(ast::make::use_tree_list(used_names.iter().map(|n| {
            ast::make::use_tree(ast::make::path_from_text(&n.to_string()), None, None, false)
        })))
    };

    let mut replace_node = |replacement: Either<ast::UseTree, ast::UseTreeList>| {
        algo::diff(node, &replacement.either(|u| u.syntax().clone(), |ut| ut.syntax().clone()))
            .into_text_edit(builder.text_edit_builder());
    };

    match_ast! {
        match node {
            ast::UseTree(use_tree) => {
                replace_node(replacement);
            },
            ast::UseTreeList(use_tree_list) => {
                replace_node(replacement);
            },
            ast::Use(use_item) => {
                builder.replace_ast(use_item, ast::make::use_item(replacement.left_or_else(|ut| ast::make::use_tree(path, Some(ut), None, false))));
            },
            _ => {},
        }
    }
}

#[cfg(test)]
mod tests {
    use crate::tests::{check_assist, check_assist_not_applicable};

    use super::*;

    #[test]
    fn expanding_glob_import() {
        check_assist(
            expand_glob_import,
            r"
mod foo {
    pub struct Bar;
    pub struct Baz;
    pub struct Qux;

    pub fn f() {}
}

use foo::*<|>;

fn qux(bar: Bar, baz: Baz) {
    f();
}
",
            r"
mod foo {
    pub struct Bar;
    pub struct Baz;
    pub struct Qux;

    pub fn f() {}
}

use foo::{Baz, Bar, f};

fn qux(bar: Bar, baz: Baz) {
    f();
}
",
        )
    }

    #[test]
    fn expanding_glob_import_with_existing_explicit_names() {
        check_assist(
            expand_glob_import,
            r"
mod foo {
    pub struct Bar;
    pub struct Baz;
    pub struct Qux;

    pub fn f() {}
}

use foo::{*<|>, f};

fn qux(bar: Bar, baz: Baz) {
    f();
}
",
            r"
mod foo {
    pub struct Bar;
    pub struct Baz;
    pub struct Qux;

    pub fn f() {}
}

use foo::{Baz, Bar, f};

fn qux(bar: Bar, baz: Baz) {
    f();
}
",
        )
    }

    #[test]
    fn expanding_nested_glob_import() {
        check_assist(
            expand_glob_import,
            r"
mod foo {
    mod bar {
        pub struct Bar;
        pub struct Baz;
        pub struct Qux;

        pub fn f() {}
    }

    mod baz {
        pub fn g() {}
    }
}

use foo::{bar::{*<|>, f}, baz::*};

fn qux(bar: Bar, baz: Baz) {
    f();
    g();
}
",
            r"
mod foo {
    mod bar {
        pub struct Bar;
        pub struct Baz;
        pub struct Qux;

        pub fn f() {}
    }

    mod baz {
        pub fn g() {}
    }
}

use foo::{bar::{Baz, Bar, f}, baz::*};

fn qux(bar: Bar, baz: Baz) {
    f();
    g();
}
",
        )
    }

    #[test]
    fn expanding_glob_import_with_macro_defs() {
        check_assist(
            expand_glob_import,
            r"
//- /lib.rs crate:foo
#[macro_export]
macro_rules! bar {
    () => ()
}

pub fn baz() {}

//- /main.rs crate:main deps:foo
use foo::*<|>;

fn main() {
    bar!();
    baz();
}
",
            r"
use foo::{bar, baz};

fn main() {
    bar!();
    baz();
}
",
        )
    }

    #[test]
    fn expanding_glob_import_with_trait_method_uses() {
        check_assist(
            expand_glob_import,
            r"
//- /lib.rs crate:foo
pub trait Tr {
    fn method(&self) {}
}
impl Tr for () {}

//- /main.rs crate:main deps:foo
use foo::*<|>;

fn main() {
    ().method();
}
",
            r"
use foo::Tr;

fn main() {
    ().method();
}
",
        )
    }

    #[test]
    fn expanding_is_not_applicable_if_cursor_is_not_in_star_token() {
        check_assist_not_applicable(
            expand_glob_import,
            r"
    mod foo {
        pub struct Bar;
        pub struct Baz;
        pub struct Qux;
    }

    use foo::Bar<|>;

    fn qux(bar: Bar, baz: Baz) {}
    ",
        )
    }
}
<|endoftext|>


<commit_before>extern crate syntex_syntax;

use std::fs::File;
use std::io;
use std::path::Path;

use syntex_syntax::ast;
use syntex_syntax::attr;
use syntex_syntax::codemap::{DUMMY_SP, respan};
use syntex_syntax::config;
use syntex_syntax::ext::base::{
    IdentMacroExpander,
    MultiItemDecorator,
    MultiItemModifier,
    NamedSyntaxExtension,
    SyntaxExtension,
    TTMacroExpander,
};
use syntex_syntax::ext::expand;
use syntex_syntax::feature_gate;
use syntex_syntax::parse::{self, token};
use syntex_syntax::print::{pp, pprust};
use syntex_syntax::ptr::P;

pub type Pass = fn(ast::Crate) -> ast::Crate;

pub struct Registry {
    macros: Vec<ast::MacroDef>,
    syntax_exts: Vec<NamedSyntaxExtension>,
    pre_expansion_passes: Vec<Box<Pass>>,
    post_expansion_passes: Vec<Box<Pass>>,
    cfg: Vec<P<ast::MetaItem>>,
    attrs: Vec<ast::Attribute>,
}

impl Registry {
    pub fn new() -> Registry {
        Registry {
            macros: Vec::new(),
            syntax_exts: Vec::new(),
            pre_expansion_passes: Vec::new(),
            post_expansion_passes: Vec::new(),
            cfg: Vec::new(),
            attrs: Vec::new(),
        }
    }

    pub fn add_cfg(&mut self, cfg: &str) {
        let meta_item = parse::parse_meta_from_source_str(
            "cfgspec".to_string(),
            cfg.to_string(),
            Vec::new(),
            &parse::new_parse_sess());

        self.cfg.push(meta_item);
    }

    pub fn add_attr(&mut self, attr: &str) {
        let meta_item = parse::parse_meta_from_source_str(
            "attrspec".to_string(),
            attr.to_string(),
            Vec::new(),
            &parse::new_parse_sess());

        self.attrs.push(respan(DUMMY_SP, ast::Attribute_ {
            id: attr::mk_attr_id(),
            style: ast::AttrOuter,
            value: meta_item,
            is_sugared_doc: false,
        }));
    }

    pub fn register_macro<F>(&mut self, name: &str, extension: F)
        where F: TTMacroExpander + 'static
    {
        let name = token::intern(name);
        let syntax_extension = SyntaxExtension::NormalTT(
            Box::new(extension),
            None,
            false
        );
        self.syntax_exts.push((name, syntax_extension));
    }

    pub fn register_ident<F>(&mut self, name: &str, extension: F)
        where F: IdentMacroExpander + 'static
    {
        let name = token::intern(name);
        let syntax_extension = SyntaxExtension::IdentTT(
            Box::new(extension),
            None,
            false
        );
        self.syntax_exts.push((name, syntax_extension));
    }

    pub fn register_decorator<F>(&mut self, name: &str, extension: F)
        where F: MultiItemDecorator + 'static
    {
        let name = token::intern(name);
        let syntax_extension = SyntaxExtension::MultiDecorator(Box::new(extension));
        self.syntax_exts.push((name, syntax_extension));
    }

    pub fn register_modifier<F>(&mut self, name: &str, extension: F)
        where F: MultiItemModifier + 'static
    {
        let name = token::intern(name);
        let syntax_extension = SyntaxExtension::MultiModifier(Box::new(extension));
        self.syntax_exts.push((name, syntax_extension));
    }

    pub fn add_pre_expansion_pass(&mut self, pass: Pass) {
        self.pre_expansion_passes.push(Box::new(pass))
    }

    pub fn add_post_expansion_pass(&mut self, pass: Pass) {
        self.post_expansion_passes.push(Box::new(pass))
    }

    pub fn expand(self, crate_name: &str, src: &Path, dst: &Path) -> io::Result<()> {
        let sess = parse::new_parse_sess();

        let mut krate = parse::parse_crate_from_file(
            src,
            self.cfg,
            &sess);

        krate.attrs.extend(self.attrs);

        let krate = config::strip_unconfigured_items(
            &sess.span_diagnostic,
            krate);

        let features = feature_gate::check_crate_macros(
            &sess.span_diagnostic.cm,
            &sess.span_diagnostic,
            &krate);

        let krate = self.pre_expansion_passes.iter()
            .fold(krate, |krate, f| (f)(krate));

        let mut ecfg = expand::ExpansionConfig::default(crate_name.to_string());
        ecfg.features = Some(&features);

        let krate = expand::expand_crate(
            &sess,
            ecfg,
            self.macros,
            self.syntax_exts,
            krate);

        let krate = self.post_expansion_passes.iter()
            .fold(krate, |krate, f| (f)(krate));

        let dst = try!(File::create(dst));

        let mut printer = pprust::rust_printer(Box::new(dst));
        try!(printer.print_mod(&krate.module, &krate.attrs[..]));
        try!(printer.print_remaining_comments());
        pp::eof(&mut printer.s)
    }
}
<commit_msg>Rename regster_* to add_*<commit_after>extern crate syntex_syntax;

use std::fs::File;
use std::io;
use std::path::Path;

use syntex_syntax::ast;
use syntex_syntax::attr;
use syntex_syntax::codemap::{DUMMY_SP, respan};
use syntex_syntax::config;
use syntex_syntax::ext::base::{
    IdentMacroExpander,
    MultiItemDecorator,
    MultiItemModifier,
    NamedSyntaxExtension,
    SyntaxExtension,
    TTMacroExpander,
};
use syntex_syntax::ext::expand;
use syntex_syntax::feature_gate;
use syntex_syntax::parse::{self, token};
use syntex_syntax::print::{pp, pprust};
use syntex_syntax::ptr::P;

pub type Pass = fn(ast::Crate) -> ast::Crate;

pub struct Registry {
    macros: Vec<ast::MacroDef>,
    syntax_exts: Vec<NamedSyntaxExtension>,
    pre_expansion_passes: Vec<Box<Pass>>,
    post_expansion_passes: Vec<Box<Pass>>,
    cfg: Vec<P<ast::MetaItem>>,
    attrs: Vec<ast::Attribute>,
}

impl Registry {
    pub fn new() -> Registry {
        Registry {
            macros: Vec::new(),
            syntax_exts: Vec::new(),
            pre_expansion_passes: Vec::new(),
            post_expansion_passes: Vec::new(),
            cfg: Vec::new(),
            attrs: Vec::new(),
        }
    }

    pub fn add_cfg(&mut self, cfg: &str) {
        let meta_item = parse::parse_meta_from_source_str(
            "cfgspec".to_string(),
            cfg.to_string(),
            Vec::new(),
            &parse::new_parse_sess());

        self.cfg.push(meta_item);
    }

    pub fn add_attr(&mut self, attr: &str) {
        let meta_item = parse::parse_meta_from_source_str(
            "attrspec".to_string(),
            attr.to_string(),
            Vec::new(),
            &parse::new_parse_sess());

        self.attrs.push(respan(DUMMY_SP, ast::Attribute_ {
            id: attr::mk_attr_id(),
            style: ast::AttrOuter,
            value: meta_item,
            is_sugared_doc: false,
        }));
    }

    pub fn add_macro<F>(&mut self, name: &str, extension: F)
        where F: TTMacroExpander + 'static
    {
        let name = token::intern(name);
        let syntax_extension = SyntaxExtension::NormalTT(
            Box::new(extension),
            None,
            false
        );
        self.syntax_exts.push((name, syntax_extension));
    }

    pub fn add_ident_macro<F>(&mut self, name: &str, extension: F)
        where F: IdentMacroExpander + 'static
    {
        let name = token::intern(name);
        let syntax_extension = SyntaxExtension::IdentTT(
            Box::new(extension),
            None,
            false
        );
        self.syntax_exts.push((name, syntax_extension));
    }

    pub fn add_decorator<F>(&mut self, name: &str, extension: F)
        where F: MultiItemDecorator + 'static
    {
        let name = token::intern(name);
        let syntax_extension = SyntaxExtension::MultiDecorator(Box::new(extension));
        self.syntax_exts.push((name, syntax_extension));
    }

    pub fn add_modifier<F>(&mut self, name: &str, extension: F)
        where F: MultiItemModifier + 'static
    {
        let name = token::intern(name);
        let syntax_extension = SyntaxExtension::MultiModifier(Box::new(extension));
        self.syntax_exts.push((name, syntax_extension));
    }

    pub fn add_pre_expansion_pass(&mut self, pass: Pass) {
        self.pre_expansion_passes.push(Box::new(pass))
    }

    pub fn add_post_expansion_pass(&mut self, pass: Pass) {
        self.post_expansion_passes.push(Box::new(pass))
    }

    pub fn expand(self, crate_name: &str, src: &Path, dst: &Path) -> io::Result<()> {
        let sess = parse::new_parse_sess();

        let mut krate = parse::parse_crate_from_file(
            src,
            self.cfg,
            &sess);

        krate.attrs.extend(self.attrs);

        let krate = config::strip_unconfigured_items(
            &sess.span_diagnostic,
            krate);

        let features = feature_gate::check_crate_macros(
            &sess.span_diagnostic.cm,
            &sess.span_diagnostic,
            &krate);

        let krate = self.pre_expansion_passes.iter()
            .fold(krate, |krate, f| (f)(krate));

        let mut ecfg = expand::ExpansionConfig::default(crate_name.to_string());
        ecfg.features = Some(&features);

        let krate = expand::expand_crate(
            &sess,
            ecfg,
            self.macros,
            self.syntax_exts,
            krate);

        let krate = self.post_expansion_passes.iter()
            .fold(krate, |krate, f| (f)(krate));

        let dst = try!(File::create(dst));

        let mut printer = pprust::rust_printer(Box::new(dst));
        try!(printer.print_mod(&krate.module, &krate.attrs[..]));
        try!(printer.print_remaining_comments());
        pp::eof(&mut printer.s)
    }
}
<|endoftext|>


<commit_before>use crate::custom::credential::NewAwsCredsForStsCreds;
use crate::{AssumeRoleWithWebIdentityRequest, Sts, StsClient};
use rusoto_core::credential::{
    AwsCredentials, CredentialsError, ProvideAwsCredentials, Secret, Variable,
};
use rusoto_core::request::HttpClient;
use rusoto_core::{Client, Region};

use async_trait::async_trait;

const AWS_WEB_IDENTITY_TOKEN_FILE: &str = "AWS_WEB_IDENTITY_TOKEN_FILE";

const AWS_ROLE_ARN: &str = "AWS_ROLE_ARN";

const AWS_ROLE_SESSION_NAME: &str = "AWS_ROLE_SESSION_NAME";

/// WebIdentityProvider using OpenID Connect bearer token to retrieve AWS IAM credentials.
///
/// See https://docs.aws.amazon.com/STS/latest/APIReference/API_AssumeRoleWithWebIdentity.html for
/// more details.
#[derive(Debug, Clone)]
pub struct WebIdentityProvider {
    /// The OAuth 2.0 access token or OpenID Connect ID token that is provided by the identity provider.
    /// Your application must get this token by authenticating the user who is using your application
    /// with a web identity provider before the application makes an AssumeRoleWithWebIdentity call.
    pub web_identity_token: Variable<Secret, CredentialsError>,
    /// The Amazon Resource Name (ARN) of the role that the caller is assuming.
    pub role_arn: Variable<String, CredentialsError>,
    /// An identifier for the assumed role session. Typically, you pass the name or identifier that is
    /// associated with the user who is using your application. That way, the temporary security credentials
    /// that your application will use are associated with that user. This session name is included as part
    /// of the ARN and assumed role ID in the AssumedRoleUser response element.
    pub role_session_name: Variable<String, CredentialsError>,
}

impl WebIdentityProvider {
    /// Create new WebIdentityProvider by explicitly passing its configuration.
    pub fn new<A, B, C>(web_identity_token: A, role_arn: B, role_session_name: Option<C>) -> Self
    where
        A: Into<Variable<Secret, CredentialsError>>,
        B: Into<Variable<String, CredentialsError>>,
        C: Into<Variable<String, CredentialsError>>,
    {
        Self {
            web_identity_token: web_identity_token.into(),
            role_arn: role_arn.into(),
            role_session_name: role_session_name
                .map(|v| v.into())
                .unwrap_or_else(|| Variable::with_value(Self::create_session_name())),
        }
    }

    /// Creat a WebIdentityProvider from the following environment variables:
    ///
    /// - `AWS_WEB_IDENTITY_TOKEN_FILE` path to the web identity token file.
    /// - `AWS_ROLE_ARN` ARN of the role to assume.
    /// - `AWS_ROLE_SESSION_NAME` (optional) name applied to the assume-role session.
    ///
    /// See https://docs.aws.amazon.com/eks/latest/userguide/iam-roles-for-service-accounts-technical-overview.html
    /// for more information about how IAM Roles for Kubernetes Service Accounts works.
    pub fn from_k8s_env() -> Self {
        Self::_from_k8s_env(
            Variable::from_env_var(AWS_WEB_IDENTITY_TOKEN_FILE),
            Variable::from_env_var(AWS_ROLE_ARN),
            Some(Variable::from_env_var(AWS_ROLE_SESSION_NAME)),
        )
    }

    /// Used by unit testing
    pub(crate) fn _from_k8s_env(
        token_file: Variable<String, CredentialsError>,
        role: Variable<String, CredentialsError>,
        session_name: Option<Variable<String, CredentialsError>>,
    ) -> Self {
        Self::new(
            Variable::dynamic(move || Variable::from_text_file(token_file.resolve()?).resolve()),
            role,
            session_name,
        )
    }

    #[cfg(test)]
    pub(crate) fn load_token(&self) -> Result<Secret, CredentialsError> {
        self.web_identity_token.resolve()
    }

    fn create_session_name() -> String {
        // TODO can we do better here?
        // - Pod service account, Pod name and Pod namespace
        // - EC2 Instance ID if available
        // - IP address if available
        // - ...
        // Having some information in the session name that identifies the client would enable
        // better correlation analysis in CloudTrail.
        "WebIdentitySession".to_string()
    }
}

#[async_trait]
impl ProvideAwsCredentials for WebIdentityProvider {
    async fn credentials(&self) -> Result<AwsCredentials, CredentialsError> {
        let http_client = match HttpClient::new() {
            Ok(c) => c,
            Err(e) => return Err(CredentialsError::new(e)),
        };
        let client = Client::new_not_signing(http_client);
        let sts = StsClient::new_with_client(client, Region::default());
        let mut req = AssumeRoleWithWebIdentityRequest::default();

        req.role_arn = self.role_arn.resolve()?;
        req.web_identity_token = self.web_identity_token.resolve()?.as_ref().to_string();
        req.role_session_name = self.role_session_name.resolve()?;

        let assume_role = sts.assume_role_with_web_identity(req).await;
        match assume_role {
            Err(e) => Err(CredentialsError::new(e)),
            Ok(role) => match role.credentials {
                None => Err(CredentialsError::new(format!(
                    "No credentials found in AssumeRoleWithWebIdentityResponse: {:?}",
                    role
                ))),
                Some(c) => AwsCredentials::new_for_credentials(c),
            },
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use std::io::Write;
    use tempfile::NamedTempFile;

    #[test]
    fn api_ergonomy() {
        WebIdentityProvider::new(Secret::from("".to_string()), "", Some("".to_string()));
    }

    #[test]
    fn from_k8s_env() -> Result<(), CredentialsError> {
        const TOKEN_VALUE: &str = "secret";
        const ROLE_ARN: &str = "role";
        const SESSION_NAME: &str = "session";
        let mut file = NamedTempFile::new()?;
        // We use writeln to add an extra newline at the end of the token, which should be
        // removed by Variable::from_text_file.
        writeln!(file, "{}", TOKEN_VALUE)?;
        let p = WebIdentityProvider::_from_k8s_env(
            Variable::with_value(file.path().to_string_lossy().to_string()),
            Variable::with_value(ROLE_ARN.to_string()),
            Some(Variable::with_value(SESSION_NAME.to_string())),
        );
        let token = p.load_token()?;
        assert_eq!(token.as_ref(), TOKEN_VALUE);
        Ok(())
    }
}
<commit_msg>Allowing to pass session policy, policy arns and duration to WebIdentityProvider<commit_after>use crate::custom::credential::NewAwsCredsForStsCreds;
use crate::{AssumeRoleWithWebIdentityRequest, Sts, StsClient, PolicyDescriptorType};
use rusoto_core::credential::{
    AwsCredentials, CredentialsError, ProvideAwsCredentials, Secret, Variable,
};
use rusoto_core::request::HttpClient;
use rusoto_core::{Client, Region};

use async_trait::async_trait;

const AWS_WEB_IDENTITY_TOKEN_FILE: &str = "AWS_WEB_IDENTITY_TOKEN_FILE";

const AWS_ROLE_ARN: &str = "AWS_ROLE_ARN";

const AWS_ROLE_SESSION_NAME: &str = "AWS_ROLE_SESSION_NAME";

/// WebIdentityProvider using OpenID Connect bearer token to retrieve AWS IAM credentials.
///
/// See https://docs.aws.amazon.com/STS/latest/APIReference/API_AssumeRoleWithWebIdentity.html for
/// more details.
#[derive(Debug, Clone)]
pub struct WebIdentityProvider {
    /// The OAuth 2.0 access token or OpenID Connect ID token that is provided by the identity provider.
    /// Your application must get this token by authenticating the user who is using your application
    /// with a web identity provider before the application makes an AssumeRoleWithWebIdentity call.
    pub web_identity_token: Variable<Secret, CredentialsError>,
    /// The Amazon Resource Name (ARN) of the role that the caller is assuming.
    pub role_arn: Variable<String, CredentialsError>,
    /// An identifier for the assumed role session. Typically, you pass the name or identifier that is
    /// associated with the user who is using your application. That way, the temporary security credentials
    /// that your application will use are associated with that user. This session name is included as part
    /// of the ARN and assumed role ID in the AssumedRoleUser response element.
    pub role_session_name: Variable<String, CredentialsError>,

    /// The duration, in seconds, of the role session.
    /// The value can range from 900 seconds (15 minutes) up to the maximum session duration setting for the role.
    pub duration_seconds: Option<i64>,
    /// An IAM policy in JSON format that you want to use as an inline session policy.
    pub policy: Option<String>,
    /// The Amazon Resource Names (ARNs) of the IAM managed policies that you want to use as managed session policies.
    pub policy_arns: Option<Vec<PolicyDescriptorType>>,
}

impl WebIdentityProvider {
    /// Create new WebIdentityProvider by explicitly passing its configuration.
    pub fn new<A, B, C>(web_identity_token: A, role_arn: B, role_session_name: Option<C>) -> Self
    where
        A: Into<Variable<Secret, CredentialsError>>,
        B: Into<Variable<String, CredentialsError>>,
        C: Into<Variable<String, CredentialsError>>,
    {
        Self {
            web_identity_token: web_identity_token.into(),
            role_arn: role_arn.into(),
            role_session_name: role_session_name
                .map(|v| v.into())
                .unwrap_or_else(|| Variable::with_value(Self::create_session_name())),
            duration_seconds: None,
            policy: None,
            policy_arns: None
        }
    }

    /// Creat a WebIdentityProvider from the following environment variables:
    ///
    /// - `AWS_WEB_IDENTITY_TOKEN_FILE` path to the web identity token file.
    /// - `AWS_ROLE_ARN` ARN of the role to assume.
    /// - `AWS_ROLE_SESSION_NAME` (optional) name applied to the assume-role session.
    ///
    /// See https://docs.aws.amazon.com/eks/latest/userguide/iam-roles-for-service-accounts-technical-overview.html
    /// for more information about how IAM Roles for Kubernetes Service Accounts works.
    pub fn from_k8s_env() -> Self {
        Self::_from_k8s_env(
            Variable::from_env_var(AWS_WEB_IDENTITY_TOKEN_FILE),
            Variable::from_env_var(AWS_ROLE_ARN),
            Some(Variable::from_env_var(AWS_ROLE_SESSION_NAME)),
        )
    }

    /// Used by unit testing
    pub(crate) fn _from_k8s_env(
        token_file: Variable<String, CredentialsError>,
        role: Variable<String, CredentialsError>,
        session_name: Option<Variable<String, CredentialsError>>,
    ) -> Self {
        Self::new(
            Variable::dynamic(move || Variable::from_text_file(token_file.resolve()?).resolve()),
            role,
            session_name,
        )
    }

    #[cfg(test)]
    pub(crate) fn load_token(&self) -> Result<Secret, CredentialsError> {
        self.web_identity_token.resolve()
    }

    fn create_session_name() -> String {
        // TODO can we do better here?
        // - Pod service account, Pod name and Pod namespace
        // - EC2 Instance ID if available
        // - IP address if available
        // - ...
        // Having some information in the session name that identifies the client would enable
        // better correlation analysis in CloudTrail.
        "WebIdentitySession".to_string()
    }
}

#[async_trait]
impl ProvideAwsCredentials for WebIdentityProvider {
    async fn credentials(&self) -> Result<AwsCredentials, CredentialsError> {
        let http_client = match HttpClient::new() {
            Ok(c) => c,
            Err(e) => return Err(CredentialsError::new(e)),
        };
        let client = Client::new_not_signing(http_client);
        let sts = StsClient::new_with_client(client, Region::default());
        let mut req = AssumeRoleWithWebIdentityRequest::default();

        req.role_arn = self.role_arn.resolve()?;
        req.web_identity_token = self.web_identity_token.resolve()?.as_ref().to_string();
        req.role_session_name = self.role_session_name.resolve()?;
        req.policy = self.policy.to_owned();
        req.duration_seconds = self.duration_seconds.to_owned();
        req.policy_arns = self.policy_arns.to_owned();

        let assume_role = sts.assume_role_with_web_identity(req).await;
        match assume_role {
            Err(e) => Err(CredentialsError::new(e)),
            Ok(role) => match role.credentials {
                None => Err(CredentialsError::new(format!(
                    "No credentials found in AssumeRoleWithWebIdentityResponse: {:?}",
                    role
                ))),
                Some(c) => AwsCredentials::new_for_credentials(c),
            },
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use std::io::Write;
    use tempfile::NamedTempFile;

    #[test]
    fn api_ergonomy() {
        WebIdentityProvider::new(Secret::from("".to_string()), "", Some("".to_string()));
    }

    #[test]
    fn from_k8s_env() -> Result<(), CredentialsError> {
        const TOKEN_VALUE: &str = "secret";
        const ROLE_ARN: &str = "role";
        const SESSION_NAME: &str = "session";
        let mut file = NamedTempFile::new()?;
        // We use writeln to add an extra newline at the end of the token, which should be
        // removed by Variable::from_text_file.
        writeln!(file, "{}", TOKEN_VALUE)?;
        let p = WebIdentityProvider::_from_k8s_env(
            Variable::with_value(file.path().to_string_lossy().to_string()),
            Variable::with_value(ROLE_ARN.to_string()),
            Some(Variable::with_value(SESSION_NAME.to_string())),
        );
        let token = p.load_token()?;
        assert_eq!(token.as_ref(), TOKEN_VALUE);
        Ok(())
    }
}
<|endoftext|>


<commit_before>//! Untyped safe interface for NumPy ndarray

use ndarray::*;
use npyffi;
use pyo3::*;

use std::os::raw::c_void;
use std::ptr::null_mut;

use super::error::ArrayCastError;
use super::*;

/// Untyped safe interface for NumPy ndarray.
pub struct PyArray(PyObject);
pyobject_native_type!(PyArray, *npyffi::PyArray_Type_Ptr, npyffi::PyArray_Check);

impl IntoPyObject for PyArray {
    fn into_object(self, _py: Python) -> PyObject {
        self.0
    }
}

impl PyArray {
    /// Get raw pointer for PyArrayObject
    pub fn as_array_ptr(&self) -> *mut npyffi::PyArrayObject {
        self.as_ptr() as _
    }

    /// Construct one-dimension PyArray from Vec.
    ///
    /// # Example
    /// ```
    /// # extern crate pyo3; extern crate numpy; fn main() {
    /// use numpy::{PyArray, PyArrayModule};
    /// let gil = pyo3::Python::acquire_gil();
    /// let np = PyArrayModule::import(gil.python()).unwrap();
    /// let pyarray = PyArray::from_vec::<u32>(gil.python(), &np, vec![1, 2, 3, 4, 5]);
    /// assert_eq!(pyarray.as_slice::<u32>().unwrap(), &[1, 2, 3, 4, 5]);
    /// # }
    /// ```
    pub fn from_vec<T: TypeNum>(py: Python, np: &PyArrayModule, v: Vec<T>) -> PyArray {
        IntoPyArray::into_pyarray(v, py, np)
    }

    /// Construct two-dimension PyArray from `Vec<Vec<T>>`.
    ///
    /// This function checks all dimension of inner vec, and if there's any vec
    /// where its dimension differs from others, it returns `ArrayCastError`.
    ///
    /// # Example
    /// ```
    /// # extern crate pyo3; extern crate numpy; #[macro_use] extern crate ndarray; fn main() {
    /// use numpy::{PyArray, PyArrayModule};
    /// let gil = pyo3::Python::acquire_gil();
    /// let np = PyArrayModule::import(gil.python()).unwrap();
    /// let vec2 = vec![vec![1, 2, 3]; 2];
    /// let pyarray = PyArray::from_vec2::<u32>(gil.python(), &np, vec2).unwrap();
    /// assert_eq!(pyarray.as_array::<u32>().unwrap(), array![[1, 2, 3], [1, 2, 3]].into_dyn());
    /// assert!(PyArray::from_vec2::<u32>(gil.python(), &np, vec![vec![1], vec![2, 3]]).is_err());
    /// # }
    /// ```
    pub fn from_vec2<T: TypeNum>(
        py: Python,
        np: &PyArrayModule,
        v: Vec<Vec<T>>,
    ) -> Result<PyArray, ArrayCastError>
    {
        let mut flattend = Vec::new();
        let mut size = None;
        let len = v.len();
        if !v.into_iter().all(|v| {
            let len = v.len();
            match size {
                Some(x) if x != v.len() => return false,
                None => size = Some(len),
                _ => {}
            }
            flattend.extend(v);
            true
        }) {
            return Err(ArrayCastError::FromVec);
        }
        let dims = [len, size.unwrap_or(0)];
        unsafe {
            let data = convert::into_raw(flattend);
            Ok(PyArray::new_::<T>(py, np, &dims, null_mut(), data))
        }
    }

    /// Construct PyArray from ndarray::Array.
    ///
    /// # Example
    /// ```
    /// # extern crate pyo3; extern crate numpy; #[macro_use] extern crate ndarray; fn main() {
    /// use numpy::{PyArray, PyArrayModule};
    /// let gil = pyo3::Python::acquire_gil();
    /// let np = PyArrayModule::import(gil.python()).unwrap();
    /// let pyarray = PyArray::from_ndarray::<u32, _>(gil.python(), &np, array![[1, 2], [3, 4]]);
    /// assert_eq!(pyarray.as_array::<u32>().unwrap(), array![[1, 2], [3, 4]].into_dyn());
    /// # }
    /// ```
    pub fn from_ndarray<A, D>(py: Python, np: &PyArrayModule, arr: Array<A, D>) -> PyArray
    where
        A: TypeNum,
        D: Dimension,
    {
        IntoPyArray::into_pyarray(arr, py, np)
    }

    pub unsafe fn from_owned_ptr(py: Python, ptr: *mut pyo3::ffi::PyObject) -> Self {
        let obj = PyObject::from_owned_ptr(py, ptr);
        PyArray(obj)
    }

    pub unsafe fn from_borrowed_ptr(py: Python, ptr: *mut pyo3::ffi::PyObject) -> Self {
        let obj = PyObject::from_borrowed_ptr(py, ptr);
        PyArray(obj)
    }

    /// Returns the number of dimensions in the array.
    ///
    /// Same as [numpy.ndarray.ndim](https://docs.scipy.org/doc/numpy/reference/generated/numpy.ndarray.ndim.html)
    ///
    /// # Example
    /// ```
    /// # extern crate pyo3; extern crate numpy; fn main() {
    /// use numpy::{PyArray, PyArrayModule};
    /// let gil = pyo3::Python::acquire_gil();
    /// let np = PyArrayModule::import(gil.python()).unwrap();
    /// let arr = PyArray::new::<f64>(gil.python(), &np, &[4, 5, 6]);
    /// assert_eq!(arr.ndim(), 3);
    /// # }
    /// ```
    // C API: https://docs.scipy.org/doc/numpy/reference/c-api.array.html#c.PyArray_NDIM
    pub fn ndim(&self) -> usize {
        let ptr = self.as_array_ptr();
        unsafe { (*ptr).nd as usize }
    }

    /// Same as [shape](./struct.PyArray.html#method.shape)
    ///
    /// Reserved for backward compatibility.
    #[inline]
    pub fn dims(&self) -> &[usize] {
        self.shape()
    }

    pub fn len(&self) -> usize {
        self.shape().iter().fold(1, |a, b| a * b)
    }

    /// Returns a slice which contains dimmensions of the array.
    ///
    /// Same as [numpy.ndarray.shape](https://docs.scipy.org/doc/numpy/reference/generated/numpy.ndarray.shape.html)
    /// # Example
    /// ```
    /// # extern crate pyo3; extern crate numpy; fn main() {
    /// use numpy::{PyArray, PyArrayModule};
    /// let gil = pyo3::Python::acquire_gil();
    /// let np = PyArrayModule::import(gil.python()).unwrap();
    /// let arr = PyArray::new::<f64>(gil.python(), &np, &[4, 5, 6]);
    /// assert_eq!(arr.shape(), &[4, 5, 6]);
    /// # }
    /// ```
    // C API: https://docs.scipy.org/doc/numpy/reference/c-api.array.html#c.PyArray_DIMS
    pub fn shape(&self) -> &[usize] {
        let n = self.ndim();
        let ptr = self.as_array_ptr();
        unsafe {
            let p = (*ptr).dimensions as *mut usize;
            ::std::slice::from_raw_parts(p, n)
        }
    }

    /// Returns a slice which contains how many bytes you need to jump to the next row.
    ///
    /// Same as [numpy.ndarray.strides](https://docs.scipy.org/doc/numpy/reference/generated/numpy.ndarray.strides.html)
    /// # Example
    /// ```
    /// # extern crate pyo3; extern crate numpy; fn main() {
    /// use numpy::{PyArray, PyArrayModule};
    /// let gil = pyo3::Python::acquire_gil();
    /// let np = PyArrayModule::import(gil.python()).unwrap();
    /// let arr = PyArray::new::<f64>(gil.python(), &np, &[4, 5, 6]);
    /// assert_eq!(arr.strides(), &[240, 48, 8]);
    /// # }
    /// ```
    // C API: https://docs.scipy.org/doc/numpy/reference/c-api.array.html#c.PyArray_STRIDES
    pub fn strides(&self) -> &[isize] {
        let n = self.ndim();
        let ptr = self.as_array_ptr();
        unsafe {
            let p = (*ptr).strides;
            ::std::slice::from_raw_parts(p, n)
        }
    }

    unsafe fn data<T>(&self) -> *mut T {
        let ptr = self.as_array_ptr();
        (*ptr).data as *mut T
    }

    fn ndarray_shape<A>(&self) -> StrideShape<IxDyn> {
        // FIXME may be done more simply
        let shape: Shape<_> = Dim(self.shape()).into();
        let st: Vec<usize> = self
            .strides()
            .iter()
            .map(|&x| x as usize / ::std::mem::size_of::<A>())
            .collect();
        shape.strides(Dim(st))
    }

    pub fn typenum(&self) -> i32 {
        unsafe {
            let descr = (*self.as_array_ptr()).descr;
            (*descr).type_num
        }
    }

    fn type_check<A: types::TypeNum>(&self) -> Result<(), ArrayCastError> {
        let test = A::typenum();
        let truth = self.typenum();
        if A::typenum() == self.typenum() {
            Ok(())
        } else {
            Err(ArrayCastError::to_rust(test, truth))
        }
    }

    /// Get data as a ndarray::ArrayView
    pub fn as_array<A: types::TypeNum>(&self) -> Result<ArrayViewD<A>, ArrayCastError> {
        self.type_check::<A>()?;
        unsafe {
            Ok(ArrayView::from_shape_ptr(
                self.ndarray_shape::<A>(),
                self.data(),
            ))
        }
    }

    /// Get data as a ndarray::ArrayViewMut
    pub fn as_array_mut<A: types::TypeNum>(&self) -> Result<ArrayViewMutD<A>, ArrayCastError> {
        self.type_check::<A>()?;
        unsafe {
            Ok(ArrayViewMut::from_shape_ptr(
                self.ndarray_shape::<A>(),
                self.data(),
            ))
        }
    }

    /// Get data as a Rust immutable slice
    pub fn as_slice<A: types::TypeNum>(&self) -> Result<&[A], ArrayCastError> {
        self.type_check::<A>()?;
        unsafe { Ok(::std::slice::from_raw_parts(self.data(), self.len())) }
    }

    /// Get data as a Rust mutable slice
    pub fn as_slice_mut<A: types::TypeNum>(&self) -> Result<&mut [A], ArrayCastError> {
        self.type_check::<A>()?;
        unsafe { Ok(::std::slice::from_raw_parts_mut(self.data(), self.len())) }
    }

    pub unsafe fn new_<T: types::TypeNum>(
        py: Python,
        np: &PyArrayModule,
        dims: &[usize],
        strides: *mut npy_intp,
        data: *mut c_void,
    ) -> Self {
        let dims: Vec<_> = dims.iter().map(|d| *d as npy_intp).collect();
        let ptr = np.PyArray_New(
            np.get_type_object(npyffi::ArrayType::PyArray_Type),
            dims.len() as i32,
            dims.as_ptr() as *mut npy_intp,
            T::typenum(),
            strides,
            data,
            0,                      // itemsize
            0,                      // flag
            ::std::ptr::null_mut(), //obj
        );
        Self::from_owned_ptr(py, ptr)
    }

    /// a wrapper of [PyArray_SimpleNew](https://docs.scipy.org/doc/numpy/reference/c-api.array.html#c.PyArray_SimpleNew)
    pub fn new<T: TypeNum>(py: Python, np: &PyArrayModule, dims: &[usize]) -> Self {
        unsafe { Self::new_::<T>(py, np, dims, null_mut(), null_mut()) }
    }

    /// a wrapper of [PyArray_ZEROS](https://docs.scipy.org/doc/numpy/reference/c-api.array.html#c.PyArray_ZEROS)
    pub fn zeros<T: TypeNum>(
        py: Python,
        np: &PyArrayModule,
        dims: &[usize],
        order: NPY_ORDER,
    ) -> Self {
        let dims: Vec<npy_intp> = dims.iter().map(|d| *d as npy_intp).collect();
        unsafe {
            let descr = np.PyArray_DescrFromType(T::typenum());
            let ptr = np.PyArray_Zeros(
                dims.len() as i32,
                dims.as_ptr() as *mut npy_intp,
                descr,
                order as i32,
            );
            Self::from_owned_ptr(py, ptr)
        }
    }

    /// a wrapper of [PyArray_Arange](https://docs.scipy.org/doc/numpy/reference/c-api.array.html#c.PyArray_Arange)
    pub fn arange<T: TypeNum>(
        py: Python,
        np: &PyArrayModule,
        start: f64,
        stop: f64,
        step: f64,
    ) -> Self {
        unsafe {
            let ptr = np.PyArray_Arange(start, stop, step, T::typenum());
            Self::from_owned_ptr(py, ptr)
        }
    }
}
<commit_msg>Simplify code<commit_after>//! Untyped safe interface for NumPy ndarray

use ndarray::*;
use npyffi;
use pyo3::*;

use std::os::raw::c_void;
use std::ptr::null_mut;

use super::error::ArrayCastError;
use super::*;

/// Untyped safe interface for NumPy ndarray.
pub struct PyArray(PyObject);
pyobject_native_type!(PyArray, *npyffi::PyArray_Type_Ptr, npyffi::PyArray_Check);

impl IntoPyObject for PyArray {
    fn into_object(self, _py: Python) -> PyObject {
        self.0
    }
}

impl PyArray {
    /// Get raw pointer for PyArrayObject
    pub fn as_array_ptr(&self) -> *mut npyffi::PyArrayObject {
        self.as_ptr() as _
    }

    /// Construct one-dimension PyArray from Vec.
    ///
    /// # Example
    /// ```
    /// # extern crate pyo3; extern crate numpy; fn main() {
    /// use numpy::{PyArray, PyArrayModule};
    /// let gil = pyo3::Python::acquire_gil();
    /// let np = PyArrayModule::import(gil.python()).unwrap();
    /// let pyarray = PyArray::from_vec::<u32>(gil.python(), &np, vec![1, 2, 3, 4, 5]);
    /// assert_eq!(pyarray.as_slice::<u32>().unwrap(), &[1, 2, 3, 4, 5]);
    /// # }
    /// ```
    pub fn from_vec<T: TypeNum>(py: Python, np: &PyArrayModule, v: Vec<T>) -> PyArray {
        IntoPyArray::into_pyarray(v, py, np)
    }

    /// Construct two-dimension PyArray from `Vec<Vec<T>>`.
    ///
    /// This function checks all dimension of inner vec, and if there's any vec
    /// where its dimension differs from others, it returns `ArrayCastError`.
    ///
    /// # Example
    /// ```
    /// # extern crate pyo3; extern crate numpy; #[macro_use] extern crate ndarray; fn main() {
    /// use numpy::{PyArray, PyArrayModule};
    /// let gil = pyo3::Python::acquire_gil();
    /// let np = PyArrayModule::import(gil.python()).unwrap();
    /// let vec2 = vec![vec![1, 2, 3]; 2];
    /// let pyarray = PyArray::from_vec2::<u32>(gil.python(), &np, vec2).unwrap();
    /// assert_eq!(pyarray.as_array::<u32>().unwrap(), array![[1, 2, 3], [1, 2, 3]].into_dyn());
    /// assert!(PyArray::from_vec2::<u32>(gil.python(), &np, vec![vec![1], vec![2, 3]]).is_err());
    /// # }
    /// ```
    pub fn from_vec2<T: TypeNum>(
        py: Python,
        np: &PyArrayModule,
        v: Vec<Vec<T>>,
    ) -> Result<PyArray, ArrayCastError>
    {
        let last_len = v.last().map_or(0, |v| v.len());
        if v.iter().any(|v| v.len() != last_len) {
            return Err(ArrayCastError::FromVec);
        }
        let dims = [v.len(), last_len];
        let flattend: Vec<_> = v.into_iter().flatten().collect();
        unsafe {
            let data = convert::into_raw(flattend);
            Ok(PyArray::new_::<T>(py, np, &dims, null_mut(), data))
        }
    }

    /// Construct PyArray from ndarray::Array.
    ///
    /// # Example
    /// ```
    /// # extern crate pyo3; extern crate numpy; #[macro_use] extern crate ndarray; fn main() {
    /// use numpy::{PyArray, PyArrayModule};
    /// let gil = pyo3::Python::acquire_gil();
    /// let np = PyArrayModule::import(gil.python()).unwrap();
    /// let pyarray = PyArray::from_ndarray::<u32, _>(gil.python(), &np, array![[1, 2], [3, 4]]);
    /// assert_eq!(pyarray.as_array::<u32>().unwrap(), array![[1, 2], [3, 4]].into_dyn());
    /// # }
    /// ```
    pub fn from_ndarray<A, D>(py: Python, np: &PyArrayModule, arr: Array<A, D>) -> PyArray
    where
        A: TypeNum,
        D: Dimension,
    {
        IntoPyArray::into_pyarray(arr, py, np)
    }

    pub unsafe fn from_owned_ptr(py: Python, ptr: *mut pyo3::ffi::PyObject) -> Self {
        let obj = PyObject::from_owned_ptr(py, ptr);
        PyArray(obj)
    }

    pub unsafe fn from_borrowed_ptr(py: Python, ptr: *mut pyo3::ffi::PyObject) -> Self {
        let obj = PyObject::from_borrowed_ptr(py, ptr);
        PyArray(obj)
    }

    /// Returns the number of dimensions in the array.
    ///
    /// Same as [numpy.ndarray.ndim](https://docs.scipy.org/doc/numpy/reference/generated/numpy.ndarray.ndim.html)
    ///
    /// # Example
    /// ```
    /// # extern crate pyo3; extern crate numpy; fn main() {
    /// use numpy::{PyArray, PyArrayModule};
    /// let gil = pyo3::Python::acquire_gil();
    /// let np = PyArrayModule::import(gil.python()).unwrap();
    /// let arr = PyArray::new::<f64>(gil.python(), &np, &[4, 5, 6]);
    /// assert_eq!(arr.ndim(), 3);
    /// # }
    /// ```
    // C API: https://docs.scipy.org/doc/numpy/reference/c-api.array.html#c.PyArray_NDIM
    pub fn ndim(&self) -> usize {
        let ptr = self.as_array_ptr();
        unsafe { (*ptr).nd as usize }
    }

    /// Same as [shape](./struct.PyArray.html#method.shape)
    ///
    /// Reserved for backward compatibility.
    #[inline]
    pub fn dims(&self) -> &[usize] {
        self.shape()
    }

    pub fn len(&self) -> usize {
        self.shape().iter().fold(1, |a, b| a * b)
    }

    /// Returns a slice which contains dimmensions of the array.
    ///
    /// Same as [numpy.ndarray.shape](https://docs.scipy.org/doc/numpy/reference/generated/numpy.ndarray.shape.html)
    /// # Example
    /// ```
    /// # extern crate pyo3; extern crate numpy; fn main() {
    /// use numpy::{PyArray, PyArrayModule};
    /// let gil = pyo3::Python::acquire_gil();
    /// let np = PyArrayModule::import(gil.python()).unwrap();
    /// let arr = PyArray::new::<f64>(gil.python(), &np, &[4, 5, 6]);
    /// assert_eq!(arr.shape(), &[4, 5, 6]);
    /// # }
    /// ```
    // C API: https://docs.scipy.org/doc/numpy/reference/c-api.array.html#c.PyArray_DIMS
    pub fn shape(&self) -> &[usize] {
        let n = self.ndim();
        let ptr = self.as_array_ptr();
        unsafe {
            let p = (*ptr).dimensions as *mut usize;
            ::std::slice::from_raw_parts(p, n)
        }
    }

    /// Returns a slice which contains how many bytes you need to jump to the next row.
    ///
    /// Same as [numpy.ndarray.strides](https://docs.scipy.org/doc/numpy/reference/generated/numpy.ndarray.strides.html)
    /// # Example
    /// ```
    /// # extern crate pyo3; extern crate numpy; fn main() {
    /// use numpy::{PyArray, PyArrayModule};
    /// let gil = pyo3::Python::acquire_gil();
    /// let np = PyArrayModule::import(gil.python()).unwrap();
    /// let arr = PyArray::new::<f64>(gil.python(), &np, &[4, 5, 6]);
    /// assert_eq!(arr.strides(), &[240, 48, 8]);
    /// # }
    /// ```
    // C API: https://docs.scipy.org/doc/numpy/reference/c-api.array.html#c.PyArray_STRIDES
    pub fn strides(&self) -> &[isize] {
        let n = self.ndim();
        let ptr = self.as_array_ptr();
        unsafe {
            let p = (*ptr).strides;
            ::std::slice::from_raw_parts(p, n)
        }
    }

    unsafe fn data<T>(&self) -> *mut T {
        let ptr = self.as_array_ptr();
        (*ptr).data as *mut T
    }

    fn ndarray_shape<A>(&self) -> StrideShape<IxDyn> {
        // FIXME may be done more simply
        let shape: Shape<_> = Dim(self.shape()).into();
        let st: Vec<usize> = self
            .strides()
            .iter()
            .map(|&x| x as usize / ::std::mem::size_of::<A>())
            .collect();
        shape.strides(Dim(st))
    }

    pub fn typenum(&self) -> i32 {
        unsafe {
            let descr = (*self.as_array_ptr()).descr;
            (*descr).type_num
        }
    }

    fn type_check<A: types::TypeNum>(&self) -> Result<(), ArrayCastError> {
        let test = A::typenum();
        let truth = self.typenum();
        if A::typenum() == self.typenum() {
            Ok(())
        } else {
            Err(ArrayCastError::to_rust(test, truth))
        }
    }

    /// Get data as a ndarray::ArrayView
    pub fn as_array<A: types::TypeNum>(&self) -> Result<ArrayViewD<A>, ArrayCastError> {
        self.type_check::<A>()?;
        unsafe {
            Ok(ArrayView::from_shape_ptr(
                self.ndarray_shape::<A>(),
                self.data(),
            ))
        }
    }

    /// Get data as a ndarray::ArrayViewMut
    pub fn as_array_mut<A: types::TypeNum>(&self) -> Result<ArrayViewMutD<A>, ArrayCastError> {
        self.type_check::<A>()?;
        unsafe {
            Ok(ArrayViewMut::from_shape_ptr(
                self.ndarray_shape::<A>(),
                self.data(),
            ))
        }
    }

    /// Get data as a Rust immutable slice
    pub fn as_slice<A: types::TypeNum>(&self) -> Result<&[A], ArrayCastError> {
        self.type_check::<A>()?;
        unsafe { Ok(::std::slice::from_raw_parts(self.data(), self.len())) }
    }

    /// Get data as a Rust mutable slice
    pub fn as_slice_mut<A: types::TypeNum>(&self) -> Result<&mut [A], ArrayCastError> {
        self.type_check::<A>()?;
        unsafe { Ok(::std::slice::from_raw_parts_mut(self.data(), self.len())) }
    }

    pub unsafe fn new_<T: types::TypeNum>(
        py: Python,
        np: &PyArrayModule,
        dims: &[usize],
        strides: *mut npy_intp,
        data: *mut c_void,
    ) -> Self {
        let dims: Vec<_> = dims.iter().map(|d| *d as npy_intp).collect();
        let ptr = np.PyArray_New(
            np.get_type_object(npyffi::ArrayType::PyArray_Type),
            dims.len() as i32,
            dims.as_ptr() as *mut npy_intp,
            T::typenum(),
            strides,
            data,
            0,                      // itemsize
            0,                      // flag
            ::std::ptr::null_mut(), //obj
        );
        Self::from_owned_ptr(py, ptr)
    }

    /// a wrapper of [PyArray_SimpleNew](https://docs.scipy.org/doc/numpy/reference/c-api.array.html#c.PyArray_SimpleNew)
    pub fn new<T: TypeNum>(py: Python, np: &PyArrayModule, dims: &[usize]) -> Self {
        unsafe { Self::new_::<T>(py, np, dims, null_mut(), null_mut()) }
    }

    /// a wrapper of [PyArray_ZEROS](https://docs.scipy.org/doc/numpy/reference/c-api.array.html#c.PyArray_ZEROS)
    pub fn zeros<T: TypeNum>(
        py: Python,
        np: &PyArrayModule,
        dims: &[usize],
        order: NPY_ORDER,
    ) -> Self {
        let dims: Vec<npy_intp> = dims.iter().map(|d| *d as npy_intp).collect();
        unsafe {
            let descr = np.PyArray_DescrFromType(T::typenum());
            let ptr = np.PyArray_Zeros(
                dims.len() as i32,
                dims.as_ptr() as *mut npy_intp,
                descr,
                order as i32,
            );
            Self::from_owned_ptr(py, ptr)
        }
    }

    /// a wrapper of [PyArray_Arange](https://docs.scipy.org/doc/numpy/reference/c-api.array.html#c.PyArray_Arange)
    pub fn arange<T: TypeNum>(
        py: Python,
        np: &PyArrayModule,
        start: f64,
        stop: f64,
        step: f64,
    ) -> Self {
        unsafe {
            let ptr = np.PyArray_Arange(start, stop, step, T::typenum());
            Self::from_owned_ptr(py, ptr)
        }
    }
}
<|endoftext|>


<commit_before>use std::io::{Read};

enum Header {

    Incomplete {
        num_segments: Option<usize>,
        segment_slices: Vec<(usize, usize)>,
        partial_read:
    }
    Complete {
        total_words: usize,
        segment_slices: Vec<(usize, usize)>
    }
}

struct MessageReader<R> where R: Read {
    read: R,

    frame: Option<(Option<usize>, Vec<(usize, usize)>

}
<commit_msg>remove accidental checkin<commit_after><|endoftext|>


<commit_before>//! checks for attributes

use rustc::lint::*;
use rustc_front::hir::*;
use reexport::*;
use syntax::codemap::Span;
use syntax::attr::*;
use syntax::ast::{Attribute, MetaList, MetaWord};
use utils::{in_macro, match_path, span_lint};

declare_lint! { pub INLINE_ALWAYS, Warn,
    "`#[inline(always)]` is a bad idea in most cases" }

#[derive(Copy,Clone)]
pub struct AttrPass;

impl LintPass for AttrPass {
    fn get_lints(&self) -> LintArray {
        lint_array!(INLINE_ALWAYS)
    }
}

impl LateLintPass for AttrPass {
    fn check_item(&mut self, cx: &LateContext, item: &Item) {
        if is_relevant_item(item) {
            check_attrs(cx, item.span, &item.name, &item.attrs)
        }
    }

    fn check_impl_item(&mut self, cx: &LateContext, item: &ImplItem) {
        if is_relevant_impl(item) {
            check_attrs(cx, item.span, &item.name, &item.attrs)
        }
    }

    fn check_trait_item(&mut self, cx: &LateContext, item: &TraitItem) {
        if is_relevant_trait(item) {
            check_attrs(cx, item.span, &item.name, &item.attrs)
        }
    }
}

fn is_relevant_item(item: &Item) -> bool {
    if let &ItemFn(_, _, _, _, _, ref block) = &item.node {
        is_relevant_block(block)
    } else { false }
}

fn is_relevant_impl(item: &ImplItem) -> bool {
    match item.node {
        MethodImplItem(_, ref block) => is_relevant_block(block),
        _ => false
    }
}

fn is_relevant_trait(item: &TraitItem) -> bool {
    match item.node {
        MethodTraitItem(_, None) => true,
        MethodTraitItem(_, Some(ref block)) => is_relevant_block(block),
        _ => false
    }
}

fn is_relevant_block(block: &Block) -> bool {
    for stmt in &block.stmts {
        match stmt.node {
            StmtDecl(_, _) => return true,
            StmtExpr(ref expr, _) | StmtSemi(ref expr, _) => {
                return is_relevant_expr(expr);
            }
        }
    }
    block.expr.as_ref().map_or(false, |e| is_relevant_expr(e))
}

fn is_relevant_expr(expr: &Expr) -> bool {
    match expr.node {
        ExprBlock(ref block) => is_relevant_block(block),
        ExprRet(Some(ref e)) => is_relevant_expr(e),
        ExprRet(None) | ExprBreak(_) => false,
        ExprCall(ref path_expr, _) => {
            if let ExprPath(_, ref path) = path_expr.node {
                !match_path(path, &["std", "rt", "begin_unwind"])
            } else { true }
        }
        _ => true
    }
}

fn check_attrs(cx: &LateContext, span: Span, name: &Name,
        attrs: &[Attribute]) {
    if in_macro(cx, span) { return; }

    for attr in attrs {
        if let MetaList(ref inline, ref values) = attr.node.value.node {
            if values.len() != 1 || inline != &"inline" { continue; }
            if let MetaWord(ref always) = values[0].node {
                if always != &"always" { continue; }
                span_lint(cx, INLINE_ALWAYS, attr.span, &format!(
                    "you have declared `#[inline(always)]` on `{}`. This \
                     is usually a bad idea. Are you sure?",
                    name));
            }
        }
    }
}
<commit_msg>Remove "are you sure?" from lint msg.<commit_after>//! checks for attributes

use rustc::lint::*;
use rustc_front::hir::*;
use reexport::*;
use syntax::codemap::Span;
use syntax::attr::*;
use syntax::ast::{Attribute, MetaList, MetaWord};
use utils::{in_macro, match_path, span_lint};

declare_lint! { pub INLINE_ALWAYS, Warn,
    "`#[inline(always)]` is a bad idea in most cases" }

#[derive(Copy,Clone)]
pub struct AttrPass;

impl LintPass for AttrPass {
    fn get_lints(&self) -> LintArray {
        lint_array!(INLINE_ALWAYS)
    }
}

impl LateLintPass for AttrPass {
    fn check_item(&mut self, cx: &LateContext, item: &Item) {
        if is_relevant_item(item) {
            check_attrs(cx, item.span, &item.name, &item.attrs)
        }
    }

    fn check_impl_item(&mut self, cx: &LateContext, item: &ImplItem) {
        if is_relevant_impl(item) {
            check_attrs(cx, item.span, &item.name, &item.attrs)
        }
    }

    fn check_trait_item(&mut self, cx: &LateContext, item: &TraitItem) {
        if is_relevant_trait(item) {
            check_attrs(cx, item.span, &item.name, &item.attrs)
        }
    }
}

fn is_relevant_item(item: &Item) -> bool {
    if let &ItemFn(_, _, _, _, _, ref block) = &item.node {
        is_relevant_block(block)
    } else { false }
}

fn is_relevant_impl(item: &ImplItem) -> bool {
    match item.node {
        MethodImplItem(_, ref block) => is_relevant_block(block),
        _ => false
    }
}

fn is_relevant_trait(item: &TraitItem) -> bool {
    match item.node {
        MethodTraitItem(_, None) => true,
        MethodTraitItem(_, Some(ref block)) => is_relevant_block(block),
        _ => false
    }
}

fn is_relevant_block(block: &Block) -> bool {
    for stmt in &block.stmts {
        match stmt.node {
            StmtDecl(_, _) => return true,
            StmtExpr(ref expr, _) | StmtSemi(ref expr, _) => {
                return is_relevant_expr(expr);
            }
        }
    }
    block.expr.as_ref().map_or(false, |e| is_relevant_expr(e))
}

fn is_relevant_expr(expr: &Expr) -> bool {
    match expr.node {
        ExprBlock(ref block) => is_relevant_block(block),
        ExprRet(Some(ref e)) => is_relevant_expr(e),
        ExprRet(None) | ExprBreak(_) => false,
        ExprCall(ref path_expr, _) => {
            if let ExprPath(_, ref path) = path_expr.node {
                !match_path(path, &["std", "rt", "begin_unwind"])
            } else { true }
        }
        _ => true
    }
}

fn check_attrs(cx: &LateContext, span: Span, name: &Name,
        attrs: &[Attribute]) {
    if in_macro(cx, span) { return; }

    for attr in attrs {
        if let MetaList(ref inline, ref values) = attr.node.value.node {
            if values.len() != 1 || inline != &"inline" { continue; }
            if let MetaWord(ref always) = values[0].node {
                if always != &"always" { continue; }
                span_lint(cx, INLINE_ALWAYS, attr.span, &format!(
                    "you have declared `#[inline(always)]` on `{}`. This \
                     is usually a bad idea",
                    name));
            }
        }
    }
}
<|endoftext|>


<commit_before>//! Primitives for audio playback.

use std::fmt;
use std::io::Cursor;
use std::rc::Rc;
use std::time::Duration;

use rodio as r;
use rodio::Source as _Source;

use hub::Operation as HubOperation;
use object::Object;

/// Audio segment with sound effects.
///
/// Can be loaded from file using [`Factory::load_audio`](struct.Factory.html#method.load_audio).
#[derive(Debug, Clone)]
pub struct Clip {
    data: Rc<Vec<u8>>,
    repeat: bool,
    duration: Option<Duration>,
    delay: Option<Duration>,
    fade_in: Option<Duration>,
    speed: f32,
}

impl Clip {
    pub(crate) fn new(data: Vec<u8>) -> Self {
        Clip {
            data: Rc::new(data),
            repeat: false,
            duration: None,
            delay: None,
            fade_in: None,
            speed: 1.0,
        }
    }

    /// Passing true enforces looping sound. Defaults to `false`.
    pub fn repeat(
        &mut self,
        enable: bool,
    ) {
        self.repeat = enable;
    }

    /// Clip the sound to the desired duration.
    pub fn take_duration(
        &mut self,
        duration: Duration,
    ) {
        self.duration = Some(duration);
    }

    /// Play sound after desired delay.
    pub fn delay(
        &mut self,
        delay: Duration,
    ) {
        self.delay = Some(delay);
    }

    /// Fade in sound in desired duration.
    pub fn fade_in(
        &mut self,
        duration: Duration,
    ) {
        self.fade_in = Some(duration);
    }

    /// Adjust the playback speed. Defaults to `1.0`.
    pub fn speed(
        &mut self,
        ratio: f32,
    ) {
        self.speed = ratio;
    }
}

#[derive(Debug, Clone)]
pub(crate) enum Operation {
    Append(Clip),
    Resume,
    Pause,
    Stop,
    SetVolume(f32),
}

#[derive(Debug)]
pub(crate) struct AudioData {
    pub(crate) source: SourceInternal,
}

impl AudioData {
    pub(crate) fn new() -> Self {
        let endpoint = if let Some(endpoint) = r::get_default_endpoint() {
            endpoint
        } else {
            // TODO: Better error handling
            panic!("Can't get default audio endpoint, can't play sound");
        };
        let sink = r::Sink::new(&endpoint);
        AudioData {
            source: SourceInternal::D2(sink),
        }
    }
}

/// Audio source. Can play only one sound at a time.
///
/// You must add it to the scene to play sounds.
/// You may create several `Source`s to play sounds simultaneously.
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct Source {
    pub(crate) object: Object,
}

impl Source {
    pub(crate) fn with_object(object: Object) -> Self {
        Source { object }
    }

    /// Add clip to the queue.
    pub fn play(
        &self,
        clip: &Clip,
    ) {
        let msg = HubOperation::SetAudio(Operation::Append(clip.clone()));
        let _ = self.object.tx.send((self.object.node.downgrade(), msg));
    }

    /// Pause current sound.
    ///
    /// You can [`resume`](struct.Source.html#method.resume) playback.
    pub fn pause(&self) {
        let msg = HubOperation::SetAudio(Operation::Pause);
        let _ = self.object.tx.send((self.object.node.downgrade(), msg));
    }

    /// Resume playback after [`pausing`](struct.Source.html#method.pause).
    pub fn resume(&self) {
        let msg = HubOperation::SetAudio(Operation::Resume);
        let _ = self.object.tx.send((self.object.node.downgrade(), msg));
    }

    /// Stop the playback by emptying the queue.
    pub fn stop(&self) {
        let msg = HubOperation::SetAudio(Operation::Stop);
        let _ = self.object.tx.send((self.object.node.downgrade(), msg));
    }

    /// Adjust playback volume.
    ///
    /// Default value is `1.0`.
    pub fn set_volume(
        &self,
        volume: f32,
    ) {
        let msg = HubOperation::SetAudio(Operation::SetVolume(volume));
        let _ = self.object.tx.send((self.object.node.downgrade(), msg));
    }
}

//TODO: Remove dead_code lint
#[allow(dead_code)]
pub(crate) enum SourceInternal {
    D2(r::Sink),
    D3(r::SpatialSink),
}

impl fmt::Debug for SourceInternal {
    fn fmt(
        &self,
        f: &mut fmt::Formatter,
    ) -> fmt::Result {
        match *self {
            SourceInternal::D2(_) => write!(f, "SourceInternal::D2"),
            SourceInternal::D3(_) => write!(f, "SourceInternal::D3"),
        }
    }
}

impl SourceInternal {
    pub(crate) fn pause(&self) {
        match *self {
            SourceInternal::D2(ref sink) => sink.pause(),
            _ => unimplemented!(),
        }
    }

    pub(crate) fn resume(&self) {
        match *self {
            SourceInternal::D2(ref sink) => sink.play(),
            _ => unimplemented!(),
        }
    }

    pub(crate) fn stop(&self) {
        match *self {
            SourceInternal::D2(ref sink) => sink.stop(),
            _ => unimplemented!(),
        }
    }

    pub(crate) fn set_volume(
        &mut self,
        volume: f32,
    ) {
        match *self {
            SourceInternal::D2(ref mut sink) => sink.set_volume(volume),
            _ => unimplemented!(),
        }
    }

    pub(crate) fn append(
        &mut self,
        clip: Clip,
    ) {
        match *self {
            SourceInternal::D2(ref mut sink) => {
                let vec: Vec<u8> = (&*clip.data).clone();
                let decoder = r::Decoder::new(Cursor::new(vec));
                let mut boxed: Box<r::Source<Item = i16> + Send> = if let Ok(decoder) = decoder {
                    Box::new(decoder)
                } else {
                    eprintln!("Can't recognize audio clip format, can't play sound");
                    return;
                };
                if clip.repeat {
                    boxed = Box::new(boxed.repeat_infinite());
                }
                if clip.speed != 1.0 {
                    boxed = Box::new(boxed.speed(clip.speed));
                }
                if let Some(duration) = clip.delay {
                    boxed = Box::new(boxed.delay(duration));
                }
                if let Some(duration) = clip.duration {
                    boxed = Box::new(boxed.take_duration(duration));
                }
                if let Some(duration) = clip.fade_in {
                    boxed = Box::new(boxed.fade_in(duration));
                }
                sink.append(boxed);
            }
            SourceInternal::D3(_) => unimplemented!(),
        }
    }
}

/* TODO: Implement 3d sound.
pub struct Listener {
    pub(crate) object: Object,
}
*/
<commit_msg>Silence deprecated warning for get_default_endpoints<commit_after>//! Primitives for audio playback.

use std::fmt;
use std::io::Cursor;
use std::rc::Rc;
use std::time::Duration;

use rodio as r;
use rodio::Source as _Source;

use hub::Operation as HubOperation;
use object::Object;

/// Audio segment with sound effects.
///
/// Can be loaded from file using [`Factory::load_audio`](struct.Factory.html#method.load_audio).
#[derive(Debug, Clone)]
pub struct Clip {
    data: Rc<Vec<u8>>,
    repeat: bool,
    duration: Option<Duration>,
    delay: Option<Duration>,
    fade_in: Option<Duration>,
    speed: f32,
}

impl Clip {
    pub(crate) fn new(data: Vec<u8>) -> Self {
        Clip {
            data: Rc::new(data),
            repeat: false,
            duration: None,
            delay: None,
            fade_in: None,
            speed: 1.0,
        }
    }

    /// Passing true enforces looping sound. Defaults to `false`.
    pub fn repeat(
        &mut self,
        enable: bool,
    ) {
        self.repeat = enable;
    }

    /// Clip the sound to the desired duration.
    pub fn take_duration(
        &mut self,
        duration: Duration,
    ) {
        self.duration = Some(duration);
    }

    /// Play sound after desired delay.
    pub fn delay(
        &mut self,
        delay: Duration,
    ) {
        self.delay = Some(delay);
    }

    /// Fade in sound in desired duration.
    pub fn fade_in(
        &mut self,
        duration: Duration,
    ) {
        self.fade_in = Some(duration);
    }

    /// Adjust the playback speed. Defaults to `1.0`.
    pub fn speed(
        &mut self,
        ratio: f32,
    ) {
        self.speed = ratio;
    }
}

#[derive(Debug, Clone)]
pub(crate) enum Operation {
    Append(Clip),
    Resume,
    Pause,
    Stop,
    SetVolume(f32),
}

#[derive(Debug)]
pub(crate) struct AudioData {
    pub(crate) source: SourceInternal,
}

impl AudioData {
    pub(crate) fn new() -> Self {
        // TODO: Change to `r::default_endpoint()` in next `rodio` release.
        #[allow(deprecated)]
        let endpoint = if let Some(endpoint) = r::get_default_endpoint() {
            endpoint
        } else {
            // TODO: Better error handling
            panic!("Can't get default audio endpoint, can't play sound");
        };
        let sink = r::Sink::new(&endpoint);
        AudioData {
            source: SourceInternal::D2(sink),
        }
    }
}

/// Audio source. Can play only one sound at a time.
///
/// You must add it to the scene to play sounds.
/// You may create several `Source`s to play sounds simultaneously.
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct Source {
    pub(crate) object: Object,
}

impl Source {
    pub(crate) fn with_object(object: Object) -> Self {
        Source { object }
    }

    /// Add clip to the queue.
    pub fn play(
        &self,
        clip: &Clip,
    ) {
        let msg = HubOperation::SetAudio(Operation::Append(clip.clone()));
        let _ = self.object.tx.send((self.object.node.downgrade(), msg));
    }

    /// Pause current sound.
    ///
    /// You can [`resume`](struct.Source.html#method.resume) playback.
    pub fn pause(&self) {
        let msg = HubOperation::SetAudio(Operation::Pause);
        let _ = self.object.tx.send((self.object.node.downgrade(), msg));
    }

    /// Resume playback after [`pausing`](struct.Source.html#method.pause).
    pub fn resume(&self) {
        let msg = HubOperation::SetAudio(Operation::Resume);
        let _ = self.object.tx.send((self.object.node.downgrade(), msg));
    }

    /// Stop the playback by emptying the queue.
    pub fn stop(&self) {
        let msg = HubOperation::SetAudio(Operation::Stop);
        let _ = self.object.tx.send((self.object.node.downgrade(), msg));
    }

    /// Adjust playback volume.
    ///
    /// Default value is `1.0`.
    pub fn set_volume(
        &self,
        volume: f32,
    ) {
        let msg = HubOperation::SetAudio(Operation::SetVolume(volume));
        let _ = self.object.tx.send((self.object.node.downgrade(), msg));
    }
}

//TODO: Remove dead_code lint
#[allow(dead_code)]
pub(crate) enum SourceInternal {
    D2(r::Sink),
    D3(r::SpatialSink),
}

impl fmt::Debug for SourceInternal {
    fn fmt(
        &self,
        f: &mut fmt::Formatter,
    ) -> fmt::Result {
        match *self {
            SourceInternal::D2(_) => write!(f, "SourceInternal::D2"),
            SourceInternal::D3(_) => write!(f, "SourceInternal::D3"),
        }
    }
}

impl SourceInternal {
    pub(crate) fn pause(&self) {
        match *self {
            SourceInternal::D2(ref sink) => sink.pause(),
            _ => unimplemented!(),
        }
    }

    pub(crate) fn resume(&self) {
        match *self {
            SourceInternal::D2(ref sink) => sink.play(),
            _ => unimplemented!(),
        }
    }

    pub(crate) fn stop(&self) {
        match *self {
            SourceInternal::D2(ref sink) => sink.stop(),
            _ => unimplemented!(),
        }
    }

    pub(crate) fn set_volume(
        &mut self,
        volume: f32,
    ) {
        match *self {
            SourceInternal::D2(ref mut sink) => sink.set_volume(volume),
            _ => unimplemented!(),
        }
    }

    pub(crate) fn append(
        &mut self,
        clip: Clip,
    ) {
        match *self {
            SourceInternal::D2(ref mut sink) => {
                let vec: Vec<u8> = (&*clip.data).clone();
                let decoder = r::Decoder::new(Cursor::new(vec));
                let mut boxed: Box<r::Source<Item = i16> + Send> = if let Ok(decoder) = decoder {
                    Box::new(decoder)
                } else {
                    eprintln!("Can't recognize audio clip format, can't play sound");
                    return;
                };
                if clip.repeat {
                    boxed = Box::new(boxed.repeat_infinite());
                }
                if clip.speed != 1.0 {
                    boxed = Box::new(boxed.speed(clip.speed));
                }
                if let Some(duration) = clip.delay {
                    boxed = Box::new(boxed.delay(duration));
                }
                if let Some(duration) = clip.duration {
                    boxed = Box::new(boxed.take_duration(duration));
                }
                if let Some(duration) = clip.fade_in {
                    boxed = Box::new(boxed.fade_in(duration));
                }
                sink.append(boxed);
            }
            SourceInternal::D3(_) => unimplemented!(),
        }
    }
}

/* TODO: Implement 3d sound.
pub struct Listener {
    pub(crate) object: Object,
}
*/
<|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 https://mozilla.org/MPL/2.0/.

use common::is_whitespace;

use regex::Regex;
use std::borrow::Cow;

/// Removes unnecessary whitespaces from a String.
///
/// # Example
///
/// ```
/// use crowbook_text_processing::clean::remove_whitespaces;
/// let s = remove_whitespaces("  A  string   with   more   whitespaces  than  needed   ");
/// assert_eq!(&s, " A string with more whitespaces than needed ");
/// ```
pub fn remove_whitespaces<'a, S: Into<Cow<'a, str>>>(input: S) -> Cow<'a, str> {
    lazy_static! {
        static ref REGEX: Regex = Regex::new(r"[  \x{202F}\x{2002}]{2,}?").unwrap();
    }
    let input = input.into();
    let first = REGEX.find(&input);
    if let Some((first, _)) = first {
        let mut new_s = String::with_capacity(input.len());
        new_s.push_str(&input[0..first]);
        let mut previous_space = false;
        for c in input[first..].chars() {
            if is_whitespace(c) {
                if previous_space {
                    // previous char already a space, don't copy it
                } else {
                    new_s.push(c);
                    previous_space = true;
                }
            } else {
                previous_space = false;
                new_s.push(c);
            }
        }
        Cow::Owned(new_s)
    } else {
        input
    }
}

/// Replace quotes with more typographic variants
///
/// While it should work pretty well for double quotes (`"`), the rules for single
/// quote (`'`) are more ambiguous, as it can be a quote, or an apostrophe and it's not
/// that easy to get right.
///
/// # Example
///
/// ```
/// use crowbook_text_processing::typographic_quotes;
/// let s = typographic_quotes("\"foo\"");
/// assert_eq!(&s, "“foo”");
/// let s = typographic_quotes("'foo'");
/// assert_eq!(&s, "‘foo’");
/// ```
pub fn typographic_quotes<'a, S: Into<Cow<'a, str>>>(input: S) -> Cow<'a, str> {
    /// Custom whitespace-detection function, including `,`, `;`, `.`, `!`, `?`, and `:`
    fn is_whitespace(c: char) -> bool {
        match c {
            ',' | '.' | ';' | '!' | '?' | ':' => true,
            _ => c.is_whitespace()
        }
    }

    lazy_static! {
        static ref REGEX: Regex = Regex::new("[\"\']").unwrap();
    }
    let input = input.into();
    let first = REGEX.find(&input);
    if let Some((mut first, _)) = first {
        let mut new_s = String::with_capacity(input.len());
        if first > 0 {
            // Move one step backward since we might need to know if previous char was
            // a letter or not
            first -= 1;
        }
        new_s.push_str(&input[0..first]);
        let chars = input[first..].chars().collect::<Vec<_>>();
        let mut has_opened_quote = false;
        for i in 0..chars.len() {
            let c = chars[i];
            match c {
                '"' => {
                    if i > 0 && !is_whitespace(chars[i - 1]) {
                        new_s.push('”');
                    } else if i < chars.len() - 1 && !is_whitespace(chars[i + 1]) {
                        new_s.push('“');
                    } else {
                        new_s.push('"');
                    }
                },
                '\'' => {
                    let prev = if i > 0 {
                        Some(!is_whitespace(chars[i - 1]))
                    } else {
                        None
                    };
                    let next = if i < chars.len() - 1 {
                        Some(!is_whitespace(chars[i + 1]))
                    } else {
                        None
                    };
                    let replacement = match (prev, next) {
                        // Elision, it's closing
                        (Some(true), Some(true)) => '’',

                        // Beginning of word, it's opening (not always though)
                        (Some(false), Some(true))
                            | (None, Some(true))
                            => {
                                let mut is_next_closing = false;
                                for j in (i + 1)..chars.len() {
                                    if chars[j] == '\'' {
                                        println!("match at {}", j);
                                        if chars[j-1].is_whitespace() {
                                            println!("prev is whitespace, not closing quote");
                                            continue;
                                        } else {
                                            if j >= chars.len() - 1
                                                || is_whitespace(chars[j+1])
                                                || chars[j+1] == '"' {
                                                    is_next_closing = true;
                                                    break;
                                                }
                                            else {
                                                println!("j: {}, len: {}", j, chars.len());
                                            }
                                        }
                                    }
                                }
                                println!("is_next_closing: {}", is_next_closing);
                                if is_next_closing && !has_opened_quote {
                                    has_opened_quote = true;
                                    '‘'
                                } else {
                                    '’'
                                }
                            }

                        // Apostrophe at end of word, it's closing
                        (Some(true), Some(false))
                            | (Some(true), None)
                            => {
                                has_opened_quote = false;
                                '’'
                            },

                        _ => '\'',
                    };
                    new_s.push(replacement);
                    // if i > 0 && !chars[i - 1].is_whitespace() {
                    //     new_s.push('’');
                    // } else if i < chars.len() - 1 && !chars[i + 1].is_whitespace() {
                    //     new_s.push('‘');
                    // } else {
                    //     new_s.push('\'');
                    // }
                },
                _ => new_s.push(c)
            }
        }
        Cow::Owned(new_s)
    } else {
        input
    }
}

#[test]
fn remove_whitespaces_1() {
    let s = "   Remove    supplementary   spaces    but    don't    trim     either   ";
    let res = remove_whitespaces(s);
    assert_eq!(&res, " Remove supplementary spaces but don't trim either ");
}

#[test]
fn typographic_quotes_1() {
    let s = "Some string without ' typographic ' quotes";
    let res = typographic_quotes(s);
    assert_eq!(&res, s);
}

#[test]
fn typographic_quotes_2() {
    let s = typographic_quotes("\"foo\"");
    assert_eq!(&s, "“foo”");
    let s = typographic_quotes("'foo'");
    assert_eq!(&s, "‘foo’");
}

#[test]
fn typographic_quotes_3() {
    let s = typographic_quotes("\'mam, how are you?");
    assert_eq!(&s, "’mam, how are you?");
}

#[test]
fn typographic_quotes_4() {
    let s = typographic_quotes("some char: 'c', '4', '&'");
    assert_eq!(&s, "some char: ‘c’, ‘4’, ‘&’");
}

#[test]
fn typographic_quotes_5() {
    let s = typographic_quotes("It's a good day to say 'hi'");
    assert_eq!(&s, "It’s a good day to say ‘hi’");
}

#[test]
fn typographic_quotes_6() {
    let s = typographic_quotes("The '60s were nice, weren't they?");
    assert_eq!(&s, "The ’60s were nice, weren’t they?");
}

#[test]
fn typographic_quotes_7() {
    let s = typographic_quotes("Plurals' possessive");
    assert_eq!(&s, "Plurals’ possessive");
}

#[test]
fn typographic_quotes_8() {
    let s = typographic_quotes("\"I like 'That '70s show'\", she said");
    assert_eq!(&s, "“I like ‘That ’70s show’”, she said");
}
<commit_msg>Fix typographic quotes algorithm<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 https://mozilla.org/MPL/2.0/.

use common::is_whitespace;

use regex::Regex;
use std::borrow::Cow;

/// Removes unnecessary whitespaces from a String.
///
/// # Example
///
/// ```
/// use crowbook_text_processing::clean::remove_whitespaces;
/// let s = remove_whitespaces("  A  string   with   more   whitespaces  than  needed   ");
/// assert_eq!(&s, " A string with more whitespaces than needed ");
/// ```
pub fn remove_whitespaces<'a, S: Into<Cow<'a, str>>>(input: S) -> Cow<'a, str> {
    lazy_static! {
        static ref REGEX: Regex = Regex::new(r"[  \x{202F}\x{2002}]{2,}?").unwrap();
    }
    let input = input.into();
    let first = REGEX.find(&input);
    if let Some((first, _)) = first {
        let mut new_s = String::with_capacity(input.len());
        new_s.push_str(&input[0..first]);
        let mut previous_space = false;
        for c in input[first..].chars() {
            if is_whitespace(c) {
                if previous_space {
                    // previous char already a space, don't copy it
                } else {
                    new_s.push(c);
                    previous_space = true;
                }
            } else {
                previous_space = false;
                new_s.push(c);
            }
        }
        Cow::Owned(new_s)
    } else {
        input
    }
}

/// Replace quotes with more typographic variants
///
/// While it should work pretty well for double quotes (`"`), the rules for single
/// quote (`'`) are more ambiguous, as it can be a quote, or an apostrophe and it's not
/// that easy to get right.
///
/// # Example
///
/// ```
/// use crowbook_text_processing::typographic_quotes;
/// let s = typographic_quotes("\"foo\"");
/// assert_eq!(&s, "“foo”");
/// let s = typographic_quotes("'foo'");
/// assert_eq!(&s, "‘foo’");
/// ```
pub fn typographic_quotes<'a, S: Into<Cow<'a, str>>>(input: S) -> Cow<'a, str> {
    /// Custom whitespace-detection function, including `,`, `;`, `.`, `!`, `?`, and `:`
    fn is_whitespace(c: char) -> bool {
        match c {
            ',' | '.' | ';' | '!' | '?' | ':' | '"'
                | '(' | ')' => true,
            _ => c.is_whitespace()
        }
    }

    lazy_static! {
        static ref REGEX: Regex = Regex::new("[\"\']").unwrap();
    }
    let input = input.into();
    let first = REGEX.find(&input);
    if let Some((mut first, _)) = first {
        let mut new_s = String::with_capacity(input.len());
        if first > 0 {
            // Move one step backward since we might need to know if previous char was
            // a letter or not
            first -= 1;
        }
        new_s.push_str(&input[0..first]);
        let chars = input[first..].chars().collect::<Vec<_>>();
        let mut closing_quote = None;
        for i in 0..chars.len() {
            let c = chars[i];
            let has_opened_quote = if let Some(n) = closing_quote {
                i <= n
            } else {
                false
            };
            match c {
                '"' => {
                    if i > 0 && !chars[i-1].is_whitespace() {
                        new_s.push('”');
                    } else if i < chars.len() - 1 && !is_whitespace(chars[i+1]) {
                        new_s.push('“');
                    } else {
                        new_s.push('"');
                    }
                },
                '\'' => {
                    let prev = if i > 0 {
                        Some(!chars[i - 1].is_whitespace())
                    } else {
                        None
                    };
                    let next = if i < chars.len() - 1 {
                        Some(!chars[i+1].is_whitespace())
                    } else {
                        None
                    };
                    let replacement = match (prev, next) {
                        // Elision, it's closing
                        (Some(true), Some(true)) => '’',

                        // Beginning of word, it's opening (not always though)
                        (Some(false), Some(true))
                            | (None, Some(true))
                            => {
                                let mut is_next_closing = false;
                                for j in (i + 1)..chars.len() {
                                    if chars[j] == '\'' {
                                        if chars[j-1].is_whitespace() {
                                            continue;
                                        } else {
                                            if j >= chars.len() - 1
                                                || is_whitespace(chars[j+1]) {
                                                    is_next_closing = true;
                                                    closing_quote = Some(j);
                                                    break;
                                                }
                                        }
                                    }
                                }
                                if is_next_closing && !has_opened_quote {
                                    '‘'
                                } else {
                                    '’'
                                }
                            }

                        // Apostrophe at end of word, it's closing
                        (Some(true), Some(false))
                            | (Some(true), None)
                            => {
                                '’'
                            },

                        _ => '\'',
                    };
                    new_s.push(replacement);
                    // if i > 0 && !chars[i - 1].is_whitespace() {
                    //     new_s.push('’');
                    // } else if i < chars.len() - 1 && !chars[i + 1].is_whitespace() {
                    //     new_s.push('‘');
                    // } else {
                    //     new_s.push('\'');
                    // }
                },
                _ => new_s.push(c)
            }
        }
        Cow::Owned(new_s)
    } else {
        input
    }
}

#[test]
fn remove_whitespaces_1() {
    let s = "   Remove    supplementary   spaces    but    don't    trim     either   ";
    let res = remove_whitespaces(s);
    assert_eq!(&res, " Remove supplementary spaces but don't trim either ");
}

#[test]
fn typographic_quotes_1() {
    let s = "Some string without ' typographic ' quotes";
    let res = typographic_quotes(s);
    assert_eq!(&res, s);
}

#[test]
fn typographic_quotes_2() {
    let s = typographic_quotes("\"foo\"");
    assert_eq!(&s, "“foo”");
    let s = typographic_quotes("'foo'");
    assert_eq!(&s, "‘foo’");
}

#[test]
fn typographic_quotes_3() {
    let s = typographic_quotes("\'mam, how are you?");
    assert_eq!(&s, "’mam, how are you?");
}

#[test]
fn typographic_quotes_4() {
    let s = typographic_quotes("some char: 'c', '4', '&'");
    assert_eq!(&s, "some char: ‘c’, ‘4’, ‘&’");
}

#[test]
fn typographic_quotes_5() {
    let s = typographic_quotes("It's a good day to say 'hi'");
    assert_eq!(&s, "It’s a good day to say ‘hi’");
}

#[test]
fn typographic_quotes_6() {
    let s = typographic_quotes("The '60s were nice, weren't they?");
    assert_eq!(&s, "The ’60s were nice, weren’t they?");
}

#[test]
fn typographic_quotes_7() {
    let s = typographic_quotes("Plurals' possessive");
    assert_eq!(&s, "Plurals’ possessive");
}

#[test]
fn typographic_quotes_8() {
    let s = typographic_quotes("\"I like 'That '70s show'\", she said");
    assert_eq!(&s, "“I like ‘That ’70s show’”, she said");
}

#[test]
fn typographic_quotes_9() {
    let s = typographic_quotes("some char: '!', '?', ','");
    assert_eq!(&s, "some char: ‘!’, ‘?’, ‘,’");
}
<|endoftext|>