<commit_before>// error-pattern:yummy
#![feature(box_syntax)]
#![feature(rustc_private)]
#![allow(unknown_lints, missing_docs_in_private_items)]

extern crate clippy_lints;
extern crate getopts;
extern crate rustc;
extern crate rustc_codegen_utils;
extern crate rustc_driver;
extern crate rustc_errors;
extern crate rustc_plugin;
extern crate syntax;

use rustc_driver::{driver::CompileController, Compilation};
use std::process::Command;

#[allow(print_stdout)]
fn show_version() {
    println!(env!("CARGO_PKG_VERSION"));
}

pub fn main() {
    use std::env;

    if std::env::args().any(|a| a == "--version" || a == "-V") {
        show_version();
        return;
    }

    let sys_root = option_env!("SYSROOT")
        .map(String::from)
        .or_else(|| std::env::var("SYSROOT").ok())
        .or_else(|| {
            let home = option_env!("RUSTUP_HOME").or(option_env!("MULTIRUST_HOME"));
            let toolchain = option_env!("RUSTUP_TOOLCHAIN").or(option_env!("MULTIRUST_TOOLCHAIN"));
            home.and_then(|home| toolchain.map(|toolchain| format!("{}/toolchains/{}", home, toolchain)))
        })
        .or_else(|| {
            Command::new("rustc")
                .arg("--print")
                .arg("sysroot")
                .output()
                .ok()
                .and_then(|out| String::from_utf8(out.stdout).ok())
                .map(|s| s.trim().to_owned())
        })
        .expect("need to specify SYSROOT env var during clippy compilation, or use rustup or multirust");

    // Setting RUSTC_WRAPPER causes Cargo to pass 'rustc' as the first argument.
    // We're invoking the compiler programmatically, so we ignore this/
    let mut orig_args: Vec<String> = env::args().collect();
    if orig_args.len() <= 1 {
        std::process::exit(1);
    }
    if orig_args[1] == "rustc" {
        // we still want to be able to invoke it normally though
        orig_args.remove(1);
    }
    // this conditional check for the --sysroot flag is there so users can call
    // `clippy_driver` directly
    // without having to pass --sysroot or anything
    let mut args: Vec<String> = if orig_args.iter().any(|s| s == "--sysroot") {
        orig_args.clone()
    } else {
        orig_args
            .clone()
            .into_iter()
            .chain(Some("--sysroot".to_owned()))
            .chain(Some(sys_root))
            .collect()
    };

    // this check ensures that dependencies are built but not linted and the final
    // crate is
    // linted but not built
    let clippy_enabled = env::var("CLIPPY_TESTS").ok().map_or(false, |val| val == "true")
        || orig_args.iter().any(|s| s == "--emit=dep-info,metadata");

    if clippy_enabled {
        args.extend_from_slice(&["--cfg".to_owned(), r#"feature="cargo-clippy""#.to_owned()]);
        if let Ok(extra_args) = env::var("CLIPPY_ARGS") {
            args.extend(
                extra_args
                    .split("__CLIPPY_HACKERY__")
                    .filter(|s| !s.is_empty())
                    .map(str::to_owned),
            );
        }
    }

    let mut controller = CompileController::basic();
    if clippy_enabled {
        controller.after_parse.callback = Box::new(move |state| {
            let mut registry = rustc_plugin::registry::Registry::new(
                state.session,
                state
                    .krate
                    .as_ref()
                    .expect(
                        "at this compilation stage \
                         the crate must be parsed",
                    )
                    .span,
            );
            registry.args_hidden = Some(Vec::new());
            clippy_lints::register_plugins(&mut registry);

            let rustc_plugin::registry::Registry {
                early_lint_passes,
                late_lint_passes,
                lint_groups,
                llvm_passes,
                attributes,
                ..
            } = registry;
            let sess = &state.session;
            let mut ls = sess.lint_store.borrow_mut();
            for pass in early_lint_passes {
                ls.register_early_pass(Some(sess), true, pass);
            }
            for pass in late_lint_passes {
                ls.register_late_pass(Some(sess), true, pass);
            }

            for (name, to) in lint_groups {
                ls.register_group(Some(sess), true, name, to);
            }

            sess.plugin_llvm_passes.borrow_mut().extend(llvm_passes);
            sess.plugin_attributes.borrow_mut().extend(attributes);
        });
    }
    controller.compilation_done.stop = Compilation::Stop;

    rustc_driver::run_compiler(&args, Box::new(controller), None, None);
}
<commit_msg>Ensure a non-zero value is returned by clippy if compilation fails<commit_after>// error-pattern:yummy
#![feature(box_syntax)]
#![feature(rustc_private)]
#![allow(unknown_lints, missing_docs_in_private_items)]

extern crate clippy_lints;
extern crate getopts;
extern crate rustc;
extern crate rustc_codegen_utils;
extern crate rustc_driver;
extern crate rustc_errors;
extern crate rustc_plugin;
extern crate syntax;

use rustc_driver::{driver::CompileController, Compilation};
use std::process::{exit, Command};

#[allow(print_stdout)]
fn show_version() {
    println!(env!("CARGO_PKG_VERSION"));
}

pub fn main() {
    use std::env;

    if std::env::args().any(|a| a == "--version" || a == "-V") {
        show_version();
        return;
    }

    let sys_root = option_env!("SYSROOT")
        .map(String::from)
        .or_else(|| std::env::var("SYSROOT").ok())
        .or_else(|| {
            let home = option_env!("RUSTUP_HOME").or(option_env!("MULTIRUST_HOME"));
            let toolchain = option_env!("RUSTUP_TOOLCHAIN").or(option_env!("MULTIRUST_TOOLCHAIN"));
            home.and_then(|home| toolchain.map(|toolchain| format!("{}/toolchains/{}", home, toolchain)))
        })
        .or_else(|| {
            Command::new("rustc")
                .arg("--print")
                .arg("sysroot")
                .output()
                .ok()
                .and_then(|out| String::from_utf8(out.stdout).ok())
                .map(|s| s.trim().to_owned())
        })
        .expect("need to specify SYSROOT env var during clippy compilation, or use rustup or multirust");

    // Setting RUSTC_WRAPPER causes Cargo to pass 'rustc' as the first argument.
    // We're invoking the compiler programmatically, so we ignore this/
    let mut orig_args: Vec<String> = env::args().collect();
    if orig_args.len() <= 1 {
        std::process::exit(1);
    }
    if orig_args[1] == "rustc" {
        // we still want to be able to invoke it normally though
        orig_args.remove(1);
    }
    // this conditional check for the --sysroot flag is there so users can call
    // `clippy_driver` directly
    // without having to pass --sysroot or anything
    let mut args: Vec<String> = if orig_args.iter().any(|s| s == "--sysroot") {
        orig_args.clone()
    } else {
        orig_args
            .clone()
            .into_iter()
            .chain(Some("--sysroot".to_owned()))
            .chain(Some(sys_root))
            .collect()
    };

    // this check ensures that dependencies are built but not linted and the final
    // crate is
    // linted but not built
    let clippy_enabled = env::var("CLIPPY_TESTS").ok().map_or(false, |val| val == "true")
        || orig_args.iter().any(|s| s == "--emit=dep-info,metadata");

    if clippy_enabled {
        args.extend_from_slice(&["--cfg".to_owned(), r#"feature="cargo-clippy""#.to_owned()]);
        if let Ok(extra_args) = env::var("CLIPPY_ARGS") {
            args.extend(
                extra_args
                    .split("__CLIPPY_HACKERY__")
                    .filter(|s| !s.is_empty())
                    .map(str::to_owned),
            );
        }
    }

    let mut controller = CompileController::basic();
    if clippy_enabled {
        controller.after_parse.callback = Box::new(move |state| {
            let mut registry = rustc_plugin::registry::Registry::new(
                state.session,
                state
                    .krate
                    .as_ref()
                    .expect(
                        "at this compilation stage \
                         the crate must be parsed",
                    )
                    .span,
            );
            registry.args_hidden = Some(Vec::new());
            clippy_lints::register_plugins(&mut registry);

            let rustc_plugin::registry::Registry {
                early_lint_passes,
                late_lint_passes,
                lint_groups,
                llvm_passes,
                attributes,
                ..
            } = registry;
            let sess = &state.session;
            let mut ls = sess.lint_store.borrow_mut();
            for pass in early_lint_passes {
                ls.register_early_pass(Some(sess), true, pass);
            }
            for pass in late_lint_passes {
                ls.register_late_pass(Some(sess), true, pass);
            }

            for (name, to) in lint_groups {
                ls.register_group(Some(sess), true, name, to);
            }

            sess.plugin_llvm_passes.borrow_mut().extend(llvm_passes);
            sess.plugin_attributes.borrow_mut().extend(attributes);
        });
    }
    controller.compilation_done.stop = Compilation::Stop;

    if rustc_driver::run_compiler(&args, Box::new(controller), None, None)
        .0
        .is_err()
    {
        exit(101);
    }
}
<|endoftext|>


<commit_before>use model::prelude::*;
use parking_lot::RwLock;
use serde_json::Value;
use std::{
    collections::HashMap,
    sync::Arc
};
use super::context::Context;
use ::client::bridge::gateway::event::*;

/// The core trait for handling events by serenity.
pub trait EventHandler {
    /// Dispatched when the cache gets full.
    ///
    /// Provides the cached guilds' ids.
    #[cfg(feature = "cache")]
    fn cached(&self, _: Context, _: Vec<GuildId>) {}

    /// Dispatched when a channel is created.
    ///
    /// Provides said channel's data.
    fn channel_create(&self, _: Context, _: Arc<RwLock<GuildChannel>>) {}

    /// Dispatched when a category is created.
    ///
    /// Provides said category's data.
    fn category_create(&self, _: Context, _: Arc<RwLock<ChannelCategory>>) {}

    /// Dispatched when a category is deleted.
    ///
    /// Provides said category's data.
    fn category_delete(&self, _: Context, _: Arc<RwLock<ChannelCategory>>) {}

    /// Dispatched when a private channel is created.
    ///
    /// Provides said channel's data.
    fn private_channel_create(&self, _: Context, _: Arc<RwLock<PrivateChannel>>) {}

    /// Dispatched when a channel is deleted.
    ///
    /// Provides said channel's data.
    fn channel_delete(&self, _: Context, _: Arc<RwLock<GuildChannel>>) {}

    /// Dispatched when a pin is added, deleted.
    ///
    /// Provides said pin's data.
    fn channel_pins_update(&self, _: Context, _: ChannelPinsUpdateEvent) {}

    /// Dispatched when a user is added to a `Group`.
    ///
    /// Provides the group's id and the user's data.
    fn channel_recipient_addition(&self, _: Context, _: ChannelId, _: User) {}

    /// Dispatched when a user is removed to a `Group`.
    ///
    /// Provides the group's id and the user's data.
    fn channel_recipient_removal(&self, _: Context, _: ChannelId, _: User) {}

    /// Dispatched when a channel is updated.
    ///
    /// Provides the old channel data, and the new data.
    #[cfg(feature = "cache")]
    fn channel_update(&self, _: Context, _: Option<Channel>, _: Channel) {}

    /// Dispatched when a channel is updated.
    ///
    /// Provides the new data.
    #[cfg(not(feature = "cache"))]
    fn channel_update(&self, _: Context, _: Channel) {}

    /// Dispatched when a user is banned from a guild.
    ///
    /// Provides the guild's id and the banned user's data.
    fn guild_ban_addition(&self, _: Context, _: GuildId, _: User) {}

    /// Dispatched when a user's ban is lifted from a guild.
    ///
    /// /// Provides the guild's id and the lifted user's data.
    fn guild_ban_removal(&self, _: Context, _: GuildId, _: User) {}

    /// Dispatched when a guild is created;
    /// or an existing guild's data is sent to us.
    ///
    /// Provides the guild's data and whether the guild is new.
    #[cfg(feature = "cache")]
    fn guild_create(&self, _: Context, _: Guild, _: bool) {}

    /// Dispatched when a guild is created;
    /// or an existing guild's data is sent to us.
    ///
    /// Provides the guild's data.
    #[cfg(not(feature = "cache"))]
    fn guild_create(&self, _: Context, _: Guild) {}

    /// Dispatched when a guild is deleted.
    ///
    /// Provides the partial data of the guild sent by discord,
    /// and the full data from the cache, if available.
    #[cfg(feature = "cache")]
    fn guild_delete(&self, _: Context, _: PartialGuild, _: Option<Arc<RwLock<Guild>>>) {}

    /// Dispatched when a guild is deleted.
    ///
    /// Provides the partial data of the guild sent by discord.
    #[cfg(not(feature = "cache"))]
    fn guild_delete(&self, _: Context, _: PartialGuild) {}

    /* the emojis were updated. */

    /// Dispatched when the emojis are updated.
    ///
    /// Provides the guild's id and the new state of the emojis in the guild.
    fn guild_emojis_update(&self, _: Context, _: GuildId, _: HashMap<EmojiId, Emoji>) {}

    /// Dispatched when a guild's integration is added, updated or removed.
    ///
    /// Provides the guild's id.
    fn guild_integrations_update(&self, _: Context, _: GuildId) {}

    /// Dispatched when a user joins a guild.
    ///
    /// Provides the guild's id and the user's member data.
    fn guild_member_addition(&self, _: Context, _: GuildId, _: Member) {}

    /// Dispatched when a user is removed (kicked).
    ///
    /// Provides the guild's id, the user's data, and the user's member data if available.
    #[cfg(feature = "cache")]
    fn guild_member_removal(&self, _: Context, _: GuildId, _: User, _: Option<Member>) {}

    /// Dispatched when a user is removed (kicked).
    ///
    /// Provides the guild's id, the user's data.
    #[cfg(not(feature = "cache"))]
    fn guild_member_removal(&self, _: Context, _: GuildId, _: User) {}

    /// Dispatched when a member is updated (e.g their nickname is updated)
    ///
    /// Provides the member's old data (if available) and the new data.
    #[cfg(feature = "cache")]
    fn guild_member_update(&self, _: Context, _: Option<Member>, _: Member) {}

    /// Dispatched when a member is updated (e.g their nickname is updated)
    ///
    /// Provides the new data.
    #[cfg(not(feature = "cache"))]
    fn guild_member_update(&self, _: Context, _: GuildMemberUpdateEvent) {}

    /// Dispatched when the data for offline members was requested.
    ///
    /// Provides the guild's id and the data.
    fn guild_members_chunk(&self, _: Context, _: GuildId, _: HashMap<UserId, Member>) {}

    /// Dispatched when a role is created.
    ///
    /// Provides the guild's id and the new role's data.
    fn guild_role_create(&self, _: Context, _: GuildId, _: Role) {}

    /// Dispatched when a role is deleted.
    ///
    /// Provides the guild's id, the role's id and its data if available.
    #[cfg(feature = "cache")]
    fn guild_role_delete(&self, _: Context, _: GuildId, _: RoleId, _: Option<Role>) {}

    /// Dispatched when a role is deleted.
    ///
    /// Provides the guild's id, the role's id.
    #[cfg(not(feature = "cache"))]
    fn guild_role_delete(&self, _: Context, _: GuildId, _: RoleId) {}

    /// Dispatched when a role is updated.
    ///
    /// Provides the guild's id, the role's old (if available) and new data.
    #[cfg(feature = "cache")]
    fn guild_role_update(&self, _: Context, _: GuildId, _: Option<Role>, _: Role) {}

    /// Dispatched when a role is updated.
    ///
    /// Provides the guild's id and the role's new data.
    #[cfg(not(feature = "cache"))]
    fn guild_role_update(&self, _: Context, _: GuildId, _: Role) {}

    /// Dispatched when a guild became unavailable.
    ///
    /// Provides the guild's id.
    fn guild_unavailable(&self, _: Context, _: GuildId) {}

    /// Dispatched when the guild is updated.
    ///
    /// Provides the guild's old full data (if available) and the new, albeit partial data.
    #[cfg(feature = "cache")]
    fn guild_update(&self, _: Context, _: Option<Arc<RwLock<Guild>>>, _: PartialGuild) {}

    /// Dispatched when the guild is updated.
    ///
    /// Provides the guild's new, albeit partial data.
    #[cfg(not(feature = "cache"))]
    fn guild_update(&self, _: Context, _: PartialGuild) {}

    /// Dispatched when a message is created.
    ///
    /// Provides the message's data.
    fn message(&self, _: Context, _: Message) {}

    /// Dispatched when a message is deleted.
    ///
    /// Provides the channel's id and the message's id.
    fn message_delete(&self, _: Context, _: ChannelId, _: MessageId) {}

    /// Dispatched when multiple messages were deleted at once.
    ///
    /// Provides the channel's id and the deleted messages' ids.
    fn message_delete_bulk(&self, _: Context, _: ChannelId, _: Vec<MessageId>) {}

    /// Dispatched when a new reaction is attached to a message.
    ///
    /// Provides the reaction's data.
    fn reaction_add(&self, _: Context, _: Reaction) {}

    /// Dispatched when a reaction is dettached from a message.
    ///
    /// Provides the reaction's data.
    fn reaction_remove(&self, _: Context, _: Reaction) {}

    /// Dispatched when all reactions of a message are dettached from a message.
    ///
    /// Provides the channel's id and the message's id.
    fn reaction_remove_all(&self, _: Context, _: ChannelId, _: MessageId) {}

    /// Dispatched when a message is updated.
    ///
    /// Provides the new data of the message.
    fn message_update(&self, _: Context, _: MessageUpdateEvent) {}

    fn presence_replace(&self, _: Context, _: Vec<Presence>) {}

    /// Dispatched when a user's presence is updated (i.e off -> on).
    ///
    /// Provides the presence's new data.
    fn presence_update(&self, _: Context, _: PresenceUpdateEvent) {}

    /// Dispatched upon startup.
    ///
    /// Provides data about the bot and the guilds it's in.
    fn ready(&self, _: Context, _: Ready) {}

    /// Dispatched upon reconnection.
    fn resume(&self, _: Context, _: ResumedEvent) {}

    /// Dispatched when a shard's connection stage is updated
    ///
    /// Provides the context of the shard and the event information about the update.
    fn shard_stage_update(&self, _: Context, _: ShardStageUpdateEvent) {}

    /// Dispatched when a user starts typing.
    fn typing_start(&self, _: Context, _: TypingStartEvent) {}

    /// Dispatched when an unknown event was sent from discord.
    ///
    /// Provides the event's name and its unparsed data.
    fn unknown(&self, _: Context, _: String, _: Value) {}

    /// Dispatched when the bot's data is updated.
    ///
    /// Provides the old and new data.
    #[cfg(feature = "cache")]
    fn user_update(&self, _: Context, _: CurrentUser, _: CurrentUser) {}

    /// Dispatched when the bot's data is updated.
    ///
    /// Provides the new data.
    #[cfg(not(feature = "cache"))]
    fn user_update(&self, _: Context, _: CurrentUser) {}

    /// Dispatched when a guild's voice server was updated (or changed to another one).
    ///
    /// Provides the voice server's data.
    fn voice_server_update(&self, _: Context, _: VoiceServerUpdateEvent) {}

    /// Dispatched when a user joins, leaves or moves a voice channel.
    ///
    /// Provides the guild's id (if available) and
    /// the new state of the guild's voice channels.
    fn voice_state_update(&self, _: Context, _: Option<GuildId>, _: VoiceState) {}

    /// Dispatched when a guild's webhook is updated.
    ///
    /// Provides the guild's id and the channel's id the webhook belongs in.
    fn webhook_update(&self, _: Context, _: GuildId, _: ChannelId) {}
}
<commit_msg>Replace (most) placeholders with actual names<commit_after>use model::prelude::*;
use parking_lot::RwLock;
use serde_json::Value;
use std::{
    collections::HashMap,
    sync::Arc
};
use super::context::Context;
use ::client::bridge::gateway::event::*;

/// The core trait for handling events by serenity.
pub trait EventHandler {
    /// Dispatched when the cache gets full.
    ///
    /// Provides the cached guilds' ids.
    #[cfg(feature = "cache")]
    fn cached(&self, _ctx: Context, _guilds: Vec<GuildId>) {}

    /// Dispatched when a channel is created.
    ///
    /// Provides said channel's data.
    fn channel_create(&self, _ctx: Context, _channel: Arc<RwLock<GuildChannel>>) {}

    /// Dispatched when a category is created.
    ///
    /// Provides said category's data.
    fn category_create(&self, _ctx: Context, _category: Arc<RwLock<ChannelCategory>>) {}

    /// Dispatched when a category is deleted.
    ///
    /// Provides said category's data.
    fn category_delete(&self, _ctx: Context, _category: Arc<RwLock<ChannelCategory>>) {}

    /// Dispatched when a private channel is created.
    ///
    /// Provides said channel's data.
    fn private_channel_create(&self, _ctx: Context, _channel: Arc<RwLock<PrivateChannel>>) {}

    /// Dispatched when a channel is deleted.
    ///
    /// Provides said channel's data.
    fn channel_delete(&self, _ctx: Context, _channel: Arc<RwLock<GuildChannel>>) {}

    /// Dispatched when a pin is added, deleted.
    ///
    /// Provides said pin's data.
    fn channel_pins_update(&self, _ctx: Context, _pin: ChannelPinsUpdateEvent) {}

    /// Dispatched when a user is added to a `Group`.
    ///
    /// Provides the group's id and the user's data.
    fn channel_recipient_addition(&self, _ctx: Context, _group_id: ChannelId, _user: User) {}

    /// Dispatched when a user is removed to a `Group`.
    ///
    /// Provides the group's id and the user's data.
    fn channel_recipient_removal(&self, _ctx: Context, _group_id: ChannelId, _user: User) {}

    /// Dispatched when a channel is updated.
    ///
    /// Provides the old channel data, and the new data.
    #[cfg(feature = "cache")]
    fn channel_update(&self, _ctx: Context, _old: Option<Channel>, _new: Channel) {}

    /// Dispatched when a channel is updated.
    ///
    /// Provides the new data.
    #[cfg(not(feature = "cache"))]
    fn channel_update(&self, _ctx: Context, _new_data: Channel) {}

    /// Dispatched when a user is banned from a guild.
    ///
    /// Provides the guild's id and the banned user's data.
    fn guild_ban_addition(&self, _ctx: Context, _guild_id: GuildId, _banned_user: User) {}

    /// Dispatched when a user's ban is lifted from a guild.
    ///
    /// Provides the guild's id and the lifted user's data.
    fn guild_ban_removal(&self, _ctx: Context, _guild_id: GuildId, _unbanned_user: User) {}

    /// Dispatched when a guild is created;
    /// or an existing guild's data is sent to us.
    ///
    /// Provides the guild's data and whether the guild is new.
    #[cfg(feature = "cache")]
    fn guild_create(&self, _ctx: Context, _guild: Guild, _is_new: bool) {}

    /// Dispatched when a guild is created;
    /// or an existing guild's data is sent to us.
    ///
    /// Provides the guild's data.
    #[cfg(not(feature = "cache"))]
    fn guild_create(&self, _ctx: Context, _guild: Guild) {}

    /// Dispatched when a guild is deleted.
    ///
    /// Provides the partial data of the guild sent by discord,
    /// and the full data from the cache, if available.
    #[cfg(feature = "cache")]
    fn guild_delete(&self, _ctx: Context, _incomplete: PartialGuild, _full: Option<Arc<RwLock<Guild>>>) {}

    /// Dispatched when a guild is deleted.
    ///
    /// Provides the partial data of the guild sent by discord.
    #[cfg(not(feature = "cache"))]
    fn guild_delete(&self, _ctx: Context, _incomplete: PartialGuild) {}

    /* the emojis were updated. */

    /// Dispatched when the emojis are updated.
    ///
    /// Provides the guild's id and the new state of the emojis in the guild.
    fn guild_emojis_update(&self, _ctx: Context, _guild_id: GuildId, _current_state: HashMap<EmojiId, Emoji>) {}

    /// Dispatched when a guild's integration is added, updated or removed.
    ///
    /// Provides the guild's id.
    fn guild_integrations_update(&self, _ctx: Context, _guild_id: GuildId) {}

    /// Dispatched when a user joins a guild.
    ///
    /// Provides the guild's id and the user's member data.
    fn guild_member_addition(&self, _ctx: Context, _guild_id: GuildId, _new_member: Member) {}

    /// Dispatched when a user is removed (kicked).
    ///
    /// Provides the guild's id, the user's data, and the user's member data if available.
    #[cfg(feature = "cache")]
    fn guild_member_removal(&self, _ctx: Context, _guild: GuildId, _user: User, _member_data_if_available: Option<Member>) {}

    /// Dispatched when a user is removed (kicked).
    ///
    /// Provides the guild's id, the user's data.
    #[cfg(not(feature = "cache"))]
    fn guild_member_removal(&self, _ctx: Context, _guild_id: GuildId, _kicked: User) {}

    /// Dispatched when a member is updated (e.g their nickname is updated)
    ///
    /// Provides the member's old data (if available) and the new data.
    #[cfg(feature = "cache")]
    fn guild_member_update(&self, _ctx: Context, _old_if_available: Option<Member>, _new: Member) {}

    /// Dispatched when a member is updated (e.g their nickname is updated)
    ///
    /// Provides the new data.
    #[cfg(not(feature = "cache"))]
    fn guild_member_update(&self, _ctx: Context, _new: GuildMemberUpdateEvent) {}

    /// Dispatched when the data for offline members was requested.
    ///
    /// Provides the guild's id and the data.
    fn guild_members_chunk(&self, _ctx: Context, _guild_id: GuildId, _offline_members: HashMap<UserId, Member>) {}

    /// Dispatched when a role is created.
    ///
    /// Provides the guild's id and the new role's data.
    fn guild_role_create(&self, _ctx: Context, _guild_id: GuildId, _new: Role) {}

    /// Dispatched when a role is deleted.
    ///
    /// Provides the guild's id, the role's id and its data if available.
    #[cfg(feature = "cache")]
    fn guild_role_delete(&self, _ctx: Context, _guild_id: GuildId, _removed_role_id: RoleId, _removed_role_data_if_available: Option<Role>) {}

    /// Dispatched when a role is deleted.
    ///
    /// Provides the guild's id, the role's id.
    #[cfg(not(feature = "cache"))]
    fn guild_role_delete(&self, _ctx: Context, _guild_id: GuildId, _removed_role_id: RoleId) {}

    /// Dispatched when a role is updated.
    ///
    /// Provides the guild's id, the role's old (if available) and new data.
    #[cfg(feature = "cache")]
    fn guild_role_update(&self, _ctx: Context, _guild_id: GuildId, _old_data_if_available: Option<Role>, _new: Role) {}

    /// Dispatched when a role is updated.
    ///
    /// Provides the guild's id and the role's new data.
    #[cfg(not(feature = "cache"))]
    fn guild_role_update(&self, _ctx: Context, _guild_id: GuildId, _new_data: Role) {}

    /// Dispatched when a guild became unavailable.
    ///
    /// Provides the guild's id.
    fn guild_unavailable(&self, _ctx: Context, _guild_id: GuildId) {}

    /// Dispatched when the guild is updated.
    ///
    /// Provides the guild's old full data (if available) and the new, albeit partial data.
    #[cfg(feature = "cache")]
    fn guild_update(&self, _ctx: Context, _old_data_if_available: Option<Arc<RwLock<Guild>>>, _new_but_incomplete: PartialGuild) {}

    /// Dispatched when the guild is updated.
    ///
    /// Provides the guild's new, albeit partial data.
    #[cfg(not(feature = "cache"))]
    fn guild_update(&self, _ctx: Context, _new_but_incomplete_data: PartialGuild) {}

    /// Dispatched when a message is created.
    ///
    /// Provides the message's data.
    fn message(&self, _ctx: Context, _new_message: Message) {}

    /// Dispatched when a message is deleted.
    ///
    /// Provides the channel's id and the message's id.
    fn message_delete(&self, _ctx: Context, _channel_id: ChannelId, _deleted_message_id: MessageId) {}

    /// Dispatched when multiple messages were deleted at once.
    ///
    /// Provides the channel's id and the deleted messages' ids.
    fn message_delete_bulk(&self, _ctx: Context, _channel_id: ChannelId, _multiple_deleted_messages_ids: Vec<MessageId>) {}

    /// Dispatched when a new reaction is attached to a message.
    ///
    /// Provides the reaction's data.
    fn reaction_add(&self, _ctx: Context, _add_reaction: Reaction) {}

    /// Dispatched when a reaction is dettached from a message.
    ///
    /// Provides the reaction's data.
    fn reaction_remove(&self, _ctx: Context, _removed_reaction: Reaction) {}

    /// Dispatched when all reactions of a message are dettached from a message.
    ///
    /// Provides the channel's id and the message's id.
    fn reaction_remove_all(&self, _ctx: Context, _channel_id: ChannelId, _removed_from_message_id: MessageId) {}

    /// Dispatched when a message is updated.
    ///
    /// Provides the new data of the message.
    fn message_update(&self, _ctx: Context, _new_data: MessageUpdateEvent) {}

    fn presence_replace(&self, _ctx: Context, _: Vec<Presence>) {}

    /// Dispatched when a user's presence is updated (e.g off -> on).
    ///
    /// Provides the presence's new data.
    fn presence_update(&self, _ctx: Context, _new_data: PresenceUpdateEvent) {}

    /// Dispatched upon startup.
    ///
    /// Provides data about the bot and the guilds it's in.
    fn ready(&self, _ctx: Context, _data_about_bot: Ready) {}

    /// Dispatched upon reconnection.
    fn resume(&self, _ctx: Context, _: ResumedEvent) {}

    /// Dispatched when a shard's connection stage is updated
    ///
    /// Provides the context of the shard and the event information about the update.
    fn shard_stage_update(&self, _ctx: Context, _: ShardStageUpdateEvent) {}

    /// Dispatched when a user starts typing.
    fn typing_start(&self, _ctx: Context, _: TypingStartEvent) {}

    /// Dispatched when an unknown event was sent from discord.
    ///
    /// Provides the event's name and its unparsed data.
    fn unknown(&self, _ctx: Context, _name: String, _raw: Value) {}

    /// Dispatched when the bot's data is updated.
    ///
    /// Provides the old and new data.
    #[cfg(feature = "cache")]
    fn user_update(&self, _ctx: Context, _old_data: CurrentUser, _new: CurrentUser) {}

    /// Dispatched when the bot's data is updated.
    ///
    /// Provides the new data.
    #[cfg(not(feature = "cache"))]
    fn user_update(&self, _ctx: Context, _new_data: CurrentUser) {}

    /// Dispatched when a guild's voice server was updated (or changed to another one).
    ///
    /// Provides the voice server's data.
    fn voice_server_update(&self, _ctx: Context, _: VoiceServerUpdateEvent) {}

    /// Dispatched when a user joins, leaves or moves a voice channel.
    ///
    /// Provides the guild's id (if available) and
    /// the new state of the guild's voice channels.
    fn voice_state_update(&self, _ctx: Context, _: Option<GuildId>, _: VoiceState) {}

    /// Dispatched when a guild's webhook is updated.
    ///
    /// Provides the guild's id and the channel's id the webhook belongs in.
    fn webhook_update(&self, _ctx: Context, _guild_id: GuildId, _belongs_to_channel_id: ChannelId) {}
}
<|endoftext|>


<commit_before>use convert;

use syntax::ast::{Expr, Ident, TokenTree, Stmt};
use syntax::codemap::Span;
use syntax::ext::base::{ExtCtxt, MacResult, MacEager};
use syntax::ext::quote::rt::ToTokens;
use syntax::parse::token;
use syntax::ptr::P;
use syntax::print::pprust;

// Copy from libsyntax
//
// A variant of 'try!' that panics on Err(FatalError). This is used as a
// crutch on the way towards a non-panic!-prone parser. It should be used
// for fatal parsing errors; eventually we plan to convert all code using
// panictry to just use normal try
macro_rules! panictry {
    ($e:expr) => ({
        use std::result::Result::{Ok, Err};
        use syntax::diagnostic::FatalError;
        match $e {
            Ok(e) => e,
            Err(FatalError) => panic!(FatalError)
        }
    })
}

fn filter_tts_by_span(span: Span, tts: &[TokenTree]) -> Vec<TokenTree> {
    tts.iter()
       .filter(|tt| span.lo <= tt.get_span().lo && tt.get_span().hi <= span.hi)
       .cloned()
       .collect()
}

fn expr_prelude(cx: &ExtCtxt) -> Vec<P<Stmt>> {
    let use_stmt = quote_stmt!(cx, use std::io::Write;);
    let fn_stmt = quote_stmt!(
        cx,
        fn inspect<T: Write>(writer: &mut T, mut vals: Vec<(i32, String)>, offset: i32, repr: &str) {
            fn width(s: &str) -> i32 { s.len() as i32 }
            fn align<T: Write>(writer: &mut T, cur: &mut i32, col: i32, s: &str) {
                while *cur < col {
                    let _ = write!(writer, " ");
                    *cur += 1;
                }
                let _ = write!(writer, "{}", s);
                *cur += width(s);
            }

            vals.sort();

            let _ = writeln!(writer, "{}", repr);
            {
                let mut cur = 0;
                for &(c, _) in &vals {
                    align(writer, &mut cur, offset + c, "|");
                }
                let _ = writeln!(writer, "");
            }
            while !vals.is_empty() {
                let mut cur = 0;
                let mut i = 0;
                while i < vals.len() {
                    if i == vals.len() - 1 ||
                        vals[i].0 + width(&vals[i].1) < vals[i + 1].0 {
                            align(writer, &mut cur, offset + vals[i].0, &vals[i].1);
                            let _ = vals.remove(i);
                        } else {
                            align(writer, &mut cur, offset + vals[i].0, "|");
                            i += 1;
                        }
                }
                let _ = writeln!(writer, "");
            }
        });
    let write_stmt = quote_stmt!(cx, let mut write_buf = vec![];);

    vec![use_stmt.unwrap(), fn_stmt.unwrap(), write_stmt.unwrap()]
}

struct ExprGen {
    ident: Ident,
    tts: Vec<TokenTree>,
    expr: P<Expr>,
    ppstr: String,
}

impl ExprGen {
    fn new(ident: &str, expr: P<Expr>, tts: &[TokenTree]) -> ExprGen {
        let tts = filter_tts_by_span(expr.span, tts);
        let ppstr = pprust::expr_to_string(&expr);

        ExprGen {
            ident: token::gensym_ident(ident),
            tts: tts,
            expr: expr,
            ppstr: ppstr,
        }
    }

    fn ppstr(&self) -> &str {
        &self.ppstr
    }

    fn init_stmt(&self, cx: &mut ExtCtxt, pushed: bool) -> P<Stmt> {
        let ident = self.ident;
        if pushed {
            quote_stmt!(cx, let mut $ident = vec![];).unwrap()
        } else {
            quote_stmt!(cx, let $ident = vec![];).unwrap()
        }
    }

    fn converted_expr(&self, cx: &mut ExtCtxt) -> (P<Expr>, bool) {
        convert::convert_expr(cx, self.expr.clone(), self.ident, &self.tts)
    }

    fn inspect_expr(&self, cx: &mut ExtCtxt, left: &str, right: &str) -> P<Expr> {
        let offset = left.len() as i32;
        let repr = format!("{}{}{}", left, self.ppstr, right);
        let ident = self.ident;
        quote_expr!(cx, inspect(&mut write_buf, $ident, $offset, $repr))
    }
}

pub fn expand_assert(cx: &mut ExtCtxt, sp: Span, args: &[TokenTree]) -> Box<MacResult + 'static> {
    let mut parser = cx.new_parser_from_tts(args);
    let cond_expr = parser.parse_expr();

    let msg_tts = if panictry!(parser.eat(&token::Token::Comma)) {
        let mut span = sp.clone();
        span.lo = parser.span.lo;
        Some(filter_tts_by_span(span, args))
    } else {
        None
    };

    let prelude = expr_prelude(cx);

    let gen = ExprGen::new("vals", cond_expr, args);
    let inspect = gen.inspect_expr(cx, "power_assert!(", ")");
    let (converted, pushed) = gen.converted_expr(cx);
    let init = gen.init_stmt(cx, pushed);

    let panic_msg = if let Some(tts) = msg_tts {
        quote_expr!(cx, format!($tts))
    } else {
        let msg = format!("assertion failed: {}", gen.ppstr());
        quote_expr!(cx, $msg)
    };

    let expr = quote_expr!(cx, {
        $init;
        let cond = $converted;
        if !cond {
            $prelude;
            let _ = writeln!(&mut write_buf, $panic_msg);
            $inspect;
            panic!(String::from_utf8(write_buf).unwrap())
        }
    });

    // println!("{:?}", expr);

    MacEager::expr(expr)
}

pub fn expand_assert_eq(cx: &mut ExtCtxt,
                        _sp: Span,
                        args: &[TokenTree])
                        -> Box<MacResult + 'static> {
    let mut parser = cx.new_parser_from_tts(args);
    let lhs = parser.parse_expr();
    panictry!(parser.expect(&token::Token::Comma));
    let rhs = parser.parse_expr();

    let lhs_gen = ExprGen::new("lhs_vals", lhs, args);
    let rhs_gen = ExprGen::new("rhs_vals", rhs, args);

    let prelude = expr_prelude(cx);

    let lhs_inspect = lhs_gen.inspect_expr(cx, "left: ", "");
    let rhs_inspect = rhs_gen.inspect_expr(cx, "right: ", "");
    let (lhs_converted, lhs_pushed) = lhs_gen.converted_expr(cx);
    let (rhs_converted, rhs_pushed) = rhs_gen.converted_expr(cx);
    let lhs_init = lhs_gen.init_stmt(cx, lhs_pushed);
    let rhs_init = rhs_gen.init_stmt(cx, rhs_pushed);
    let assert_msg = format!("power_assert_eq!({}, {})", lhs_gen.ppstr(), rhs_gen.ppstr());

    let expr = quote_expr!(cx, {
        $lhs_init;
        $rhs_init;
        match (&$lhs_converted, &$rhs_converted) {
            (left_val, right_val) => {
                if !(*left_val == *right_val) {
                    $prelude;
                    let _ = writeln!(&mut write_buf, "assertion failed: `(left == right)` \
                            (left: `{:?}`, right: `{:?}`)", *left_val, *right_val);
                    let _ = writeln!(&mut write_buf, $assert_msg);
                    $lhs_inspect;
                    $rhs_inspect;
                    panic!(String::from_utf8(write_buf).unwrap());
                }
            }
        }
    });

    // println!("{:?}", expr);

    MacEager::expr(expr)
}
<commit_msg>update to rustc 1.6.0-nightly (1e3e7e73c 2015-11-07)<commit_after>use convert;

use syntax::ast::{Expr, Ident, TokenTree, Stmt};
use syntax::codemap::Span;
use syntax::ext::base::{ExtCtxt, MacResult, MacEager};
use syntax::ext::quote::rt::ToTokens;
use syntax::parse::token;
use syntax::ptr::P;
use syntax::print::pprust;

// Copy from libsyntax
//
// A variant of 'try!' that panics on Err(FatalError). This is used as a
// crutch on the way towards a non-panic!-prone parser. It should be used
// for fatal parsing errors; eventually we plan to convert all code using
// panictry to just use normal try
macro_rules! panictry {
    ($e:expr) => ({
        use std::result::Result::{Ok, Err};
        use syntax::diagnostic::FatalError;
        match $e {
            Ok(e) => e,
            Err(FatalError) => panic!(FatalError)
        }
    })
}

fn filter_tts_by_span(span: Span, tts: &[TokenTree]) -> Vec<TokenTree> {
    tts.iter()
       .filter(|tt| span.lo <= tt.get_span().lo && tt.get_span().hi <= span.hi)
       .cloned()
       .collect()
}

fn expr_prelude(cx: &ExtCtxt) -> Vec<P<Stmt>> {
    let use_stmt = quote_stmt!(cx, use std::io::Write;);
    let fn_stmt = quote_stmt!(
        cx,
        fn inspect<T: Write>(writer: &mut T, mut vals: Vec<(i32, String)>, offset: i32, repr: &str) {
            fn width(s: &str) -> i32 { s.len() as i32 }
            fn align<T: Write>(writer: &mut T, cur: &mut i32, col: i32, s: &str) {
                while *cur < col {
                    let _ = write!(writer, " ");
                    *cur += 1;
                }
                let _ = write!(writer, "{}", s);
                *cur += width(s);
            }

            vals.sort();

            let _ = writeln!(writer, "{}", repr);
            {
                let mut cur = 0;
                for &(c, _) in &vals {
                    align(writer, &mut cur, offset + c, "|");
                }
                let _ = writeln!(writer, "");
            }
            while !vals.is_empty() {
                let mut cur = 0;
                let mut i = 0;
                while i < vals.len() {
                    if i == vals.len() - 1 ||
                        vals[i].0 + width(&vals[i].1) < vals[i + 1].0 {
                            align(writer, &mut cur, offset + vals[i].0, &vals[i].1);
                            let _ = vals.remove(i);
                        } else {
                            align(writer, &mut cur, offset + vals[i].0, "|");
                            i += 1;
                        }
                }
                let _ = writeln!(writer, "");
            }
        });
    let write_stmt = quote_stmt!(cx, let mut write_buf = vec![];);

    vec![use_stmt.unwrap(), fn_stmt.unwrap(), write_stmt.unwrap()]
}

struct ExprGen {
    ident: Ident,
    tts: Vec<TokenTree>,
    expr: P<Expr>,
    ppstr: String,
}

impl ExprGen {
    fn new(ident: &str, expr: P<Expr>, tts: &[TokenTree]) -> ExprGen {
        let tts = filter_tts_by_span(expr.span, tts);
        let ppstr = pprust::expr_to_string(&expr);

        ExprGen {
            ident: token::gensym_ident(ident),
            tts: tts,
            expr: expr,
            ppstr: ppstr,
        }
    }

    fn ppstr(&self) -> &str {
        &self.ppstr
    }

    fn init_stmt(&self, cx: &mut ExtCtxt, pushed: bool) -> P<Stmt> {
        let ident = self.ident;
        if pushed {
            quote_stmt!(cx, let mut $ident = vec![];).unwrap()
        } else {
            quote_stmt!(cx, let $ident = vec![];).unwrap()
        }
    }

    fn converted_expr(&self, cx: &mut ExtCtxt) -> (P<Expr>, bool) {
        convert::convert_expr(cx, self.expr.clone(), self.ident, &self.tts)
    }

    fn inspect_expr(&self, cx: &mut ExtCtxt, left: &str, right: &str) -> P<Expr> {
        let offset = left.len() as i32;
        let repr = format!("{}{}{}", left, self.ppstr, right);
        let ident = self.ident;
        quote_expr!(cx, inspect(&mut write_buf, $ident, $offset, $repr))
    }
}

pub fn expand_assert(cx: &mut ExtCtxt, sp: Span, args: &[TokenTree]) -> Box<MacResult + 'static> {
    let mut parser = cx.new_parser_from_tts(args);
    let cond_expr = parser.parse_expr_panic();

    let msg_tts = if panictry!(parser.eat(&token::Token::Comma)) {
        let mut span = sp.clone();
        span.lo = parser.span.lo;
        Some(filter_tts_by_span(span, args))
    } else {
        None
    };

    let prelude = expr_prelude(cx);

    let gen = ExprGen::new("vals", cond_expr, args);
    let inspect = gen.inspect_expr(cx, "power_assert!(", ")");
    let (converted, pushed) = gen.converted_expr(cx);
    let init = gen.init_stmt(cx, pushed);

    let panic_msg = if let Some(tts) = msg_tts {
        quote_expr!(cx, format!($tts))
    } else {
        let msg = format!("assertion failed: {}", gen.ppstr());
        quote_expr!(cx, $msg)
    };

    let expr = quote_expr!(cx, {
        $init;
        let cond = $converted;
        if !cond {
            $prelude;
            let _ = writeln!(&mut write_buf, $panic_msg);
            $inspect;
            panic!(String::from_utf8(write_buf).unwrap())
        }
    });

    // println!("{:?}", expr);

    MacEager::expr(expr)
}

pub fn expand_assert_eq(cx: &mut ExtCtxt,
                        _sp: Span,
                        args: &[TokenTree])
                        -> Box<MacResult + 'static> {
    let mut parser = cx.new_parser_from_tts(args);
    let lhs = parser.parse_expr_panic();
    panictry!(parser.expect(&token::Token::Comma));
    let rhs = parser.parse_expr_panic();

    let lhs_gen = ExprGen::new("lhs_vals", lhs, args);
    let rhs_gen = ExprGen::new("rhs_vals", rhs, args);

    let prelude = expr_prelude(cx);

    let lhs_inspect = lhs_gen.inspect_expr(cx, "left: ", "");
    let rhs_inspect = rhs_gen.inspect_expr(cx, "right: ", "");
    let (lhs_converted, lhs_pushed) = lhs_gen.converted_expr(cx);
    let (rhs_converted, rhs_pushed) = rhs_gen.converted_expr(cx);
    let lhs_init = lhs_gen.init_stmt(cx, lhs_pushed);
    let rhs_init = rhs_gen.init_stmt(cx, rhs_pushed);
    let assert_msg = format!("power_assert_eq!({}, {})", lhs_gen.ppstr(), rhs_gen.ppstr());

    let expr = quote_expr!(cx, {
        $lhs_init;
        $rhs_init;
        match (&$lhs_converted, &$rhs_converted) {
            (left_val, right_val) => {
                if !(*left_val == *right_val) {
                    $prelude;
                    let _ = writeln!(&mut write_buf, "assertion failed: `(left == right)` \
                            (left: `{:?}`, right: `{:?}`)", *left_val, *right_val);
                    let _ = writeln!(&mut write_buf, $assert_msg);
                    $lhs_inspect;
                    $rhs_inspect;
                    panic!(String::from_utf8(write_buf).unwrap());
                }
            }
        }
    });

    // println!("{:?}", expr);

    MacEager::expr(expr)
}
<|endoftext|>


<commit_before>// Copyright 2015 Copyright (c) 2015 Benoît Labaere (benoit.labaere@gmail.com)
//
// Licensed under the MIT license LICENSE or <http://opensource.org/licenses/MIT>
// This file may not be copied, modified, or distributed except according to those terms.

use std::io;
use std::thread;
use std::sync::mpsc;
use std::sync::mpsc::Receiver;
use std::time;

use mio;
use mio::Sender;

use global::*;
use event_loop_msg::*;

use Message;
use EventLoop;
use session::Session;

pub struct SessionFacade {
    cmd_sender: mio::Sender<EventLoopCmd>,
    evt_receiver: mpsc::Receiver<SessionEvt>
}

impl SessionFacade {
    pub fn new() -> io::Result<SessionFacade> {
        let mut config = mio::EventLoopConfig::new();

        config.
            io_poll_timeout_ms(250).
            notify_capacity(4_096).
            messages_per_tick(256).
            timer_tick_ms(15).
            timer_wheel_size(1_024).
            timer_capacity(4_096);

        let mut event_loop = try!(mio::EventLoop::configured(config));
        let (tx, rx) = mpsc::channel();
        let session = SessionFacade {
            cmd_sender: event_loop.channel(),
            evt_receiver: rx };

        thread::spawn(move || SessionFacade::run_event_loop(&mut event_loop, tx));

        Ok(session)
    }

    fn run_event_loop(event_loop: &mut EventLoop, evt_tx: mpsc::Sender<SessionEvt>) {
        let mut handler = Session::new(evt_tx);
        let exec = event_loop.run(&mut handler);

        match exec {
            Ok(_) => debug!("event loop exited"),
            Err(e) => error!("event loop failed to run: {}", e)
        }
    }

    fn send_cmd(&self, session_cmd: SessionCmd) -> Result<(), io::Error> {
        let cmd = EventLoopCmd::SessionLevel(session_cmd);

        self.cmd_sender.send(cmd).map_err(|e| convert_notify_err(e))
    }

    pub fn create_socket(&self, socket_type: SocketType) -> io::Result<SocketFacade> {
        let session_cmd = SessionCmd::CreateSocket(socket_type);

        try!(self.send_cmd(session_cmd));

        match self.evt_receiver.recv() {
            Ok(SessionEvt::SocketCreated(id, rx)) => Ok(self.new_socket(id, rx)),
            Err(_)                                => Err(other_io_error("evt channel closed"))
        }
    }

    fn new_socket(&self, id: SocketId, rx: mpsc::Receiver<SocketEvt>) -> SocketFacade {
        SocketFacade::new(id, self.cmd_sender.clone(), rx)
    }
}

impl Drop for SessionFacade {
    fn drop(&mut self) {
        let session_cmd = SessionCmd::Shutdown;
        let cmd = EventLoopCmd::SessionLevel(session_cmd);

        let _ = self.cmd_sender.send(cmd);
    }
}

pub struct SocketFacade {
    id: SocketId,
    cmd_sender: Sender<EventLoopCmd>,
    evt_receiver: Receiver<SocketEvt>
    // Could use https://github.com/polyfractal/bounded-spsc-queue ?
    // Maybe once a smart waiting strategy is available (like spin, then sleep 0, then sleep 1, then mutex ?)
}

impl SocketFacade {
    pub fn new(id: SocketId, cmd_tx: Sender<EventLoopCmd>, evt_rx: Receiver<SocketEvt>) -> SocketFacade {
        SocketFacade { id: id, cmd_sender: cmd_tx, evt_receiver: evt_rx }
    }

    fn send_cmd(&self, socket_cmd: SocketCmd) -> Result<(), io::Error> {
        let cmd = EventLoopCmd::SocketLevel(self.id, socket_cmd);

        self.cmd_sender.send(cmd).map_err(|e| convert_notify_err(e))
    }

    pub fn connect(&mut self, addr: &str) -> Result<(), io::Error> {
        let cmd = SocketCmd::Connect(addr.to_owned());

        try!(self.send_cmd(cmd));

        match self.evt_receiver.recv() {
            Ok(SocketEvt::Connected)       => Ok(()),
            Ok(SocketEvt::NotConnected(e)) => Err(e),
            Ok(_)                          => Err(other_io_error("unexpected evt")),
            Err(_)                         => Err(other_io_error("evt channel closed"))
        }
    }

    pub fn bind(&mut self, addr: &str) -> Result<(), io::Error> {
        let cmd = SocketCmd::Bind(addr.to_owned());

        try!(self.send_cmd(cmd));

        match self.evt_receiver.recv() {
            Ok(SocketEvt::Bound)       => Ok(()),
            Ok(SocketEvt::NotBound(e)) => Err(e),
            Ok(_)                      => Err(other_io_error("unexpected evt")),
            Err(_)                     => Err(other_io_error("evt channel closed"))
        }
    }

    pub fn send(&mut self, buffer: Vec<u8>) -> Result<(), io::Error> {
        self.send_msg(Message::with_body(buffer))
    }

    pub fn send_msg(&mut self, msg: Message) -> Result<(), io::Error> {
        let cmd = SocketCmd::SendMsg(msg);

        try!(self.send_cmd(cmd));

        match self.evt_receiver.recv() {
            Ok(SocketEvt::MsgSent)       => Ok(()),
            Ok(SocketEvt::MsgNotSent(e)) => Err(e),
            Ok(_)                        => Err(other_io_error("unexpected evt")),
            Err(_)                       => Err(other_io_error("evt channel closed"))
        }
    }

    pub fn recv(&mut self) -> Result<Vec<u8>, io::Error> {
        self.recv_msg().map(|msg| msg.to_buffer())
    }

    pub fn recv_msg(&mut self) -> Result<Message, io::Error> {
        try!(self.send_cmd(SocketCmd::RecvMsg));

        match self.evt_receiver.recv() {
            Ok(SocketEvt::MsgRecv(msg))  => Ok(msg),
            Ok(SocketEvt::MsgNotRecv(e)) => Err(e),
            Ok(_)                        => Err(other_io_error("unexpected evt")),
            Err(_)                       => Err(other_io_error("evt channel closed"))
        }
    }

    pub fn set_option(&mut self, option: SocketOption) -> io::Result<()> {
        let cmd = SocketCmd::SetOption(option);

        try!(self.send_cmd(cmd));

        match self.evt_receiver.recv() {
            Ok(SocketEvt::OptionSet)       => Ok(()),
            Ok(SocketEvt::OptionNotSet(e)) => Err(e),
            Ok(_)                          => Err(other_io_error("unexpected evt")),
            Err(_)                         => Err(other_io_error("evt channel closed"))
        }
    }

    pub fn set_send_timeout(&mut self, timeout: time::Duration) -> io::Result<()> {
        self.set_option(SocketOption::SendTimeout(timeout))
    }

    pub fn set_recv_timeout(&mut self, timeout: time::Duration) -> io::Result<()> {
        self.set_option(SocketOption::RecvTimeout(timeout))
    }
}

#[cfg(test)]
mod tests {
    use super::SessionFacade;
    use global::SocketType;
    use std::io;
    use std::io::Write;

    #[test]
    fn session_can_create_a_socket() {
        let session = SessionFacade::new().unwrap();
        let socket = session.create_socket(SocketType::Push).unwrap();

        drop(socket);
    }

    #[test]
    fn can_connect_socket() {
        let session = SessionFacade::new().unwrap();
        let mut socket = session.create_socket(SocketType::Push).unwrap();
        let connect_res = socket.connect("tcp://127.0.0.1:18080");

        match connect_res {
            Ok(()) => {},
            Err(e) => {
                //writeln!(&mut io::stderr(), "connect failed: {} !", e);
                panic!("connection failed {}", e);
            }
        }
    }

    #[test]
    fn can_try_connect_socket() {
        let session = SessionFacade::new().unwrap();
        let mut socket = session.create_socket(SocketType::Push).unwrap();

        assert!(socket.connect("tcp://this should not work").is_err());
    }
}<commit_msg>Adds some debugging log in tests to see what's going wrong during appveyor logs<commit_after>// Copyright 2015 Copyright (c) 2015 Benoît Labaere (benoit.labaere@gmail.com)
//
// Licensed under the MIT license LICENSE or <http://opensource.org/licenses/MIT>
// This file may not be copied, modified, or distributed except according to those terms.

use std::io;
use std::thread;
use std::sync::mpsc;
use std::sync::mpsc::Receiver;
use std::time;

use mio;
use mio::Sender;

use global::*;
use event_loop_msg::*;

use Message;
use EventLoop;
use session::Session;

pub struct SessionFacade {
    cmd_sender: mio::Sender<EventLoopCmd>,
    evt_receiver: mpsc::Receiver<SessionEvt>
}

impl SessionFacade {
    pub fn new() -> io::Result<SessionFacade> {
        let mut config = mio::EventLoopConfig::new();

        config.
            io_poll_timeout_ms(250).
            notify_capacity(4_096).
            messages_per_tick(256).
            timer_tick_ms(15).
            timer_wheel_size(1_024).
            timer_capacity(4_096);

        let mut event_loop = try!(mio::EventLoop::configured(config));
        let (tx, rx) = mpsc::channel();
        let session = SessionFacade {
            cmd_sender: event_loop.channel(),
            evt_receiver: rx };

        thread::spawn(move || SessionFacade::run_event_loop(&mut event_loop, tx));

        Ok(session)
    }

    fn run_event_loop(event_loop: &mut EventLoop, evt_tx: mpsc::Sender<SessionEvt>) {
        let mut handler = Session::new(evt_tx);
        let exec = event_loop.run(&mut handler);

        match exec {
            Ok(_) => debug!("event loop exited"),
            Err(e) => error!("event loop failed to run: {}", e)
        }
    }

    fn send_cmd(&self, session_cmd: SessionCmd) -> Result<(), io::Error> {
        let cmd = EventLoopCmd::SessionLevel(session_cmd);

        self.cmd_sender.send(cmd).map_err(|e| convert_notify_err(e))
    }

    pub fn create_socket(&self, socket_type: SocketType) -> io::Result<SocketFacade> {
        let session_cmd = SessionCmd::CreateSocket(socket_type);

        try!(self.send_cmd(session_cmd));

        match self.evt_receiver.recv() {
            Ok(SessionEvt::SocketCreated(id, rx)) => Ok(self.new_socket(id, rx)),
            Err(_)                                => Err(other_io_error("evt channel closed"))
        }
    }

    fn new_socket(&self, id: SocketId, rx: mpsc::Receiver<SocketEvt>) -> SocketFacade {
        SocketFacade::new(id, self.cmd_sender.clone(), rx)
    }
}

impl Drop for SessionFacade {
    fn drop(&mut self) {
        let session_cmd = SessionCmd::Shutdown;
        let cmd = EventLoopCmd::SessionLevel(session_cmd);

        let _ = self.cmd_sender.send(cmd);
    }
}

pub struct SocketFacade {
    id: SocketId,
    cmd_sender: Sender<EventLoopCmd>,
    evt_receiver: Receiver<SocketEvt>
    // Could use https://github.com/polyfractal/bounded-spsc-queue ?
    // Maybe once a smart waiting strategy is available (like spin, then sleep 0, then sleep 1, then mutex ?)
}

impl SocketFacade {
    pub fn new(id: SocketId, cmd_tx: Sender<EventLoopCmd>, evt_rx: Receiver<SocketEvt>) -> SocketFacade {
        SocketFacade { id: id, cmd_sender: cmd_tx, evt_receiver: evt_rx }
    }

    fn send_cmd(&self, socket_cmd: SocketCmd) -> Result<(), io::Error> {
        let cmd = EventLoopCmd::SocketLevel(self.id, socket_cmd);

        self.cmd_sender.send(cmd).map_err(|e| convert_notify_err(e))
    }

    pub fn connect(&mut self, addr: &str) -> Result<(), io::Error> {
        let cmd = SocketCmd::Connect(addr.to_owned());

        try!(self.send_cmd(cmd));

        match self.evt_receiver.recv() {
            Ok(SocketEvt::Connected)       => Ok(()),
            Ok(SocketEvt::NotConnected(e)) => Err(e),
            Ok(_)                          => Err(other_io_error("unexpected evt")),
            Err(_)                         => Err(other_io_error("evt channel closed"))
        }
    }

    pub fn bind(&mut self, addr: &str) -> Result<(), io::Error> {
        let cmd = SocketCmd::Bind(addr.to_owned());

        try!(self.send_cmd(cmd));

        match self.evt_receiver.recv() {
            Ok(SocketEvt::Bound)       => Ok(()),
            Ok(SocketEvt::NotBound(e)) => Err(e),
            Ok(_)                      => Err(other_io_error("unexpected evt")),
            Err(_)                     => Err(other_io_error("evt channel closed"))
        }
    }

    pub fn send(&mut self, buffer: Vec<u8>) -> Result<(), io::Error> {
        self.send_msg(Message::with_body(buffer))
    }

    pub fn send_msg(&mut self, msg: Message) -> Result<(), io::Error> {
        let cmd = SocketCmd::SendMsg(msg);

        try!(self.send_cmd(cmd));

        match self.evt_receiver.recv() {
            Ok(SocketEvt::MsgSent)       => Ok(()),
            Ok(SocketEvt::MsgNotSent(e)) => Err(e),
            Ok(_)                        => Err(other_io_error("unexpected evt")),
            Err(_)                       => Err(other_io_error("evt channel closed"))
        }
    }

    pub fn recv(&mut self) -> Result<Vec<u8>, io::Error> {
        self.recv_msg().map(|msg| msg.to_buffer())
    }

    pub fn recv_msg(&mut self) -> Result<Message, io::Error> {
        try!(self.send_cmd(SocketCmd::RecvMsg));

        match self.evt_receiver.recv() {
            Ok(SocketEvt::MsgRecv(msg))  => Ok(msg),
            Ok(SocketEvt::MsgNotRecv(e)) => Err(e),
            Ok(_)                        => Err(other_io_error("unexpected evt")),
            Err(_)                       => Err(other_io_error("evt channel closed"))
        }
    }

    pub fn set_option(&mut self, option: SocketOption) -> io::Result<()> {
        let cmd = SocketCmd::SetOption(option);

        try!(self.send_cmd(cmd));

        match self.evt_receiver.recv() {
            Ok(SocketEvt::OptionSet)       => Ok(()),
            Ok(SocketEvt::OptionNotSet(e)) => Err(e),
            Ok(_)                          => Err(other_io_error("unexpected evt")),
            Err(_)                         => Err(other_io_error("evt channel closed"))
        }
    }

    pub fn set_send_timeout(&mut self, timeout: time::Duration) -> io::Result<()> {
        self.set_option(SocketOption::SendTimeout(timeout))
    }

    pub fn set_recv_timeout(&mut self, timeout: time::Duration) -> io::Result<()> {
        self.set_option(SocketOption::RecvTimeout(timeout))
    }
}

#[cfg(test)]
mod tests {
    use super::SessionFacade;
    use global::SocketType;
    use std::io;
    use std::io::Write;

    #[test]
    fn session_can_create_a_socket() {
        let session = SessionFacade::new().unwrap();
        let socket = session.create_socket(SocketType::Push).unwrap();

        drop(socket);
    }

    #[test]
    fn can_connect_socket() {
        let session = SessionFacade::new().unwrap();
        let mut socket = session.create_socket(SocketType::Push).unwrap();
        let connect_res = socket.connect("tcp://127.0.0.1:18080");

        match connect_res {
            Ok(()) => {},
            Err(e) => {
                //writeln!(&mut io::stderr(), "connect failed: {} !", e);
                panic!("connection failed: '{}'", e);
            }
        }
    }

    #[test]
    fn can_try_connect_socket() {
        let session = SessionFacade::new().unwrap();
        let mut socket = session.create_socket(SocketType::Push).unwrap();

        assert!(socket.connect("tcp://this should not work").is_err());
    }
}<|endoftext|>


<commit_before>use crate::types::pid::Pid;
use crate::wrapper::{NIF_ENV, NIF_TERM};
use crate::{Encoder, Term};
use std::marker::PhantomData;
use std::ptr;
use std::sync::{Arc, Weak};

/// Private type system hack to help ensure that each environment exposed to safe Rust code is
/// given a different lifetime. The size of this type is zero, so it costs nothing at run time. Its
/// purpose is to make `Env<'a>` and `Term<'a>` *invariant* w.r.t. `'a`, so that Rust won't
/// auto-convert a `Env<'a>` to a `Env<'b>`.
type EnvId<'a> = PhantomData<*mut &'a u8>;

/// On each NIF call, a Env is passed in. The Env is used for most operations that involve
/// communicating with the BEAM, like decoding and encoding terms.
///
/// There is no way to allocate a Env at the moment, but this may be possible in the future.
#[derive(Clone, Copy)]
pub struct Env<'a> {
    env: NIF_ENV,
    id: EnvId<'a>,
}

/// Two environments are equal if they're the same `NIF_ENV` value.
///
/// A `Env<'a>` is equal to a `Env<'b>` if and only if `'a` and `'b` are the same lifetime.
impl<'a, 'b> PartialEq<Env<'b>> for Env<'a> {
    fn eq(&self, other: &Env<'b>) -> bool {
        self.env == other.env
    }
}

impl<'a> Env<'a> {
    /// Create a new Env. For the `_lifetime_marker` argument, pass a
    /// reference to any local variable that has its own lifetime, different
    /// from all other `Env` values. The purpose of the argument is to make
    /// it easier to know for sure that the `Env` you're creating has a
    /// unique lifetime (i.e. that you're following the most important safety
    /// rule of Rustler).
    ///
    /// # Unsafe
    /// Don't create multiple `Env`s with the same lifetime.
    pub unsafe fn new<T>(_lifetime_marker: &'a T, env: NIF_ENV) -> Env<'a> {
        Env {
            env,
            id: PhantomData,
        }
    }

    pub fn as_c_arg(self) -> NIF_ENV {
        self.env
    }

    /// Convenience method for building a tuple `{error, Reason}`.
    pub fn error_tuple<T>(self, reason: T) -> Term<'a>
    where
        T: Encoder,
    {
        let error = crate::types::atom::error().to_term(self);
        (error, reason).encode(self)
    }

    /// Send a message to a process.
    ///
    /// The Erlang VM imposes some odd restrictions on sending messages.
    /// You can send messages in either of these situations:
    ///
    /// *   The current thread is managed by the Erlang VM, and `self` is the
    ///     environment of the calling process (that is, the environment that
    ///     Rustler passed in to your NIF); *or*
    ///
    /// *   The current thread is *not* managed by the Erlang VM.
    ///
    /// # Panics
    ///
    /// Panics if the above rules are broken (by trying to send a message from
    /// an `OwnedEnv` on a thread that's managed by the Erlang VM).
    ///
    pub fn send(self, pid: &Pid, message: Term<'a>) {
        let thread_type = unsafe { rustler_sys::enif_thread_type() };
        let env = if thread_type == rustler_sys::ERL_NIF_THR_UNDEFINED {
            ptr::null_mut()
        } else if thread_type == rustler_sys::ERL_NIF_THR_NORMAL_SCHEDULER
            || thread_type == rustler_sys::ERL_NIF_THR_DIRTY_CPU_SCHEDULER
            || thread_type == rustler_sys::ERL_NIF_THR_DIRTY_IO_SCHEDULER
        {
            // Panic if `self` is not the environment of the calling process.
            self.pid();

            self.as_c_arg()
        } else {
            panic!("Env::send(): unrecognized calling thread type");
        };

        // Send the message.
        unsafe {
            rustler_sys::enif_send(env, pid.as_c_arg(), ptr::null_mut(), message.as_c_arg());
        }
    }

    /// Decodes binary data to a term.
    ///
    /// Follows the erlang
    /// [External Term Format](http://erlang.org/doc/apps/erts/erl_ext_dist.html).
    pub fn binary_to_term(self, data: &[u8]) -> Option<(Term<'a>, usize)> {
        unsafe {
            crate::wrapper::env::binary_to_term(self.as_c_arg(), data, true)
                .map(|(term, size)| (Term::new(self, term), size))
        }
    }

    /// Like `binary_to_term`, but can only be called on valid
    /// and trusted data.
    pub unsafe fn binary_to_term_trusted(self, data: &[u8]) -> Option<(Term<'a>, usize)> {
        crate::wrapper::env::binary_to_term(self.as_c_arg(), data, false)
            .map(|(term, size)| (Term::new(self, term), size))
    }
}

/// A process-independent environment, a place where Erlang terms can be created outside of a NIF
/// call.
///
/// Rust code can use an owned environment to build a message and send it to an
/// Erlang process.
///
///     use rustler::env::OwnedEnv;
///     use rustler::types::pid::Pid;
///     use rustler::Encoder;
///
///     fn send_string_to_pid(data: &str, pid: &Pid) {
///         let mut msg_env = OwnedEnv::new();
///         msg_env.send_and_clear(pid, |env| data.encode(env));
///     }
///
/// There's no way to run Erlang code in an `OwnedEnv`. It's not a process. It's just a workspace
/// for building terms.
pub struct OwnedEnv {
    env: Arc<NIF_ENV>,
}

unsafe impl Send for OwnedEnv {}

impl OwnedEnv {
    /// Allocates a new process-independent environment.
    pub fn new() -> OwnedEnv {
        OwnedEnv {
            env: Arc::new(unsafe { rustler_sys::enif_alloc_env() }),
        }
    }

    /// Run some code in this environment.
    pub fn run<F, R>(&self, closure: F) -> R
    where
        F: for<'a> FnOnce(Env<'a>) -> R,
    {
        let env = unsafe { Env::new(&(), *self.env) };
        closure(env)
    }

    /// Send a message from a Rust thread to an Erlang process.
    ///
    /// The environment is cleared as though by calling the `.clear()` method.
    /// To avoid that, use `env.send(pid, term)` instead.
    ///
    /// # Panics
    ///
    /// Panics if called from a thread that is managed by the Erlang VM. You
    /// can only use this method on a thread that was created by other
    /// means. (This curious restriction is imposed by the Erlang VM.)
    ///
    pub fn send_and_clear<F>(&mut self, recipient: &Pid, closure: F)
    where
        F: for<'a> FnOnce(Env<'a>) -> Term<'a>,
    {
        if unsafe { rustler_sys::enif_thread_type() } != rustler_sys::ERL_NIF_THR_UNDEFINED {
            panic!("send_and_clear: current thread is managed");
        }

        let message = self.run(|env| closure(env).as_c_arg());

        let c_env = *self.env;
        self.env = Arc::new(c_env); // invalidate SavedTerms
        unsafe {
            rustler_sys::enif_send(ptr::null_mut(), recipient.as_c_arg(), c_env, message);
        }
    }

    /// Free all terms in this environment and clear it for reuse.
    ///
    /// This invalidates `SavedTerm`s that were saved in this environment;
    /// if you later try to `.load()` one, you'll get a panic.
    ///
    /// Unless you call this method after a call to `run()`, all terms created within the
    /// environment hang around in memory until the `OwnedEnv` is dropped: garbage collection does
    /// not continually happen as needed in a NIF environment.
    pub fn clear(&mut self) {
        let c_env = *self.env;
        self.env = Arc::new(c_env);
        unsafe {
            rustler_sys::enif_clear_env(c_env);
        }
    }

    /// Save a term for use in a later call to `.run()` or `.send()`.
    ///
    /// For your safety, Rust doesn't let you save `Term` values from one `.run()` call to a
    /// later `.run()` call. If you try, it'll complain about lifetimes.
    ///
    /// `.save()` offers a way to do this. For example, maybe you'd like to copy a term from the
    /// caller into an `OwnedEnv`, then use that term on another thread.
    ///
    ///     # use rustler::{ Env, Term };
    ///     use rustler::env::OwnedEnv;
    ///     use std::thread;
    ///
    ///     fn thread_example<'a>(env: Env<'a>, term: Term<'a>) {
    ///         // Copy `term` into a new OwnedEnv, for use on another thread.
    ///         let mut thread_env = OwnedEnv::new();
    ///         let saved_term = thread_env.save(term);
    ///
    ///         thread::spawn(move || {
    ///             // Now run some code on the thread, using the saved term.
    ///             thread_env.run(|env| {
    ///                 let term = saved_term.load(env);
    ///                 //... do stuff with term ...
    ///             });
    ///         });
    ///     }
    ///
    /// **Note: There is no way to save terms across `OwnedEnv::send()` or `clear()`.**
    /// If you try, the `.load()` call will panic.
    pub fn save<'a>(&self, term: Term<'a>) -> SavedTerm {
        SavedTerm {
            term: self.run(|env| term.in_env(env).as_c_arg()),
            env_generation: Arc::downgrade(&self.env),
        }
    }
}

impl Drop for OwnedEnv {
    fn drop(&mut self) {
        unsafe {
            rustler_sys::enif_free_env(*self.env);
        }
    }
}

/// A term that was created in an `OwnedEnv` and saved for later use.
///
/// These are created by calling `OwnedEnv::save()`. See that method's documentation for an
/// example.
#[derive(Clone)]
pub struct SavedTerm {
    env_generation: Weak<NIF_ENV>,
    term: NIF_TERM,
}

unsafe impl Send for SavedTerm {}

impl SavedTerm {
    /// Load this saved term back into its environment.
    ///
    /// # Panics
    ///
    /// `env` must be the `Env` of a `.run()` or `.send()` call on the
    /// `OwnedEnv` where this term was saved, and the `OwnedEnv` must not have
    /// been cleared or dropped since then. Otherwise this method will panic.
    pub fn load<'a>(&self, env: Env<'a>) -> Term<'a> {
        // Check that the saved term is still valid.
        match self.env_generation.upgrade() {
            None => panic!("term is from a cleared or dropped OwnedEnv"),
            Some(ref env_arc) if **env_arc == env.as_c_arg() => unsafe {
                Term::new(env, self.term)
            },
            _ => panic!("can't load SavedTerm into a different environment"),
        }
    }
}

impl Default for OwnedEnv {
    fn default() -> Self {
        Self::new()
    }
}
<commit_msg>Fix Segfault in OwnedEnv::send_and_clear<commit_after>use crate::types::pid::Pid;
use crate::wrapper::{NIF_ENV, NIF_TERM};
use crate::{Encoder, Term};
use std::marker::PhantomData;
use std::ptr;
use std::sync::{Arc, Weak};

/// Private type system hack to help ensure that each environment exposed to safe Rust code is
/// given a different lifetime. The size of this type is zero, so it costs nothing at run time. Its
/// purpose is to make `Env<'a>` and `Term<'a>` *invariant* w.r.t. `'a`, so that Rust won't
/// auto-convert a `Env<'a>` to a `Env<'b>`.
type EnvId<'a> = PhantomData<*mut &'a u8>;

/// On each NIF call, a Env is passed in. The Env is used for most operations that involve
/// communicating with the BEAM, like decoding and encoding terms.
///
/// There is no way to allocate a Env at the moment, but this may be possible in the future.
#[derive(Clone, Copy)]
pub struct Env<'a> {
    env: NIF_ENV,
    id: EnvId<'a>,
}

/// Two environments are equal if they're the same `NIF_ENV` value.
///
/// A `Env<'a>` is equal to a `Env<'b>` if and only if `'a` and `'b` are the same lifetime.
impl<'a, 'b> PartialEq<Env<'b>> for Env<'a> {
    fn eq(&self, other: &Env<'b>) -> bool {
        self.env == other.env
    }
}

impl<'a> Env<'a> {
    /// Create a new Env. For the `_lifetime_marker` argument, pass a
    /// reference to any local variable that has its own lifetime, different
    /// from all other `Env` values. The purpose of the argument is to make
    /// it easier to know for sure that the `Env` you're creating has a
    /// unique lifetime (i.e. that you're following the most important safety
    /// rule of Rustler).
    ///
    /// # Unsafe
    /// Don't create multiple `Env`s with the same lifetime.
    pub unsafe fn new<T>(_lifetime_marker: &'a T, env: NIF_ENV) -> Env<'a> {
        Env {
            env,
            id: PhantomData,
        }
    }

    pub fn as_c_arg(self) -> NIF_ENV {
        self.env
    }

    /// Convenience method for building a tuple `{error, Reason}`.
    pub fn error_tuple<T>(self, reason: T) -> Term<'a>
    where
        T: Encoder,
    {
        let error = crate::types::atom::error().to_term(self);
        (error, reason).encode(self)
    }

    /// Send a message to a process.
    ///
    /// The Erlang VM imposes some odd restrictions on sending messages.
    /// You can send messages in either of these situations:
    ///
    /// *   The current thread is managed by the Erlang VM, and `self` is the
    ///     environment of the calling process (that is, the environment that
    ///     Rustler passed in to your NIF); *or*
    ///
    /// *   The current thread is *not* managed by the Erlang VM.
    ///
    /// # Panics
    ///
    /// Panics if the above rules are broken (by trying to send a message from
    /// an `OwnedEnv` on a thread that's managed by the Erlang VM).
    ///
    pub fn send(self, pid: &Pid, message: Term<'a>) {
        let thread_type = unsafe { rustler_sys::enif_thread_type() };
        let env = if thread_type == rustler_sys::ERL_NIF_THR_UNDEFINED {
            ptr::null_mut()
        } else if thread_type == rustler_sys::ERL_NIF_THR_NORMAL_SCHEDULER
            || thread_type == rustler_sys::ERL_NIF_THR_DIRTY_CPU_SCHEDULER
            || thread_type == rustler_sys::ERL_NIF_THR_DIRTY_IO_SCHEDULER
        {
            // Panic if `self` is not the environment of the calling process.
            self.pid();

            self.as_c_arg()
        } else {
            panic!("Env::send(): unrecognized calling thread type");
        };

        // Send the message.
        unsafe {
            rustler_sys::enif_send(env, pid.as_c_arg(), ptr::null_mut(), message.as_c_arg());
        }
    }

    /// Decodes binary data to a term.
    ///
    /// Follows the erlang
    /// [External Term Format](http://erlang.org/doc/apps/erts/erl_ext_dist.html).
    pub fn binary_to_term(self, data: &[u8]) -> Option<(Term<'a>, usize)> {
        unsafe {
            crate::wrapper::env::binary_to_term(self.as_c_arg(), data, true)
                .map(|(term, size)| (Term::new(self, term), size))
        }
    }

    /// Like `binary_to_term`, but can only be called on valid
    /// and trusted data.
    pub unsafe fn binary_to_term_trusted(self, data: &[u8]) -> Option<(Term<'a>, usize)> {
        crate::wrapper::env::binary_to_term(self.as_c_arg(), data, false)
            .map(|(term, size)| (Term::new(self, term), size))
    }
}

/// A process-independent environment, a place where Erlang terms can be created outside of a NIF
/// call.
///
/// Rust code can use an owned environment to build a message and send it to an
/// Erlang process.
///
///     use rustler::env::OwnedEnv;
///     use rustler::types::pid::Pid;
///     use rustler::Encoder;
///
///     fn send_string_to_pid(data: &str, pid: &Pid) {
///         let mut msg_env = OwnedEnv::new();
///         msg_env.send_and_clear(pid, |env| data.encode(env));
///     }
///
/// There's no way to run Erlang code in an `OwnedEnv`. It's not a process. It's just a workspace
/// for building terms.
pub struct OwnedEnv {
    env: Arc<NIF_ENV>,
}

unsafe impl Send for OwnedEnv {}

impl OwnedEnv {
    /// Allocates a new process-independent environment.
    pub fn new() -> OwnedEnv {
        OwnedEnv {
            env: Arc::new(unsafe { rustler_sys::enif_alloc_env() }),
        }
    }

    /// Run some code in this environment.
    pub fn run<F, R>(&self, closure: F) -> R
    where
        F: for<'a> FnOnce(Env<'a>) -> R,
    {
        let env = unsafe { Env::new(&(), *self.env) };
        closure(env)
    }

    /// Send a message from a Rust thread to an Erlang process.
    ///
    /// The environment is cleared as though by calling the `.clear()` method.
    /// To avoid that, use `env.send(pid, term)` instead.
    ///
    /// # Panics
    ///
    /// Panics if called from a thread that is managed by the Erlang VM. You
    /// can only use this method on a thread that was created by other
    /// means. (This curious restriction is imposed by the Erlang VM.)
    ///
    pub fn send_and_clear<F>(&mut self, recipient: &Pid, closure: F)
    where
        F: for<'a> FnOnce(Env<'a>) -> Term<'a>,
    {
        if unsafe { rustler_sys::enif_thread_type() } != rustler_sys::ERL_NIF_THR_UNDEFINED {
            panic!("send_and_clear: current thread is managed");
        }

        let message = self.run(|env| closure(env).as_c_arg());

        unsafe {
            rustler_sys::enif_send(ptr::null_mut(), recipient.as_c_arg(), *self.env, message);
        }

        self.clear();
    }

    /// Free all terms in this environment and clear it for reuse.
    ///
    /// This invalidates `SavedTerm`s that were saved in this environment;
    /// if you later try to `.load()` one, you'll get a panic.
    ///
    /// Unless you call this method after a call to `run()`, all terms created within the
    /// environment hang around in memory until the `OwnedEnv` is dropped: garbage collection does
    /// not continually happen as needed in a NIF environment.
    pub fn clear(&mut self) {
        let c_env = *self.env;
        self.env = Arc::new(c_env);
        unsafe {
            rustler_sys::enif_clear_env(c_env);
        }
    }

    /// Save a term for use in a later call to `.run()` or `.send()`.
    ///
    /// For your safety, Rust doesn't let you save `Term` values from one `.run()` call to a
    /// later `.run()` call. If you try, it'll complain about lifetimes.
    ///
    /// `.save()` offers a way to do this. For example, maybe you'd like to copy a term from the
    /// caller into an `OwnedEnv`, then use that term on another thread.
    ///
    ///     # use rustler::{ Env, Term };
    ///     use rustler::env::OwnedEnv;
    ///     use std::thread;
    ///
    ///     fn thread_example<'a>(env: Env<'a>, term: Term<'a>) {
    ///         // Copy `term` into a new OwnedEnv, for use on another thread.
    ///         let mut thread_env = OwnedEnv::new();
    ///         let saved_term = thread_env.save(term);
    ///
    ///         thread::spawn(move || {
    ///             // Now run some code on the thread, using the saved term.
    ///             thread_env.run(|env| {
    ///                 let term = saved_term.load(env);
    ///                 //... do stuff with term ...
    ///             });
    ///         });
    ///     }
    ///
    /// **Note: There is no way to save terms across `OwnedEnv::send()` or `clear()`.**
    /// If you try, the `.load()` call will panic.
    pub fn save<'a>(&self, term: Term<'a>) -> SavedTerm {
        SavedTerm {
            term: self.run(|env| term.in_env(env).as_c_arg()),
            env_generation: Arc::downgrade(&self.env),
        }
    }
}

impl Drop for OwnedEnv {
    fn drop(&mut self) {
        unsafe {
            rustler_sys::enif_free_env(*self.env);
        }
    }
}

/// A term that was created in an `OwnedEnv` and saved for later use.
///
/// These are created by calling `OwnedEnv::save()`. See that method's documentation for an
/// example.
#[derive(Clone)]
pub struct SavedTerm {
    env_generation: Weak<NIF_ENV>,
    term: NIF_TERM,
}

unsafe impl Send for SavedTerm {}

impl SavedTerm {
    /// Load this saved term back into its environment.
    ///
    /// # Panics
    ///
    /// `env` must be the `Env` of a `.run()` or `.send()` call on the
    /// `OwnedEnv` where this term was saved, and the `OwnedEnv` must not have
    /// been cleared or dropped since then. Otherwise this method will panic.
    pub fn load<'a>(&self, env: Env<'a>) -> Term<'a> {
        // Check that the saved term is still valid.
        match self.env_generation.upgrade() {
            None => panic!("term is from a cleared or dropped OwnedEnv"),
            Some(ref env_arc) if **env_arc == env.as_c_arg() => unsafe {
                Term::new(env, self.term)
            },
            _ => panic!("can't load SavedTerm into a different environment"),
        }
    }
}

impl Default for OwnedEnv {
    fn default() -> Self {
        Self::new()
    }
}
<|endoftext|>


<commit_before>
use std::fmt;
use std::io::Read;  // Trait providing read_to_string()
use std::env;

use hyper;
use serde;
use serde_json;

use BuildQuery;
use Groups;

pub const API_VERSION: u16 = 3;

#[derive(Debug)]
pub struct Pagination {
    pub page: u16,
    pub per_page: u16,
}

pub struct GitLab {
    scheme: String,
    domain: String,
    port: u16,
    private_token: String,
    client: hyper::Client,
    pagination: Pagination,
}

// Explicitly implement Debug trait for GitLab so we can hide the token.
impl fmt::Debug for GitLab {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f,
               "GitLab {{ scheme: {}, domain: {}, port: {}, private_token: XXXXXXXXXXXXXXXXXXXX, \
                client: {:?}, pagination: {:?} }}",
               self.scheme,
               self.domain,
               self.port,
               self.client,
               self.pagination)
    }
}

impl GitLab {
    pub fn new(scheme: &str, domain: &str, port: u16, private_token: &str) -> GitLab {
        GitLab {
            scheme: scheme.to_string(),
            domain: domain.to_string(),
            port: port,
            private_token: private_token.to_string(),
            client: match env::var("HTTP_PROXY") {
                Ok(proxy) => {
                    let proxy: Vec<&str> = proxy.trim_left_matches("http://").split(':').collect();
                    let hostname = proxy[0].to_string();
                    let port = proxy[1];

                    hyper::Client::with_http_proxy(hostname, port.parse().unwrap())
                }
                Err(_) => hyper::Client::new(),
            },
            pagination: Pagination {
                page: 1,
                per_page: 20,
            },
        }
    }

    pub fn new_http(domain: &str, private_token: &str) -> GitLab {
        GitLab::new("http", domain, 80, private_token)
    }

    pub fn new_https(domain: &str, private_token: &str) -> GitLab {
        GitLab::new("https", domain, 443, private_token)
    }

    /// Build a URL used to access GitLab instance, including some parameters.
    ///
    /// # Examples
    ///
    /// Example from GitLab: https://docs.gitlab.com/ce/api/#basic-usage
    ///
    /// ```
    /// use gitlab_api::GitLab;
    ///
    /// let expected_url = "https://gitlab.example.com:\
    ///                     443/api/v3/groups?\
    ///                     order_by=path&private_token=XXXXXXXXXXXXX&page=1&per_page=20";
    ///
    /// let gl = GitLab::new_https("gitlab.example.com", "XXXXXXXXXXXXX");
    ///
    /// assert_eq!(gl.build_url("groups?order_by=path"), expected_url);
    /// ```
    pub fn build_url(&self, query: &str) -> String {
        let params_splitter = if query.find('?').is_some() { "&" } else { "?" };
        format!("{}://{}:{}/api/v{}/{}{}private_token={}&page={}&per_page={}",
                self.scheme,
                self.domain,
                self.port,
                API_VERSION,
                query,
                params_splitter,
                self.private_token,
                self.pagination.page,
                self.pagination.per_page)
    }

    pub fn attempt_connection(&self) -> Result<hyper::client::Response, hyper::Error> {
        let url = self.build_url("version");
        // Close connections after each GET.
        self.client.get(&url).header(hyper::header::Connection::close()).send()
    }

    pub fn set_pagination(&mut self, pagination: Pagination) {
        self.pagination = pagination;
    }

    pub fn get<T>(&self, query: &str) -> Result<T, serde_json::Error>
        where T: serde::Deserialize
    {
        let url = self.build_url(&query);
        info!("url: {:?}", url);
        let mut res: hyper::client::Response = self.client
            .get(&url)
            .header(hyper::header::Connection::close())
            .send()
            .unwrap();

        let mut body = String::new();
        res.read_to_string(&mut body).unwrap();
        debug!("body: {:?}", body);

        // FIXME: Properly handle the error. Will require defining our own errors...
        assert_eq!(res.status, hyper::status::StatusCode::Ok);

        serde_json::from_str(&body.as_str())
    }

    // pub fn version(&self) -> Result<Version, serde_json::Error> {
    //     self.get("version")
    // }
    //
    // pub fn groups(&self) -> Result<Groups, serde_json::Error> {
    //     self.get("groups")
    // }
    //
    // pub fn projects(&self) -> Result<Projects, serde_json::Error> {
    //     self.get("projects")
    // }

    pub fn groups(&mut self, listing: ::groups::Listing) -> Result<Groups, serde_json::Error> {
        let query = listing.build_query();
        self.get(&query)
    }

    pub fn owned_groups(&mut self) -> Result<Groups, serde_json::Error> {
        let query = ::groups::owned_groups::Listing::new().build_query();
        info!("query: {:?}", query);
        self.get(&query)
    }
}

// #[cfg(test)]
// mod tests {
//
// #[test]
// fn it_works() {
// }
// }
//
<commit_msg>needless_borrow<commit_after>
use std::fmt;
use std::io::Read;  // Trait providing read_to_string()
use std::env;

use hyper;
use serde;
use serde_json;

use BuildQuery;
use Groups;

pub const API_VERSION: u16 = 3;

#[derive(Debug)]
pub struct Pagination {
    pub page: u16,
    pub per_page: u16,
}

pub struct GitLab {
    scheme: String,
    domain: String,
    port: u16,
    private_token: String,
    client: hyper::Client,
    pagination: Pagination,
}

// Explicitly implement Debug trait for GitLab so we can hide the token.
impl fmt::Debug for GitLab {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f,
               "GitLab {{ scheme: {}, domain: {}, port: {}, private_token: XXXXXXXXXXXXXXXXXXXX, \
                client: {:?}, pagination: {:?} }}",
               self.scheme,
               self.domain,
               self.port,
               self.client,
               self.pagination)
    }
}

impl GitLab {
    pub fn new(scheme: &str, domain: &str, port: u16, private_token: &str) -> GitLab {
        GitLab {
            scheme: scheme.to_string(),
            domain: domain.to_string(),
            port: port,
            private_token: private_token.to_string(),
            client: match env::var("HTTP_PROXY") {
                Ok(proxy) => {
                    let proxy: Vec<&str> = proxy.trim_left_matches("http://").split(':').collect();
                    let hostname = proxy[0].to_string();
                    let port = proxy[1];

                    hyper::Client::with_http_proxy(hostname, port.parse().unwrap())
                }
                Err(_) => hyper::Client::new(),
            },
            pagination: Pagination {
                page: 1,
                per_page: 20,
            },
        }
    }

    pub fn new_http(domain: &str, private_token: &str) -> GitLab {
        GitLab::new("http", domain, 80, private_token)
    }

    pub fn new_https(domain: &str, private_token: &str) -> GitLab {
        GitLab::new("https", domain, 443, private_token)
    }

    /// Build a URL used to access GitLab instance, including some parameters.
    ///
    /// # Examples
    ///
    /// Example from GitLab: https://docs.gitlab.com/ce/api/#basic-usage
    ///
    /// ```
    /// use gitlab_api::GitLab;
    ///
    /// let expected_url = "https://gitlab.example.com:\
    ///                     443/api/v3/groups?\
    ///                     order_by=path&private_token=XXXXXXXXXXXXX&page=1&per_page=20";
    ///
    /// let gl = GitLab::new_https("gitlab.example.com", "XXXXXXXXXXXXX");
    ///
    /// assert_eq!(gl.build_url("groups?order_by=path"), expected_url);
    /// ```
    pub fn build_url(&self, query: &str) -> String {
        let params_splitter = if query.find('?').is_some() { "&" } else { "?" };
        format!("{}://{}:{}/api/v{}/{}{}private_token={}&page={}&per_page={}",
                self.scheme,
                self.domain,
                self.port,
                API_VERSION,
                query,
                params_splitter,
                self.private_token,
                self.pagination.page,
                self.pagination.per_page)
    }

    pub fn attempt_connection(&self) -> Result<hyper::client::Response, hyper::Error> {
        let url = self.build_url("version");
        // Close connections after each GET.
        self.client.get(&url).header(hyper::header::Connection::close()).send()
    }

    pub fn set_pagination(&mut self, pagination: Pagination) {
        self.pagination = pagination;
    }

    pub fn get<T>(&self, query: &str) -> Result<T, serde_json::Error>
        where T: serde::Deserialize
    {
        let url = self.build_url(query);
        info!("url: {:?}", url);
        let mut res: hyper::client::Response = self.client
            .get(&url)
            .header(hyper::header::Connection::close())
            .send()
            .unwrap();

        let mut body = String::new();
        res.read_to_string(&mut body).unwrap();
        debug!("body: {:?}", body);

        // FIXME: Properly handle the error. Will require defining our own errors...
        assert_eq!(res.status, hyper::status::StatusCode::Ok);

        serde_json::from_str(&body.as_str())
    }

    // pub fn version(&self) -> Result<Version, serde_json::Error> {
    //     self.get("version")
    // }
    //
    // pub fn groups(&self) -> Result<Groups, serde_json::Error> {
    //     self.get("groups")
    // }
    //
    // pub fn projects(&self) -> Result<Projects, serde_json::Error> {
    //     self.get("projects")
    // }

    pub fn groups(&mut self, listing: ::groups::Listing) -> Result<Groups, serde_json::Error> {
        let query = listing.build_query();
        self.get(&query)
    }

    pub fn owned_groups(&mut self) -> Result<Groups, serde_json::Error> {
        let query = ::groups::owned_groups::Listing::new().build_query();
        info!("query: {:?}", query);
        self.get(&query)
    }
}

// #[cfg(test)]
// mod tests {
//
// #[test]
// fn it_works() {
// }
// }
//
<|endoftext|>