<commit_before>//! A set of constants used by the library.

/// The maximum length of the textual size of an embed.
pub const EMBED_MAX_LENGTH: usize = 6000;

/// The maximum number of embeds in a message.
pub const EMBED_MAX_COUNT: usize = 10;

/// The maximum number of stickers in a message.
pub const STICKER_MAX_COUNT: usize = 3;

/// The gateway version used by the library. The gateway URI is retrieved via
/// the REST API.
pub const GATEWAY_VERSION: u8 = 9;

/// The large threshold to send on identify.
pub const LARGE_THRESHOLD: u8 = 250;

/// The maximum unicode code points allowed within a message by Discord.
pub const MESSAGE_CODE_LIMIT: usize = 2000;

/// The maximum number of members the bot can fetch at once
pub const MEMBER_FETCH_LIMIT: u64 = 1000;

/// The [UserAgent] sent along with every request.
///
/// [UserAgent]: ::reqwest::header::USER_AGENT
pub const USER_AGENT: &str = concat!(
    "DiscordBot (https://github.com/serenity-rs/serenity, ",
    env!("CARGO_PKG_VERSION"),
    ")"
);

/// List of messages Discord shows on member join.
#[allow(clippy::non_ascii_literal)] // allow for discord join messages
pub static JOIN_MESSAGES: &[&str] = &[
    "$user just joined the server - glhf!",
    "$user just joined. Everyone, look busy!",
    "$user just joined. Can I get a heal?",
    "$user joined your party.",
    "$user joined. You must construct additional pylons.",
    "Ermagherd. $user is here.",
    "Welcome, $user. Stay awhile and listen.",
    "Welcome, $user. We were expecting you ( ͡° ͜ʖ ͡°)",
    "Welcome, $user. We hope you brought pizza.",
    "Welcome $user. Leave your weapons by the door.",
    "A wild $user appeared.",
    "Swoooosh. $user just landed.",
    "Brace yourselves. $user just joined the server.",
    "$user just joined... or did they?",
    "$user just arrived. Seems OP - please nerf.",
    "$user just slid into the server.",
    "A $user has spawned in the server.",
    "Big $user showed up!",
    "Where’s $user? In the server!",
    "$user hopped into the server. Kangaroo!!",
    "$user just showed up. Hold my beer.",
    "Challenger approaching - $user has appeared!",
    "It's a bird! It's a plane! Nevermind, it's just $user.",
    r"It's $user! Praise the sun! \[T]/",
    "Never gonna give $user up. Never gonna let $user down.",
    "$user has joined the battle bus.",
    "Cheers, love! $user's here!",
    "Hey! Listen! $user has joined!",
    "We've been expecting you $user",
    "It's dangerous to go alone, take $user!",
    "$user has joined the server! It's super effective!",
    "Cheers, love! $user is here!",
    "$user is here, as the prophecy foretold.",
    "$user has arrived. Party's over.",
    "Ready player $user",
    "$user is here to kick butt and chew bubblegum. And $user is all out of gum.",
    "Hello. Is it $user you're looking for?",
    "$user has joined. Stay a while and listen!",
    "Roses are red, violets are blue, $user joined this server with you",
];

/// Enum to map gateway opcodes.
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)]
#[non_exhaustive]
pub enum OpCode {
    /// Dispatches an event.
    Event = 0,
    /// Used for ping checking.
    Heartbeat = 1,
    /// Used for client handshake.
    Identify = 2,
    /// Used to update the client status.
    StatusUpdate = 3,
    /// Used to join/move/leave voice channels.
    VoiceStateUpdate = 4,
    /// Used for voice ping checking.
    VoiceServerPing = 5,
    /// Used to resume a closed connection.
    Resume = 6,
    /// Used to tell clients to reconnect to the gateway.
    Reconnect = 7,
    /// Used to request guild members.
    GetGuildMembers = 8,
    /// Used to notify clients that they have an invalid session Id.
    InvalidSession = 9,
    /// Sent immediately after connection, contains heartbeat + server info.
    Hello = 10,
    /// Sent immediately following a client heartbeat that was received.
    HeartbeatAck = 11,
    /// Unknown opcode.
    Unknown = !0,
}

enum_number!(OpCode {
    Event,
    Heartbeat,
    Identify,
    StatusUpdate,
    VoiceStateUpdate,
    VoiceServerPing,
    Resume,
    Reconnect,
    GetGuildMembers,
    InvalidSession,
    Hello,
    HeartbeatAck,
});

pub mod close_codes {
    /// Unknown error; try reconnecting?
    ///
    /// Can reconnect.
    pub const UNKNOWN_ERROR: u16 = 4000;
    /// Invalid Gateway OP Code.
    ///
    /// Can resume.
    pub const UNKNOWN_OPCODE: u16 = 4001;
    /// An invalid payload was sent.
    ///
    /// Can resume.
    pub const DECODE_ERROR: u16 = 4002;
    /// A payload was sent prior to identifying.
    ///
    /// Cannot reconnect.
    pub const NOT_AUTHENTICATED: u16 = 4003;
    /// The account token sent with the identify payload was incorrect.
    ///
    /// Cannot reconnect.
    pub const AUTHENTICATION_FAILED: u16 = 4004;
    /// More than one identify payload was sent.
    ///
    /// Can reconnect.
    pub const ALREADY_AUTHENTICATED: u16 = 4005;
    /// The sequence sent when resuming the session was invalid.
    ///
    /// Can reconnect.
    pub const INVALID_SEQUENCE: u16 = 4007;
    /// Payloads were being sent too quickly.
    ///
    /// Can resume.
    pub const RATE_LIMITED: u16 = 4008;
    /// A session timed out.
    ///
    /// Can reconnect.
    pub const SESSION_TIMEOUT: u16 = 4009;
    /// An invalid shard when identifying was sent.
    ///
    /// Cannot reconnect.
    pub const INVALID_SHARD: u16 = 4010;
    /// The session would have handled too many guilds.
    ///
    /// Cannot reconnect.
    pub const SHARDING_REQUIRED: u16 = 4011;
    /// Undocumented gateway intents have been provided.
    pub const INVALID_GATEWAY_INTENTS: u16 = 4013;
    /// Disallowed gateway intents have been provided.
    pub const DISALLOWED_GATEWAY_INTENTS: u16 = 4014;
}
<commit_msg>Update `JOIN_MESSAGES` constant to be up to date (#1695)<commit_after>//! A set of constants used by the library.

/// The maximum length of the textual size of an embed.
pub const EMBED_MAX_LENGTH: usize = 6000;

/// The maximum number of embeds in a message.
pub const EMBED_MAX_COUNT: usize = 10;

/// The maximum number of stickers in a message.
pub const STICKER_MAX_COUNT: usize = 3;

/// The gateway version used by the library. The gateway URI is retrieved via
/// the REST API.
pub const GATEWAY_VERSION: u8 = 9;

/// The large threshold to send on identify.
pub const LARGE_THRESHOLD: u8 = 250;

/// The maximum unicode code points allowed within a message by Discord.
pub const MESSAGE_CODE_LIMIT: usize = 2000;

/// The maximum number of members the bot can fetch at once
pub const MEMBER_FETCH_LIMIT: u64 = 1000;

/// The [UserAgent] sent along with every request.
///
/// [UserAgent]: ::reqwest::header::USER_AGENT
pub const USER_AGENT: &str = concat!(
    "DiscordBot (https://github.com/serenity-rs/serenity, ",
    env!("CARGO_PKG_VERSION"),
    ")"
);

/// List of messages Discord shows on member join.
pub static JOIN_MESSAGES: &[&str] = &[
    "$user joined the party.",
    "$user is here.",
    "Welcome, $user. We hope you brought pizza.",
    "A wild $user appeared.",
    "$user just landed.",
    "$user just slid into the server.",
    "$user just showed up!",
    "Welcome $user. Say hi!",
    "$user hopped into the server.",
    "Everyone welcome $user!",
    "Glad you're here, $user.",
    "Good to see you, $user.",
    "Yay you made it, $user!",
];

/// Enum to map gateway opcodes.
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)]
#[non_exhaustive]
pub enum OpCode {
    /// Dispatches an event.
    Event = 0,
    /// Used for ping checking.
    Heartbeat = 1,
    /// Used for client handshake.
    Identify = 2,
    /// Used to update the client status.
    StatusUpdate = 3,
    /// Used to join/move/leave voice channels.
    VoiceStateUpdate = 4,
    /// Used for voice ping checking.
    VoiceServerPing = 5,
    /// Used to resume a closed connection.
    Resume = 6,
    /// Used to tell clients to reconnect to the gateway.
    Reconnect = 7,
    /// Used to request guild members.
    GetGuildMembers = 8,
    /// Used to notify clients that they have an invalid session Id.
    InvalidSession = 9,
    /// Sent immediately after connection, contains heartbeat + server info.
    Hello = 10,
    /// Sent immediately following a client heartbeat that was received.
    HeartbeatAck = 11,
    /// Unknown opcode.
    Unknown = !0,
}

enum_number!(OpCode {
    Event,
    Heartbeat,
    Identify,
    StatusUpdate,
    VoiceStateUpdate,
    VoiceServerPing,
    Resume,
    Reconnect,
    GetGuildMembers,
    InvalidSession,
    Hello,
    HeartbeatAck,
});

pub mod close_codes {
    /// Unknown error; try reconnecting?
    ///
    /// Can reconnect.
    pub const UNKNOWN_ERROR: u16 = 4000;
    /// Invalid Gateway OP Code.
    ///
    /// Can resume.
    pub const UNKNOWN_OPCODE: u16 = 4001;
    /// An invalid payload was sent.
    ///
    /// Can resume.
    pub const DECODE_ERROR: u16 = 4002;
    /// A payload was sent prior to identifying.
    ///
    /// Cannot reconnect.
    pub const NOT_AUTHENTICATED: u16 = 4003;
    /// The account token sent with the identify payload was incorrect.
    ///
    /// Cannot reconnect.
    pub const AUTHENTICATION_FAILED: u16 = 4004;
    /// More than one identify payload was sent.
    ///
    /// Can reconnect.
    pub const ALREADY_AUTHENTICATED: u16 = 4005;
    /// The sequence sent when resuming the session was invalid.
    ///
    /// Can reconnect.
    pub const INVALID_SEQUENCE: u16 = 4007;
    /// Payloads were being sent too quickly.
    ///
    /// Can resume.
    pub const RATE_LIMITED: u16 = 4008;
    /// A session timed out.
    ///
    /// Can reconnect.
    pub const SESSION_TIMEOUT: u16 = 4009;
    /// An invalid shard when identifying was sent.
    ///
    /// Cannot reconnect.
    pub const INVALID_SHARD: u16 = 4010;
    /// The session would have handled too many guilds.
    ///
    /// Cannot reconnect.
    pub const SHARDING_REQUIRED: u16 = 4011;
    /// Undocumented gateway intents have been provided.
    pub const INVALID_GATEWAY_INTENTS: u16 = 4013;
    /// Disallowed gateway intents have been provided.
    pub const DISALLOWED_GATEWAY_INTENTS: u16 = 4014;
}
<|endoftext|>


<commit_before>//! Mappings of objects received from the API, with optional helper methods for
//! ease of use.
//!
//! Models can optionally have additional helper methods compiled, by enabling
//! the `model` feature.

#[macro_use]
mod utils;

pub mod event;
pub mod permissions;

mod channel;
mod error;
mod gateway;
mod guild;
mod invite;
mod misc;
mod user;
mod voice;
mod webhook;

pub use self::channel::*;
pub use self::error::Error as ModelError;
pub use self::gateway::*;
pub use self::guild::*;
pub use self::invite::*;
pub use self::misc::*;
pub use self::permissions::Permissions;
pub use self::user::*;
pub use self::voice::*;
pub use self::webhook::*;

use chrono::NaiveDateTime;
use self::utils::*;
use serde::de::Visitor;
use std::collections::HashMap;
use std::fmt::{Display, Formatter, Result as FmtResult};
use std::sync::{Arc, RwLock};
use internal::prelude::*;

#[cfg(feature = "utils")]
use utils::Colour;

fn default_true() -> bool { true }

macro_rules! id_u64 {
    ($(#[$attr:meta] $name:ident;)*) => {
        $(
            #[$attr]
            #[derive(Copy, Clone, Debug, Eq, Hash, PartialOrd, Ord, Serialize)]
            #[allow(derive_hash_xor_eq)]
            pub struct $name(pub u64);

            impl $name {
                /// Retrieves the time that the Id was created at.
                pub fn created_at(&self) -> NaiveDateTime {
                    let offset = (self.0 >> 22) / 1000;

                    NaiveDateTime::from_timestamp(1_420_070_400 + offset as i64, 0)
                }
            }

            // This is a hack so that functions can accept `IntoIterator<Item=IdType>`, or `IntoIterator<Item=&IdType>`
            impl AsRef<$name> for $name {
                fn as_ref(&self) -> &Self {
                    self
                }
            }

            impl From<u64> for $name {
                fn from(id_as_u64: u64) -> $name {
                    $name(id_as_u64)
                }
            }

            impl PartialEq for $name {
                fn eq(&self, other: &Self) -> bool {
                    self.0 == other.0
                }
            }

            impl PartialEq<u64> for $name {
                fn eq(&self, u: &u64) -> bool {
                    self.0 == *u
                }
            }

            impl Display for $name {
                fn fmt(&self, f: &mut Formatter) -> FmtResult {
                    Display::fmt(&self.0, f)
                }
            }

            impl<'de> Deserialize<'de> for $name {
                fn deserialize<D: Deserializer<'de>>(deserializer: D) -> StdResult<Self, D::Error> {
                    deserializer.deserialize_u64(U64Visitor).map($name)
                }
            }
        )*
    }
}

id_u64! {
    /// An identifier for a Channel
    ChannelId;
    /// An identifier for an Emoji
    EmojiId;
    /// An identifier for a Guild
    GuildId;
    /// An identifier for an Integration
    IntegrationId;
    /// An identifier for a Message
    MessageId;
    /// An identifier for a Role
    RoleId;
    /// An identifier for a User
    UserId;
    /// An identifier for a [`Webhook`](struct.Webhook.html).
    WebhookId;
    /// An identifier for an audit log entry.
    AuditLogEntryId;
}

/// A container for guilds.
///
/// This is used to differentiate whether a guild itself can be used or whether
/// a guild needs to be retrieved from the cache.
#[allow(large_enum_variant)]
#[derive(Clone, Debug)]
pub enum GuildContainer {
    /// A guild which can have its contents directly searched.
    Guild(PartialGuild),
    /// A guild's id, which can be used to search the cache for a guild.
    Id(GuildId),
}

/// Information about a user's application. An application does not necessarily
/// have an associated bot user.
#[derive(Clone, Debug, Deserialize)]
pub struct ApplicationInfo {
    /// The bot user associated with the application. See [`BotApplication`] for
    /// more information.
    ///
    /// [`BotApplication`]: struct.BotApplication.html
    pub bot: Option<BotApplication>,
    /// Indicator of whether the bot is public.
    ///
    /// If a bot is public, anyone may invite it to their [`Guild`]. While a bot
    /// is private, only the owner may add it to a guild.
    ///
    /// [`Guild`]: struct.Guild.html
    #[serde(default = "default_true")]
    pub bot_public: bool,
    /// Indicator of whether the bot requires an OAuth2 code grant.
    pub bot_require_code_grant: bool,
    /// A description of the application, assigned by the application owner.
    pub description: String,
    /// A set of bitflags assigned to the application, which represent gated
    /// feature flags that have been enabled for the application.
    pub flags: Option<u64>,
    /// A hash pointing to the application's icon.
    ///
    /// This is not necessarily equivalent to the bot user's avatar.
    pub icon: Option<String>,
    /// The unique numeric Id of the application.
    pub id: UserId,
    /// The name assigned to the application by the application owner.
    pub name: String,
    /// A list of redirect URIs assigned to the application.
    pub redirect_uris: Vec<String>,
    /// A list of RPC Origins assigned to the application.
    pub rpc_origins: Vec<String>,
    /// The given secret to the application.
    ///
    /// This is not equivalent to the application's bot user's token.
    pub secret: String,
}

/// Information about an application with an application's bot user.
#[derive(Clone, Debug, Deserialize)]
pub struct BotApplication {
    /// The unique Id of the bot user.
    pub id: UserId,
    /// A hash of the avatar, if one is assigned.
    ///
    /// Can be used to generate a full URL to the avatar.
    pub avatar: Option<String>,
    /// Indicator of whether it is a bot.
    #[serde(default)]
    pub bot: bool,
    /// The discriminator assigned to the bot user.
    ///
    /// While discriminators are not unique, the `username#discriminator` pair
    /// is.
    pub discriminator: u16,
    /// The bot user's username.
    pub name: String,
    /// The token used to authenticate as the bot user.
    ///
    /// **Note**: Keep this information private, as untrusted sources can use it
    /// to perform any action with a bot user.
    pub token: String,
}

/// Information about the current application and its owner.
#[derive(Clone, Debug, Deserialize)]
pub struct CurrentApplicationInfo {
    pub description: String,
    pub icon: Option<String>,
    pub id: UserId,
    pub name: String,
    pub owner: User,
    #[serde(default)] pub rpc_origins: Vec<String>,
}

/// The name of a region that a voice server can be located in.
#[derive(Copy, Clone, Debug, Deserialize, Eq, Hash, PartialEq, PartialOrd, Ord, Serialize)]
pub enum Region {
    #[serde(rename = "amsterdam")] Amsterdam,
    #[serde(rename = "brazil")] Brazil,
    #[serde(rename = "eu-central")] EuCentral,
    #[serde(rename = "eu-west")] EuWest,
    #[serde(rename = "frankfurt")] Frankfurt,
    #[serde(rename = "london")] London,
    #[serde(rename = "sydney")] Sydney,
    #[serde(rename = "us-central")] UsCentral,
    #[serde(rename = "us-east")] UsEast,
    #[serde(rename = "us-south")] UsSouth,
    #[serde(rename = "us-west")] UsWest,
    #[serde(rename = "vip-amsterdam")] VipAmsterdam,
    #[serde(rename = "vip-us-east")] VipUsEast,
    #[serde(rename = "vip-us-west")] VipUsWest,
}

impl Region {
    pub fn name(&self) -> &str {
        match *self {
            Region::Amsterdam => "amsterdam",
            Region::Brazil => "brazil",
            Region::EuCentral => "eu-central",
            Region::EuWest => "eu-west",
            Region::Frankfurt => "frankfurt",
            Region::London => "london",
            Region::Sydney => "sydney",
            Region::UsCentral => "us-central",
            Region::UsEast => "us-east",
            Region::UsSouth => "us-south",
            Region::UsWest => "us-west",
            Region::VipAmsterdam => "vip-amsterdam",
            Region::VipUsEast => "vip-us-east",
            Region::VipUsWest => "vip-us-west",
        }
    }
}

use serde::{Deserialize, Deserializer};
use std::result::Result as StdResult;

fn deserialize_sync_user<'de, D: Deserializer<'de>>(deserializer: D)
                                                    -> StdResult<Arc<RwLock<User>>, D::Error> {
    Ok(Arc::new(RwLock::new(User::deserialize(deserializer)?)))
}
<commit_msg>Add an and<commit_after>//! Mappings of objects received from the API, with optional helper methods for
//! ease of use.
//!
//! Models can optionally have additional helper methods compiled, by enabling
//! the `model` feature.

#[macro_use]
mod utils;

pub mod event;
pub mod permissions;

mod channel;
mod error;
mod gateway;
mod guild;
mod invite;
mod misc;
mod user;
mod voice;
mod webhook;

pub use self::channel::*;
pub use self::error::Error as ModelError;
pub use self::gateway::*;
pub use self::guild::*;
pub use self::invite::*;
pub use self::misc::*;
pub use self::permissions::Permissions;
pub use self::user::*;
pub use self::voice::*;
pub use self::webhook::*;

use chrono::NaiveDateTime;
use self::utils::*;
use serde::de::Visitor;
use std::collections::HashMap;
use std::fmt::{Display, Formatter, Result as FmtResult};
use std::sync::{Arc, RwLock};
use internal::prelude::*;

#[cfg(feature = "utils")]
use utils::Colour;

fn default_true() -> bool { true }

macro_rules! id_u64 {
    ($(#[$attr:meta] $name:ident;)*) => {
        $(
            #[$attr]
            #[derive(Copy, Clone, Debug, Eq, Hash, PartialOrd, Ord, Serialize)]
            #[allow(derive_hash_xor_eq)]
            pub struct $name(pub u64);

            impl $name {
                /// Retrieves the time that the Id was created at.
                pub fn created_at(&self) -> NaiveDateTime {
                    let offset = (self.0 >> 22) / 1000;

                    NaiveDateTime::from_timestamp(1_420_070_400 + offset as i64, 0)
                }
            }

            // This is a hack so that functions can accept `IntoIterator<Item=IdType>`, and or `IntoIterator<Item=&IdType>`
            impl AsRef<$name> for $name {
                fn as_ref(&self) -> &Self {
                    self
                }
            }

            impl From<u64> for $name {
                fn from(id_as_u64: u64) -> $name {
                    $name(id_as_u64)
                }
            }

            impl PartialEq for $name {
                fn eq(&self, other: &Self) -> bool {
                    self.0 == other.0
                }
            }

            impl PartialEq<u64> for $name {
                fn eq(&self, u: &u64) -> bool {
                    self.0 == *u
                }
            }

            impl Display for $name {
                fn fmt(&self, f: &mut Formatter) -> FmtResult {
                    Display::fmt(&self.0, f)
                }
            }

            impl<'de> Deserialize<'de> for $name {
                fn deserialize<D: Deserializer<'de>>(deserializer: D) -> StdResult<Self, D::Error> {
                    deserializer.deserialize_u64(U64Visitor).map($name)
                }
            }
        )*
    }
}

id_u64! {
    /// An identifier for a Channel
    ChannelId;
    /// An identifier for an Emoji
    EmojiId;
    /// An identifier for a Guild
    GuildId;
    /// An identifier for an Integration
    IntegrationId;
    /// An identifier for a Message
    MessageId;
    /// An identifier for a Role
    RoleId;
    /// An identifier for a User
    UserId;
    /// An identifier for a [`Webhook`](struct.Webhook.html).
    WebhookId;
    /// An identifier for an audit log entry.
    AuditLogEntryId;
}

/// A container for guilds.
///
/// This is used to differentiate whether a guild itself can be used or whether
/// a guild needs to be retrieved from the cache.
#[allow(large_enum_variant)]
#[derive(Clone, Debug)]
pub enum GuildContainer {
    /// A guild which can have its contents directly searched.
    Guild(PartialGuild),
    /// A guild's id, which can be used to search the cache for a guild.
    Id(GuildId),
}

/// Information about a user's application. An application does not necessarily
/// have an associated bot user.
#[derive(Clone, Debug, Deserialize)]
pub struct ApplicationInfo {
    /// The bot user associated with the application. See [`BotApplication`] for
    /// more information.
    ///
    /// [`BotApplication`]: struct.BotApplication.html
    pub bot: Option<BotApplication>,
    /// Indicator of whether the bot is public.
    ///
    /// If a bot is public, anyone may invite it to their [`Guild`]. While a bot
    /// is private, only the owner may add it to a guild.
    ///
    /// [`Guild`]: struct.Guild.html
    #[serde(default = "default_true")]
    pub bot_public: bool,
    /// Indicator of whether the bot requires an OAuth2 code grant.
    pub bot_require_code_grant: bool,
    /// A description of the application, assigned by the application owner.
    pub description: String,
    /// A set of bitflags assigned to the application, which represent gated
    /// feature flags that have been enabled for the application.
    pub flags: Option<u64>,
    /// A hash pointing to the application's icon.
    ///
    /// This is not necessarily equivalent to the bot user's avatar.
    pub icon: Option<String>,
    /// The unique numeric Id of the application.
    pub id: UserId,
    /// The name assigned to the application by the application owner.
    pub name: String,
    /// A list of redirect URIs assigned to the application.
    pub redirect_uris: Vec<String>,
    /// A list of RPC Origins assigned to the application.
    pub rpc_origins: Vec<String>,
    /// The given secret to the application.
    ///
    /// This is not equivalent to the application's bot user's token.
    pub secret: String,
}

/// Information about an application with an application's bot user.
#[derive(Clone, Debug, Deserialize)]
pub struct BotApplication {
    /// The unique Id of the bot user.
    pub id: UserId,
    /// A hash of the avatar, if one is assigned.
    ///
    /// Can be used to generate a full URL to the avatar.
    pub avatar: Option<String>,
    /// Indicator of whether it is a bot.
    #[serde(default)]
    pub bot: bool,
    /// The discriminator assigned to the bot user.
    ///
    /// While discriminators are not unique, the `username#discriminator` pair
    /// is.
    pub discriminator: u16,
    /// The bot user's username.
    pub name: String,
    /// The token used to authenticate as the bot user.
    ///
    /// **Note**: Keep this information private, as untrusted sources can use it
    /// to perform any action with a bot user.
    pub token: String,
}

/// Information about the current application and its owner.
#[derive(Clone, Debug, Deserialize)]
pub struct CurrentApplicationInfo {
    pub description: String,
    pub icon: Option<String>,
    pub id: UserId,
    pub name: String,
    pub owner: User,
    #[serde(default)] pub rpc_origins: Vec<String>,
}

/// The name of a region that a voice server can be located in.
#[derive(Copy, Clone, Debug, Deserialize, Eq, Hash, PartialEq, PartialOrd, Ord, Serialize)]
pub enum Region {
    #[serde(rename = "amsterdam")] Amsterdam,
    #[serde(rename = "brazil")] Brazil,
    #[serde(rename = "eu-central")] EuCentral,
    #[serde(rename = "eu-west")] EuWest,
    #[serde(rename = "frankfurt")] Frankfurt,
    #[serde(rename = "london")] London,
    #[serde(rename = "sydney")] Sydney,
    #[serde(rename = "us-central")] UsCentral,
    #[serde(rename = "us-east")] UsEast,
    #[serde(rename = "us-south")] UsSouth,
    #[serde(rename = "us-west")] UsWest,
    #[serde(rename = "vip-amsterdam")] VipAmsterdam,
    #[serde(rename = "vip-us-east")] VipUsEast,
    #[serde(rename = "vip-us-west")] VipUsWest,
}

impl Region {
    pub fn name(&self) -> &str {
        match *self {
            Region::Amsterdam => "amsterdam",
            Region::Brazil => "brazil",
            Region::EuCentral => "eu-central",
            Region::EuWest => "eu-west",
            Region::Frankfurt => "frankfurt",
            Region::London => "london",
            Region::Sydney => "sydney",
            Region::UsCentral => "us-central",
            Region::UsEast => "us-east",
            Region::UsSouth => "us-south",
            Region::UsWest => "us-west",
            Region::VipAmsterdam => "vip-amsterdam",
            Region::VipUsEast => "vip-us-east",
            Region::VipUsWest => "vip-us-west",
        }
    }
}

use serde::{Deserialize, Deserializer};
use std::result::Result as StdResult;

fn deserialize_sync_user<'de, D: Deserializer<'de>>(deserializer: D)
                                                    -> StdResult<Arc<RwLock<User>>, D::Error> {
    Ok(Arc::new(RwLock::new(User::deserialize(deserializer)?)))
}
<|endoftext|>


<commit_before>/* Copyright 2020 The TensorFlow Authors. All Rights Reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==============================================================================*/

//! Command-line interface for the main entry point.

use clap::Clap;
use log::{debug, error, info, LevelFilter};
use std::fs::File;
use std::io::{Read, Write};
use std::net::{IpAddr, SocketAddr};
use std::path::{Path, PathBuf};
use std::str::FromStr;
use std::sync::Arc;
use std::thread;
use std::time::{Duration, Instant};
use tokio::net::TcpListener;
use tokio_stream::wrappers::TcpListenerStream;
use tonic::transport::Server;

use crate::commit::Commit;
use crate::logdir::LogdirLoader;
use crate::proto::tensorboard::data;
use crate::server::DataProviderHandler;
use crate::types::PluginSamplingHint;

use data::tensor_board_data_provider_server::TensorBoardDataProviderServer;

pub mod dynamic_logdir;
use dynamic_logdir::DynLogdir;

#[derive(Clap, Debug)]
#[clap(name = "rustboard", version = crate::VERSION)]
struct Opts {
    /// Log directory to load
    ///
    /// Directory to recursively scan for event files (files matching the `*tfevents*` glob). This
    /// directory, its descendants, and its event files will be periodically polled for new data.
    ///
    /// If this log directory is invalid or unsupported, exits with status 8.
    #[clap(long, setting(clap::ArgSettings::AllowEmptyValues))]
    logdir: PathBuf,

    /// Bind to this IP address
    ///
    /// IP address to bind this server to. May be an IPv4 address (e.g., 127.0.0.1 or 0.0.0.0) or
    /// an IPv6 address (e.g., ::1 or ::0).
    #[clap(long, default_value = "::1")]
    host: IpAddr,

    /// Bind to this port
    ///
    /// Port to bind this server to. Use `0` to request an arbitrary free port from the OS.
    #[clap(long, default_value = "6806")]
    port: u16,

    /// Seconds to sleep between reloads, or "once"
    ///
    /// Number of seconds to wait between finishing one load cycle and starting the next one. This
    /// does not include the time for the reload itself. If "once", data will be loaded only once.
    #[clap(long, default_value = "5", value_name = "secs")]
    reload: ReloadStrategy,

    /// Use verbose output (-vv for very verbose output)
    #[clap(long = "verbose", short, parse(from_occurrences))]
    verbosity: u32,

    /// Kill this server once stdin is closed
    ///
    /// While this server is running, read stdin to end of file and then kill the server. Used to
    /// portably ensure that the server exits when the parent process dies, even due to a crash.
    /// Don't set this if stdin is connected to a tty and the process will be backgrounded, since
    /// then the server will receive `SIGTTIN` and its process will be stopped (in the `SIGSTOP`
    /// sense) but not killed.
    #[clap(long)]
    die_after_stdin: bool,

    /// Write bound port to this file
    ///
    /// Once a server socket is opened, write the port on which it's listening to the file at this
    /// path. Useful with `--port 0`. Port will be written as ASCII decimal followed by a newline
    /// (e.g., "6806\n"). If the server fails to start, this file may not be written at all. If the
    /// port file is specified but cannot be written, the server will die.
    ///
    /// This also suppresses the "listening on HOST:PORT" line that is otherwise written to stderr
    /// when the server starts.
    #[clap(long)]
    port_file: Option<PathBuf>,

    /// Write startup errors to this file
    ///
    /// If the logdir is invalid or unsupported, write the error message to this file instead of to
    /// stderr. That way, you can capture this output while still keeping stderr open for normal
    /// logging.
    #[clap(long)]
    error_file: Option<PathBuf>,

    /// Checksum all records (negate with `--no-checksum`)
    ///
    /// With `--checksum`, every record will be checksummed before being parsed. With
    /// `--no-checksum` (the default), records are only checksummed if parsing fails. Skipping
    /// checksums for records that successfully parse can be significantly faster, but also means
    /// that some bit flips may not be detected.
    #[clap(long, multiple_occurrences = true, overrides_with = "no_checksum")]
    checksum: bool,

    /// Only checksum records that fail to parse
    ///
    /// Negates `--checksum`. This is the default.
    #[clap(
        long,
        multiple_occurrences = true,
        overrides_with = "checksum",
        hidden = true
    )]
    #[allow(unused)]
    no_checksum: bool,

    /// Set explicit series sampling
    ///
    /// A comma separated list of `plugin_name=num_samples` pairs to explicitly specify how many
    /// samples to keep per tag for the specified plugin. For unspecified plugins, series are
    /// randomly downsampled to reasonable values to prevent out-of-memory errors in long-running
    /// jobs. Each `num_samples` may be the special token `all` to retain all data without
    /// downsampling. For instance, `--samples_per_plugin=scalars=500,images=all,audio=0` keeps 500
    /// events in each scalar series, all of the images, and none of the audio.
    #[clap(long, default_value = "", setting(clap::ArgSettings::AllowEmptyValues))]
    samples_per_plugin: PluginSamplingHint,
}

#[derive(Debug, Copy, Clone, PartialEq, Eq)]
enum ReloadStrategy {
    Loop { delay: Duration },
    Once,
}
impl FromStr for ReloadStrategy {
    type Err = <u64 as FromStr>::Err;
    fn from_str(s: &str) -> Result<Self, Self::Err> {
        if s == "once" {
            Ok(ReloadStrategy::Once)
        } else {
            Ok(ReloadStrategy::Loop {
                delay: Duration::from_secs(s.parse()?),
            })
        }
    }
}

/// Exit code for failure of [`DynLogdir::new`].
// Keep in sync with docs on `Opts::logdir`.
const EXIT_BAD_LOGDIR: i32 = 8;

#[tokio::main]
pub async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let opts = Opts::parse();
    init_logging(match opts.verbosity {
        0 => LevelFilter::Warn,
        1 => LevelFilter::Info,
        _ => LevelFilter::max(),
    });
    debug!("Parsed options: {:?}", opts);
    let data_location = opts.logdir.display().to_string();

    // Create the logdir outside an async runtime (see docs for `DynLogdir::new`).
    let (raw_logdir, error_file) = (opts.logdir, opts.error_file);
    let logdir = tokio::task::spawn_blocking(|| DynLogdir::new(raw_logdir))
        .await?
        .unwrap_or_else(|e| {
            write_startup_error(error_file.as_ref().map(|p| p.as_ref()), e);
            std::process::exit(EXIT_BAD_LOGDIR);
        });

    if opts.die_after_stdin {
        thread::Builder::new()
            .name("StdinWatcher".to_string())
            .spawn(die_after_stdin)
            .expect("failed to spawn stdin watcher thread");
    }

    let addr = SocketAddr::new(opts.host, opts.port);
    let listener = TcpListener::bind(addr).await?;
    let bound = listener.local_addr()?;

    if let Some(port_file) = opts.port_file {
        let port = bound.port();
        if let Err(e) = write_port_file(&port_file, port) {
            error!(
                "Failed to write port \"{}\" to {}: {}",
                port,
                port_file.display(),
                e
            );
            std::process::exit(1);
        }
        info!("Wrote port \"{}\" to {}", port, port_file.display());
    } else {
        eprintln!("listening on {:?}", bound);
    }

    // Leak the commit object, since the Tonic server must have only 'static references. This only
    // leaks the outer commit structure (of constant size), not the pointers to the actual data.
    let commit: &'static Commit = Box::leak(Box::new(Commit::new()));
    let psh_ref = Arc::new(opts.samples_per_plugin);
    thread::Builder::new()
        .name("Reloader".to_string())
        .spawn({
            let reload_strategy = opts.reload;
            let checksum = opts.checksum;
            move || {
                let mut loader = LogdirLoader::new(commit, logdir, 0, psh_ref);
                // Checksum only if `--checksum` given (i.e., off by default).
                loader.checksum(checksum);
                loop {
                    info!("Starting load cycle");
                    let start = Instant::now();
                    loader.reload();
                    let end = Instant::now();
                    info!("Finished load cycle ({:?})", end - start);
                    match reload_strategy {
                        ReloadStrategy::Loop { delay } => thread::sleep(delay),
                        ReloadStrategy::Once => break,
                    };
                }
            }
        })
        .expect("failed to spawn reloader thread");

    let handler = DataProviderHandler {
        data_location,
        commit,
    };
    Server::builder()
        .add_service(TensorBoardDataProviderServer::new(handler))
        .serve_with_incoming(TcpListenerStream::new(listener))
        .await?;
    Ok(())
}

/// Installs a logging handler whose behavior is determined by the `RUST_LOG` environment variable
/// (per <https://docs.rs/env_logger> semantics), or by including all logs at `default_log_level`
/// or above if `RUST_LOG_LEVEL` is not given.
fn init_logging(default_log_level: LevelFilter) {
    use env_logger::{Builder, Env};
    Builder::from_env(Env::default().default_filter_or(default_log_level.to_string())).init();
}

/// Locks stdin and reads it to EOF, then exits the process.
fn die_after_stdin() {
    let stdin = std::io::stdin();
    let stdin_lock = stdin.lock();
    for _ in stdin_lock.bytes() {}
    info!("Stdin closed; exiting");
    std::process::exit(0);
}

/// Writes `port` to file `path` as an ASCII decimal followed by newline.
fn write_port_file(path: &Path, port: u16) -> std::io::Result<()> {
    let mut f = File::create(path)?;
    writeln!(f, "{}", port)?;
    f.sync_all()?;
    Ok(())
}

/// Writes error to the given file, or to stderr as a fallback.
fn write_startup_error(path: Option<&Path>, error: dynamic_logdir::Error) {
    let write_to_file = |path: &Path| -> std::io::Result<()> {
        let mut f = File::create(path)?;
        writeln!(f, "{}", error)?;
        f.sync_all()?;
        Ok(())
    };
    if let Some(p) = path {
        if let Err(e) = write_to_file(p) {
            info!("Failed to write error to {:?}: {}", p, e);
        } else {
            return;
        }
    }
    // fall back to stderr if no path given or if write failed
    eprintln!("fatal: {}", error);
}

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

    #[test]
    fn test_parse_reload() {
        assert_eq!("once".parse::<ReloadStrategy>(), Ok(ReloadStrategy::Once));
        assert_eq!(
            "5".parse::<ReloadStrategy>(),
            Ok(ReloadStrategy::Loop {
                delay: Duration::from_secs(5)
            })
        );
        "5s".parse::<ReloadStrategy>()
            .expect_err("explicit \"s\" trailer should be forbidden");
    }
}
<commit_msg>rust: support and default to `--host localhost` (#4804)<commit_after>/* Copyright 2020 The TensorFlow Authors. All Rights Reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==============================================================================*/

//! Command-line interface for the main entry point.

use clap::Clap;
use log::{debug, error, info, LevelFilter};
use std::fs::File;
use std::io::{Read, Write};
use std::path::{Path, PathBuf};
use std::str::FromStr;
use std::sync::Arc;
use std::thread;
use std::time::{Duration, Instant};
use tokio::net::TcpListener;
use tokio_stream::wrappers::TcpListenerStream;
use tonic::transport::Server;

use crate::commit::Commit;
use crate::logdir::LogdirLoader;
use crate::proto::tensorboard::data;
use crate::server::DataProviderHandler;
use crate::types::PluginSamplingHint;

use data::tensor_board_data_provider_server::TensorBoardDataProviderServer;

pub mod dynamic_logdir;
use dynamic_logdir::DynLogdir;

#[derive(Clap, Debug)]
#[clap(name = "rustboard", version = crate::VERSION)]
struct Opts {
    /// Log directory to load
    ///
    /// Directory to recursively scan for event files (files matching the `*tfevents*` glob). This
    /// directory, its descendants, and its event files will be periodically polled for new data.
    ///
    /// If this log directory is invalid or unsupported, exits with status 8.
    #[clap(long, setting(clap::ArgSettings::AllowEmptyValues))]
    logdir: PathBuf,

    /// Bind to this host name
    ///
    /// Host to bind this server to. May be an IPv4 address (e.g., 127.0.0.1 or 0.0.0.0), an IPv6
    /// address (e.g., ::1 or ::0), or a string like `localhost` to pass to `getaddrinfo(3)`.
    #[clap(long, default_value = "localhost")]
    host: String,

    /// Bind to this port
    ///
    /// Port to bind this server to. Use `0` to request an arbitrary free port from the OS.
    #[clap(long, default_value = "6806")]
    port: u16,

    /// Seconds to sleep between reloads, or "once"
    ///
    /// Number of seconds to wait between finishing one load cycle and starting the next one. This
    /// does not include the time for the reload itself. If "once", data will be loaded only once.
    #[clap(long, default_value = "5", value_name = "secs")]
    reload: ReloadStrategy,

    /// Use verbose output (-vv for very verbose output)
    #[clap(long = "verbose", short, parse(from_occurrences))]
    verbosity: u32,

    /// Kill this server once stdin is closed
    ///
    /// While this server is running, read stdin to end of file and then kill the server. Used to
    /// portably ensure that the server exits when the parent process dies, even due to a crash.
    /// Don't set this if stdin is connected to a tty and the process will be backgrounded, since
    /// then the server will receive `SIGTTIN` and its process will be stopped (in the `SIGSTOP`
    /// sense) but not killed.
    #[clap(long)]
    die_after_stdin: bool,

    /// Write bound port to this file
    ///
    /// Once a server socket is opened, write the port on which it's listening to the file at this
    /// path. Useful with `--port 0`. Port will be written as ASCII decimal followed by a newline
    /// (e.g., "6806\n"). If the server fails to start, this file may not be written at all. If the
    /// port file is specified but cannot be written, the server will die.
    ///
    /// This also suppresses the "listening on HOST:PORT" line that is otherwise written to stderr
    /// when the server starts.
    #[clap(long)]
    port_file: Option<PathBuf>,

    /// Write startup errors to this file
    ///
    /// If the logdir is invalid or unsupported, write the error message to this file instead of to
    /// stderr. That way, you can capture this output while still keeping stderr open for normal
    /// logging.
    #[clap(long)]
    error_file: Option<PathBuf>,

    /// Checksum all records (negate with `--no-checksum`)
    ///
    /// With `--checksum`, every record will be checksummed before being parsed. With
    /// `--no-checksum` (the default), records are only checksummed if parsing fails. Skipping
    /// checksums for records that successfully parse can be significantly faster, but also means
    /// that some bit flips may not be detected.
    #[clap(long, multiple_occurrences = true, overrides_with = "no_checksum")]
    checksum: bool,

    /// Only checksum records that fail to parse
    ///
    /// Negates `--checksum`. This is the default.
    #[clap(
        long,
        multiple_occurrences = true,
        overrides_with = "checksum",
        hidden = true
    )]
    #[allow(unused)]
    no_checksum: bool,

    /// Set explicit series sampling
    ///
    /// A comma separated list of `plugin_name=num_samples` pairs to explicitly specify how many
    /// samples to keep per tag for the specified plugin. For unspecified plugins, series are
    /// randomly downsampled to reasonable values to prevent out-of-memory errors in long-running
    /// jobs. Each `num_samples` may be the special token `all` to retain all data without
    /// downsampling. For instance, `--samples_per_plugin=scalars=500,images=all,audio=0` keeps 500
    /// events in each scalar series, all of the images, and none of the audio.
    #[clap(long, default_value = "", setting(clap::ArgSettings::AllowEmptyValues))]
    samples_per_plugin: PluginSamplingHint,
}

#[derive(Debug, Copy, Clone, PartialEq, Eq)]
enum ReloadStrategy {
    Loop { delay: Duration },
    Once,
}
impl FromStr for ReloadStrategy {
    type Err = <u64 as FromStr>::Err;
    fn from_str(s: &str) -> Result<Self, Self::Err> {
        if s == "once" {
            Ok(ReloadStrategy::Once)
        } else {
            Ok(ReloadStrategy::Loop {
                delay: Duration::from_secs(s.parse()?),
            })
        }
    }
}

/// Exit code for failure of [`DynLogdir::new`].
// Keep in sync with docs on `Opts::logdir`.
const EXIT_BAD_LOGDIR: i32 = 8;
const EXIT_FAILED_TO_BIND: i32 = 9;

#[tokio::main]
pub async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let opts = Opts::parse();
    init_logging(match opts.verbosity {
        0 => LevelFilter::Warn,
        1 => LevelFilter::Info,
        _ => LevelFilter::max(),
    });
    debug!("Parsed options: {:?}", opts);
    let data_location = opts.logdir.display().to_string();

    let error_file_path = opts.error_file.as_ref().map(PathBuf::as_ref);

    // Create the logdir outside an async runtime (see docs for `DynLogdir::new`).
    let raw_logdir = opts.logdir;
    let logdir = tokio::task::spawn_blocking(|| DynLogdir::new(raw_logdir))
        .await?
        .unwrap_or_else(|e| {
            write_startup_error(error_file_path, &e.to_string());
            std::process::exit(EXIT_BAD_LOGDIR);
        });

    if opts.die_after_stdin {
        thread::Builder::new()
            .name("StdinWatcher".to_string())
            .spawn(die_after_stdin)
            .expect("failed to spawn stdin watcher thread");
    }

    let addr = (opts.host.as_str(), opts.port);
    let listener = TcpListener::bind(addr).await.unwrap_or_else(|e| {
        let msg = format!("failed to bind to {:?}: {}", addr, e);
        write_startup_error(error_file_path, &msg);
        std::process::exit(EXIT_FAILED_TO_BIND);
    });
    let bound = listener.local_addr()?;

    if let Some(port_file) = opts.port_file {
        let port = bound.port();
        if let Err(e) = write_port_file(&port_file, port) {
            error!(
                "Failed to write port \"{}\" to {}: {}",
                port,
                port_file.display(),
                e
            );
            std::process::exit(1);
        }
        info!("Wrote port \"{}\" to {}", port, port_file.display());
    } else {
        eprintln!("listening on {:?}", bound);
    }

    // Leak the commit object, since the Tonic server must have only 'static references. This only
    // leaks the outer commit structure (of constant size), not the pointers to the actual data.
    let commit: &'static Commit = Box::leak(Box::new(Commit::new()));
    let psh_ref = Arc::new(opts.samples_per_plugin);
    thread::Builder::new()
        .name("Reloader".to_string())
        .spawn({
            let reload_strategy = opts.reload;
            let checksum = opts.checksum;
            move || {
                let mut loader = LogdirLoader::new(commit, logdir, 0, psh_ref);
                // Checksum only if `--checksum` given (i.e., off by default).
                loader.checksum(checksum);
                loop {
                    info!("Starting load cycle");
                    let start = Instant::now();
                    loader.reload();
                    let end = Instant::now();
                    info!("Finished load cycle ({:?})", end - start);
                    match reload_strategy {
                        ReloadStrategy::Loop { delay } => thread::sleep(delay),
                        ReloadStrategy::Once => break,
                    };
                }
            }
        })
        .expect("failed to spawn reloader thread");

    let handler = DataProviderHandler {
        data_location,
        commit,
    };
    Server::builder()
        .add_service(TensorBoardDataProviderServer::new(handler))
        .serve_with_incoming(TcpListenerStream::new(listener))
        .await?;
    Ok(())
}

/// Installs a logging handler whose behavior is determined by the `RUST_LOG` environment variable
/// (per <https://docs.rs/env_logger> semantics), or by including all logs at `default_log_level`
/// or above if `RUST_LOG_LEVEL` is not given.
fn init_logging(default_log_level: LevelFilter) {
    use env_logger::{Builder, Env};
    Builder::from_env(Env::default().default_filter_or(default_log_level.to_string())).init();
}

/// Locks stdin and reads it to EOF, then exits the process.
fn die_after_stdin() {
    let stdin = std::io::stdin();
    let stdin_lock = stdin.lock();
    for _ in stdin_lock.bytes() {}
    info!("Stdin closed; exiting");
    std::process::exit(0);
}

/// Writes `port` to file `path` as an ASCII decimal followed by newline.
fn write_port_file(path: &Path, port: u16) -> std::io::Result<()> {
    let mut f = File::create(path)?;
    writeln!(f, "{}", port)?;
    f.sync_all()?;
    Ok(())
}

/// Writes error to the given file, or to stderr as a fallback.
fn write_startup_error(path: Option<&Path>, error: &str) {
    let write_to_file = |path: &Path| -> std::io::Result<()> {
        let mut f = File::create(path)?;
        writeln!(f, "{}", error)?;
        f.sync_all()?;
        Ok(())
    };
    if let Some(p) = path {
        if let Err(e) = write_to_file(p) {
            info!("Failed to write error to {:?}: {}", p, e);
        } else {
            return;
        }
    }
    // fall back to stderr if no path given or if write failed
    eprintln!("fatal: {}", error);
}

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

    #[test]
    fn test_parse_reload() {
        assert_eq!("once".parse::<ReloadStrategy>(), Ok(ReloadStrategy::Once));
        assert_eq!(
            "5".parse::<ReloadStrategy>(),
            Ok(ReloadStrategy::Loop {
                delay: Duration::from_secs(5)
            })
        );
        "5s".parse::<ReloadStrategy>()
            .expect_err("explicit \"s\" trailer should be forbidden");
    }
}
<|endoftext|>


<commit_before>extern crate find_folder;

use screen;
use screen::Screen;

use piston_window;

type Screens = Vec<Box<screen::Screen>>;

pub struct Game {
    window: piston_window::PistonWindow,
    context: screen::Context,
    screens: Screens,
}

impl Game {
    pub fn new() -> Game {
        let window: piston_window::PistonWindow = piston_window::WindowSettings::new("rrthozopsi", [600, 600])
                                                      .exit_on_esc(true)
                                                      .build()
                                                      .unwrap();
        let mut context = screen::Context {
            assets_path: find_folder::Search::KidsThenParents(3, 5).for_folder("assets").unwrap(),
        };
        let screens: Screens = vec![Box::new(screen::MainMenuScreen::new(&window, &mut context))];
        Game {
            window: window,
            screens: screens,
            context: context,
        }
    }

    pub fn run(&mut self) {
        use piston_window::Event;

        let screens = &mut self.screens;

        // TODO: Remove clone?
        for window in &mut self.window {
            match window.event {
                Some(Event::Update(ref args)) => {
                    last(screens).on_update(&args);
                }
                Some(Event::Render(ref args)) => {
                    last(screens).on_draw(&args, &window);
                }
                Some(Event::Input(ref input)) => {
                    handle_input(screens, &input, &window, &mut self.context);
                    if screens.is_empty() {
                        // No screens - exit game.
                        return
                    }
                }
                _ => {}
            }
        }
    }
}

fn last<'a>(vec: &'a mut Screens) -> &'a mut Box<screen::Screen> {
    vec.last_mut().unwrap()
}

fn handle_input(screens: &mut Screens,
                input: &piston_window::Input,
                window: &piston_window::PistonWindow,
                context: &mut screen::Context) {
    for action in last(screens).on_input(&input, &window, context) {
        match action {
            screen::InputResult::PushScreen(new_screen) => {
                screens.push(new_screen);
            }
            screen::InputResult::PopScreen => {
                screens.pop();
            }
        }
    }
}
<commit_msg>exit_on_esc has been removed<commit_after>extern crate find_folder;

use screen;
use screen::Screen;

use piston_window;

type Screens = Vec<Box<screen::Screen>>;

pub struct Game {
    window: piston_window::PistonWindow,
    context: screen::Context,
    screens: Screens,
}

impl Game {
    pub fn new() -> Game {
        let window: piston_window::PistonWindow = piston_window::WindowSettings::new("rrthozopsi", [600, 600])
                                                      .build()
                                                      .unwrap();
        let mut context = screen::Context {
            assets_path: find_folder::Search::KidsThenParents(3, 5).for_folder("assets").unwrap(),
        };
        let screens: Screens = vec![Box::new(screen::MainMenuScreen::new(&window, &mut context))];
        Game {
            window: window,
            screens: screens,
            context: context,
        }
    }

    pub fn run(&mut self) {
        use piston_window::Event;

        let screens = &mut self.screens;

        // TODO: Remove clone?
        for window in &mut self.window {
            match window.event {
                Some(Event::Update(ref args)) => {
                    last(screens).on_update(&args);
                }
                Some(Event::Render(ref args)) => {
                    last(screens).on_draw(&args, &window);
                }
                Some(Event::Input(ref input)) => {
                    handle_input(screens, &input, &window, &mut self.context);
                    if screens.is_empty() {
                        // No screens - exit game.
                        return
                    }
                }
                _ => {}
            }
        }
    }
}

fn last<'a>(vec: &'a mut Screens) -> &'a mut Box<screen::Screen> {
    vec.last_mut().unwrap()
}

fn handle_input(screens: &mut Screens,
                input: &piston_window::Input,
                window: &piston_window::PistonWindow,
                context: &mut screen::Context) {
    for action in last(screens).on_input(&input, &window, context) {
        match action {
            screen::InputResult::PushScreen(new_screen) => {
                screens.push(new_screen);
            }
            screen::InputResult::PopScreen => {
                screens.pop();
            }
        }
    }
}
<|endoftext|>


<commit_before>// Copyright 2018 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

extern crate cargo;
extern crate cargo_raze;
extern crate docopt;
extern crate failure;
#[cfg(test)]
#[macro_use]
extern crate hamcrest;
extern crate itertools;
extern crate serde;
#[macro_use]
extern crate serde_derive;
extern crate serde_json;
extern crate slug;
extern crate tempdir;
extern crate tera;
extern crate toml;

use std::fs;
use std::fs::File;
use std::io::Read;
use std::io::Write;
use std::path::Path;
use std::path::PathBuf;

use cargo::CargoError;
use cargo::CliResult;
use cargo::util::CargoResult;
use cargo::util::Config;
use docopt::Docopt;

use bazel::BazelRenderer;
use cargo_raze::bazel;
use cargo_raze::metadata;
use cargo_raze::planning;
use cargo_raze::rendering;
use cargo_raze::settings;
use cargo_raze::util;
use metadata::CargoInternalsMetadataFetcher;
use metadata::CargoWorkspaceFiles;
use planning::BuildPlanner;
use planning::BuildPlannerImpl;
use rendering::BuildRenderer;
use rendering::FileOutputs;
use rendering::RenderDetails;
use settings::GenMode;
use settings::RazeSettings;
use util::PlatformDetails;
use util::RazeError;

#[derive(Debug, Deserialize)]
struct Options {
  arg_buildprefix: Option<String>,
  flag_verbose: u32,
  flag_quiet: Option<bool>,
  flag_host: Option<String>,
  flag_color: Option<String>,
  flag_target: Option<String>,
  flag_dryrun: Option<bool>,
}

const USAGE: &'static str = r#"
Generate BUILD files for your pre-vendored Cargo dependencies.

Usage:
    cargo raze

Options:
    -h, --help                Print this message
    -v, --verbose             Use verbose output
    -q, --quiet               No output printed to stdout
    --color WHEN              Coloring: auto, always, never
    -d, --dryrun              Do not emit any files
"#;

fn main() {
  let mut config = Config::default().unwrap();

  let options = Docopt::new(USAGE)
    .and_then(|d| d.deserialize())
    .unwrap_or_else(|e| e.exit());

  let result = real_main(options, &mut config);

  if let Err(e) = result {
    cargo::exit_with_error(e, &mut *config.shell());
  }
}

fn real_main(options: Options, cargo_config: &mut Config) -> CliResult {
  try!(cargo_config.configure(
    options.flag_verbose,
    options.flag_quiet,
    &options.flag_color,
    /* frozen = */ false,
    /* locked = */ false,
    /* target_dir = */ &None,
    &[]
  ));
  let mut settings = try!(load_settings("Cargo.toml"));
  println!("Loaded override settings: {:#?}", settings);

  try!(validate_settings(&mut settings));

  let mut metadata_fetcher = CargoInternalsMetadataFetcher::new(&cargo_config);
  let mut planner = BuildPlannerImpl::new(&mut metadata_fetcher);

  let toml_path = PathBuf::from("./Cargo.toml");
  let mut lock_path_opt = None;
  if fs::metadata("./Cargo.lock").is_ok() {
    lock_path_opt = Some(PathBuf::from("./Cargo.lock"));
  }
  let files = CargoWorkspaceFiles {
    toml_path: toml_path,
    lock_path_opt: lock_path_opt,
  };
  let platform_details = try!(PlatformDetails::new_using_rustc(&settings.target));
  let planned_build = try!(planner
    .plan_build(&settings, files, platform_details));
  let mut bazel_renderer = BazelRenderer::new();
  let render_details = RenderDetails {
    path_prefix: "./".to_owned(),
    buildfile_suffix: settings.output_buildfile_suffix,
  };

  let bazel_file_outputs = match settings.genmode {
    GenMode::Vendored => try!(bazel_renderer.render_planned_build(&render_details, &planned_build)),
    GenMode::Remote => {
      // Create "remote/" if it doesn't exist
      if fs::metadata("remote/").is_err() {
        try!(fs::create_dir("remote/").map_err(|e| CargoError::from(e)));
      }

      try!(bazel_renderer.render_remote_planned_build(&render_details, &planned_build))
    } /* exhaustive, we control the definition */
  };

  let dry_run = options.flag_dryrun.unwrap_or(false);
  for FileOutputs { path, contents } in bazel_file_outputs {
    if !dry_run {
      try!(write_to_file_loudly(&path, &contents));
    } else {
      println!("{}:\n{}", path, contents);
    }
  }

  Ok(())
}

/** Verifies that the provided settings make sense. */
fn validate_settings(settings: &mut RazeSettings) -> CargoResult<()> {
  if !settings.workspace_path.starts_with("//") {
    return Err(CargoError::from(RazeError::Config {
          field_path_opt: Some("raze.workspace_path".to_owned()),
          message: format!("Path must start with \"//\". Paths into local repositories (such as \
                           @local//path) are currently unsupported.")
    }));
  }

  if settings.workspace_path != "//" && settings.workspace_path.ends_with("/") {
    settings.workspace_path.pop();
  }

  return Ok(());
}

fn write_to_file_loudly(path: &str, contents: &str) -> CargoResult<()> {
  try!(
    File::create(&path)
      .and_then(|mut f| f.write_all(contents.as_bytes()))
      .map_err(CargoError::from));
  println!("Generated {} successfully", path);
  Ok(())
}

fn load_settings<T: AsRef<Path>>(cargo_toml_path: T) -> Result<RazeSettings, CargoError> {
  let path = cargo_toml_path.as_ref();
  let mut toml = try!(File::open(path).map_err(CargoError::from));
  let mut toml_contents = String::new();
  try!(toml.read_to_string(&mut toml_contents).map_err(CargoError::from));
  toml::from_str::<settings::CargoToml>(&toml_contents)
    .map_err(CargoError::from)
    .map(|toml| toml.raze)
}
<commit_msg>Fix docopt's usage (#118)<commit_after>// Copyright 2018 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

extern crate cargo;
extern crate cargo_raze;
extern crate docopt;
extern crate failure;
#[cfg(test)]
#[macro_use]
extern crate hamcrest;
extern crate itertools;
extern crate serde;
#[macro_use]
extern crate serde_derive;
extern crate serde_json;
extern crate slug;
extern crate tempdir;
extern crate tera;
extern crate toml;

use std::fs;
use std::fs::File;
use std::io::Read;
use std::io::Write;
use std::path::Path;
use std::path::PathBuf;

use cargo::CargoError;
use cargo::CliResult;
use cargo::util::CargoResult;
use cargo::util::Config;
use docopt::Docopt;

use bazel::BazelRenderer;
use cargo_raze::bazel;
use cargo_raze::metadata;
use cargo_raze::planning;
use cargo_raze::rendering;
use cargo_raze::settings;
use cargo_raze::util;
use metadata::CargoInternalsMetadataFetcher;
use metadata::CargoWorkspaceFiles;
use planning::BuildPlanner;
use planning::BuildPlannerImpl;
use rendering::BuildRenderer;
use rendering::FileOutputs;
use rendering::RenderDetails;
use settings::GenMode;
use settings::RazeSettings;
use util::PlatformDetails;
use util::RazeError;

#[derive(Debug, Deserialize)]
struct Options {
  arg_buildprefix: Option<String>,
  flag_verbose: u32,
  flag_quiet: Option<bool>,
  flag_host: Option<String>,
  flag_color: Option<String>,
  flag_target: Option<String>,
  flag_dryrun: Option<bool>,
}

const USAGE: &'static str = r#"
Generate BUILD files for your pre-vendored Cargo dependencies.

Usage:
    cargo raze [--help --verbose --quiet --color WHEN --dryrun]

Options:
    -h, --help                Print this message
    -v, --verbose             Use verbose output
    -q, --quiet               No output printed to stdout
    --color WHEN              Coloring: auto, always, never
    -d, --dryrun              Do not emit any files
"#;

fn main() {
  let mut config = Config::default().unwrap();

  let options = Docopt::new(USAGE)
    .and_then(|d| d.deserialize())
    .unwrap_or_else(|e| e.exit());

  let result = real_main(options, &mut config);

  if let Err(e) = result {
    cargo::exit_with_error(e, &mut *config.shell());
  }
}

fn real_main(options: Options, cargo_config: &mut Config) -> CliResult {
  try!(cargo_config.configure(
    options.flag_verbose,
    options.flag_quiet,
    &options.flag_color,
    /* frozen = */ false,
    /* locked = */ false,
    /* target_dir = */ &None,
    &[]
  ));
  let mut settings = try!(load_settings("Cargo.toml"));
  println!("Loaded override settings: {:#?}", settings);

  try!(validate_settings(&mut settings));

  let mut metadata_fetcher = CargoInternalsMetadataFetcher::new(&cargo_config);
  let mut planner = BuildPlannerImpl::new(&mut metadata_fetcher);

  let toml_path = PathBuf::from("./Cargo.toml");
  let mut lock_path_opt = None;
  if fs::metadata("./Cargo.lock").is_ok() {
    lock_path_opt = Some(PathBuf::from("./Cargo.lock"));
  }
  let files = CargoWorkspaceFiles {
    toml_path: toml_path,
    lock_path_opt: lock_path_opt,
  };
  let platform_details = try!(PlatformDetails::new_using_rustc(&settings.target));
  let planned_build = try!(planner
    .plan_build(&settings, files, platform_details));
  let mut bazel_renderer = BazelRenderer::new();
  let render_details = RenderDetails {
    path_prefix: "./".to_owned(),
    buildfile_suffix: settings.output_buildfile_suffix,
  };

  let bazel_file_outputs = match settings.genmode {
    GenMode::Vendored => try!(bazel_renderer.render_planned_build(&render_details, &planned_build)),
    GenMode::Remote => {
      // Create "remote/" if it doesn't exist
      if fs::metadata("remote/").is_err() {
        try!(fs::create_dir("remote/").map_err(|e| CargoError::from(e)));
      }

      try!(bazel_renderer.render_remote_planned_build(&render_details, &planned_build))
    } /* exhaustive, we control the definition */
  };

  let dry_run = options.flag_dryrun.unwrap_or(false);
  for FileOutputs { path, contents } in bazel_file_outputs {
    if !dry_run {
      try!(write_to_file_loudly(&path, &contents));
    } else {
      println!("{}:\n{}", path, contents);
    }
  }

  Ok(())
}

/** Verifies that the provided settings make sense. */
fn validate_settings(settings: &mut RazeSettings) -> CargoResult<()> {
  if !settings.workspace_path.starts_with("//") {
    return Err(CargoError::from(RazeError::Config {
          field_path_opt: Some("raze.workspace_path".to_owned()),
          message: format!("Path must start with \"//\". Paths into local repositories (such as \
                           @local//path) are currently unsupported.")
    }));
  }

  if settings.workspace_path != "//" && settings.workspace_path.ends_with("/") {
    settings.workspace_path.pop();
  }

  return Ok(());
}

fn write_to_file_loudly(path: &str, contents: &str) -> CargoResult<()> {
  try!(
    File::create(&path)
      .and_then(|mut f| f.write_all(contents.as_bytes()))
      .map_err(CargoError::from));
  println!("Generated {} successfully", path);
  Ok(())
}

fn load_settings<T: AsRef<Path>>(cargo_toml_path: T) -> Result<RazeSettings, CargoError> {
  let path = cargo_toml_path.as_ref();
  let mut toml = try!(File::open(path).map_err(CargoError::from));
  let mut toml_contents = String::new();
  try!(toml.read_to_string(&mut toml_contents).map_err(CargoError::from));
  toml::from_str::<settings::CargoToml>(&toml_contents)
    .map_err(CargoError::from)
    .map(|toml| toml.raze)
}
<|endoftext|>


<commit_before>use draw_state::DrawState;
use types::{ self, Matrix2d, Scalar };
use deform::DeformGrid;
use {
    CircleArc,
    Ellipse,
    Image,
    ImageSize,
    Line,
    Polygon,
    Rectangle,
};

/// Implemented by all graphics back-ends.
///
/// Default trait methods can be overridden for better performance or higher
/// quality.
///
/// When drawing, use this trait as generic constraint:
///
/// ```ignore
/// fn draw<G: Graphics>(c: &Context, g: &mut G) { ... }
/// ```
pub trait Graphics: Sized {
    /// The texture type associated with the back-end.
    ///
    /// In generic code, this type is often unknown.
    /// This might lead to more boilerplate code:
    ///
    /// ```ignore
    /// fn draw_texture<G, T>(c: &Context, g: &mut G)
    ///     where G: Graphcis<Texture = T>, T: ImageSize { ... }
    /// ```
    ///
    /// Code written specifically for one back-end can be easier to write.
    /// Later, when the code is done, it can be refactored into generic code.
    type Texture: ImageSize;

    /// Clears background with a color.
    fn clear_color(&mut self, color: [f32; 4]);

    /// Clears stencil buffer with a value.
    fn clear_stencil(&mut self, value: u8);

    /// Renders list of 2d triangles.
    fn tri_list<F>(&mut self, draw_state: &DrawState, color: &[f32; 4], f: F)
        where F: FnMut(&mut FnMut(&[f32]));

    /// Renders list of 2d triangles.
    ///
    /// A texture coordinate is assigned per vertex.
    /// The texture coordinates refers to the current texture.
    fn tri_list_uv<F>(
        &mut self,
        draw_state: &DrawState,
        color: &[f32; 4],
        texture: &<Self as Graphics>::Texture,
        f: F
    ) where F: FnMut(&mut FnMut(&[f32], &[f32]));

    /// Draws a rectangle.
    #[inline(always)]
    fn rectangle<R: Into<types::Rectangle>>(
        &mut self,
        r: &Rectangle,
        rectangle: R,
        draw_state: &DrawState,
        transform: Matrix2d
    ) {
        r.draw_tri(rectangle, draw_state, transform, self);
    }

    /// Draws a polygon.
    #[inline(always)]
    fn polygon(
        &mut self,
        p: &Polygon,
        polygon: types::Polygon,
        draw_state: &DrawState,
        transform: Matrix2d
    ) {
        p.draw_tri(polygon, draw_state, transform, self);
    }

    /// Draws a tweened polygon using linear interpolation.
    #[inline(always)]
    fn polygon_tween_lerp(
        &mut self,
        p: &Polygon,
        polygons: types::Polygons,
        tween_factor: Scalar,
        draw_state: &DrawState,
        transform: Matrix2d
    ) {
        p.draw_tween_lerp_tri(polygons, tween_factor,
            draw_state, transform, self);
    }

    /// Draws image.
    #[inline(always)]
    fn image(
        &mut self,
        image: &Image,
        texture: &Self::Texture,
        draw_state: &DrawState,
        transform: Matrix2d
    ) {
        image.draw_tri(texture, draw_state, transform, self);
    }

    /// Draws ellipse.
    #[inline(always)]
    fn ellipse<R: Into<types::Rectangle>>(
        &mut self,
        e: &Ellipse,
        rectangle: R,
        draw_state: &DrawState,
        transform: Matrix2d
    ) {
        e.draw_tri(rectangle, draw_state, transform, self);
    }

    /// Draws line.
    #[inline(always)]
    fn line<L: Into<types::Line>>(
        &mut self,
        l: &Line,
        line: L,
        draw_state: &DrawState,
        transform: Matrix2d
    ) {
        l.draw_tri(line, draw_state, transform, self);
    }

    /// Draws circle arc.
    #[inline(always)]
    fn circle_arc<R: Into<types::Rectangle>>(
        &mut self,
        c: &CircleArc,
        rectangle: R,
        draw_state: &DrawState,
        transform: Matrix2d
    ) {
        c.draw_tri(rectangle, draw_state, transform, self);
    }

    /// Draws deformed image.
    #[inline(always)]
    fn deform_image(
        &mut self,
        d: &DeformGrid,
        texture: &Self::Texture,
        draw_state: &DrawState,
        transform: Matrix2d
    ) {
        d.draw_image_tri(texture, draw_state, transform, self);
    }
}
<commit_msg>Used `types::Color` instead of `[f32; 4]`<commit_after>use draw_state::DrawState;
use types::{ self, Matrix2d, Scalar };
use deform::DeformGrid;
use {
    CircleArc,
    Ellipse,
    Image,
    ImageSize,
    Line,
    Polygon,
    Rectangle,
};

/// Implemented by all graphics back-ends.
///
/// Default trait methods can be overridden for better performance or higher
/// quality.
///
/// When drawing, use this trait as generic constraint:
///
/// ```ignore
/// fn draw<G: Graphics>(c: &Context, g: &mut G) { ... }
/// ```
pub trait Graphics: Sized {
    /// The texture type associated with the back-end.
    ///
    /// In generic code, this type is often unknown.
    /// This might lead to more boilerplate code:
    ///
    /// ```ignore
    /// fn draw_texture<G, T>(c: &Context, g: &mut G)
    ///     where G: Graphcis<Texture = T>, T: ImageSize { ... }
    /// ```
    ///
    /// Code written specifically for one back-end can be easier to write.
    /// Later, when the code is done, it can be refactored into generic code.
    type Texture: ImageSize;

    /// Clears background with a color.
    fn clear_color(&mut self, color: types::Color);

    /// Clears stencil buffer with a value.
    fn clear_stencil(&mut self, value: u8);

    /// Renders list of 2d triangles.
    fn tri_list<F>(&mut self, draw_state: &DrawState, color: &[f32; 4], f: F)
        where F: FnMut(&mut FnMut(&[f32]));

    /// Renders list of 2d triangles.
    ///
    /// A texture coordinate is assigned per vertex.
    /// The texture coordinates refers to the current texture.
    fn tri_list_uv<F>(
        &mut self,
        draw_state: &DrawState,
        color: &[f32; 4],
        texture: &<Self as Graphics>::Texture,
        f: F
    ) where F: FnMut(&mut FnMut(&[f32], &[f32]));

    /// Draws a rectangle.
    #[inline(always)]
    fn rectangle<R: Into<types::Rectangle>>(
        &mut self,
        r: &Rectangle,
        rectangle: R,
        draw_state: &DrawState,
        transform: Matrix2d
    ) {
        r.draw_tri(rectangle, draw_state, transform, self);
    }

    /// Draws a polygon.
    #[inline(always)]
    fn polygon(
        &mut self,
        p: &Polygon,
        polygon: types::Polygon,
        draw_state: &DrawState,
        transform: Matrix2d
    ) {
        p.draw_tri(polygon, draw_state, transform, self);
    }

    /// Draws a tweened polygon using linear interpolation.
    #[inline(always)]
    fn polygon_tween_lerp(
        &mut self,
        p: &Polygon,
        polygons: types::Polygons,
        tween_factor: Scalar,
        draw_state: &DrawState,
        transform: Matrix2d
    ) {
        p.draw_tween_lerp_tri(polygons, tween_factor,
            draw_state, transform, self);
    }

    /// Draws image.
    #[inline(always)]
    fn image(
        &mut self,
        image: &Image,
        texture: &Self::Texture,
        draw_state: &DrawState,
        transform: Matrix2d
    ) {
        image.draw_tri(texture, draw_state, transform, self);
    }

    /// Draws ellipse.
    #[inline(always)]
    fn ellipse<R: Into<types::Rectangle>>(
        &mut self,
        e: &Ellipse,
        rectangle: R,
        draw_state: &DrawState,
        transform: Matrix2d
    ) {
        e.draw_tri(rectangle, draw_state, transform, self);
    }

    /// Draws line.
    #[inline(always)]
    fn line<L: Into<types::Line>>(
        &mut self,
        l: &Line,
        line: L,
        draw_state: &DrawState,
        transform: Matrix2d
    ) {
        l.draw_tri(line, draw_state, transform, self);
    }

    /// Draws circle arc.
    #[inline(always)]
    fn circle_arc<R: Into<types::Rectangle>>(
        &mut self,
        c: &CircleArc,
        rectangle: R,
        draw_state: &DrawState,
        transform: Matrix2d
    ) {
        c.draw_tri(rectangle, draw_state, transform, self);
    }

    /// Draws deformed image.
    #[inline(always)]
    fn deform_image(
        &mut self,
        d: &DeformGrid,
        texture: &Self::Texture,
        draw_state: &DrawState,
        transform: Matrix2d
    ) {
        d.draw_image_tri(texture, draw_state, transform, self);
    }
}
<|endoftext|>


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

//! Processes that are directly spawned by shared mempool runtime initialization

use crate::{
    core_mempool::{CoreMempool, TimelineState},
    counters,
    network::{MempoolNetworkEvents, MempoolSyncMsg},
    shared_mempool::{
        tasks,
        types::{notify_subscribers, SharedMempool, SharedMempoolNotification},
    },
    CommitNotification, ConsensusRequest, SubmissionStatus,
};
use ::network::protocols::network::Event;
use anyhow::Result;
use bounded_executor::BoundedExecutor;
use channel::libra_channel;
use debug_interface::prelude::*;
use futures::{
    channel::{mpsc, oneshot},
    stream::{select_all, FuturesUnordered},
    StreamExt,
};
use libra_config::{config::PeerNetworkId, network_id::NetworkId};
use libra_logger::prelude::*;
use libra_types::{on_chain_config::OnChainConfigPayload, transaction::SignedTransaction};
use std::{
    ops::Deref,
    sync::{Arc, Mutex},
    time::Duration,
};
use tokio::{runtime::Handle, time::interval};
use vm_validator::vm_validator::TransactionValidation;

/// Coordinator that handles inbound network events and outbound txn broadcasts.
pub(crate) async fn coordinator<V>(
    mut smp: SharedMempool<V>,
    executor: Handle,
    network_events: Vec<(NetworkId, MempoolNetworkEvents)>,
    mut client_events: mpsc::Receiver<(
        SignedTransaction,
        oneshot::Sender<Result<SubmissionStatus>>,
    )>,
    mut consensus_requests: mpsc::Receiver<ConsensusRequest>,
    mut state_sync_requests: mpsc::Receiver<CommitNotification>,
    mut mempool_reconfig_events: libra_channel::Receiver<(), OnChainConfigPayload>,
) where
    V: TransactionValidation,
{
    let smp_events: Vec<_> = network_events
        .into_iter()
        .map(|(network_id, events)| events.map(move |e| (network_id.clone(), e)))
        .collect();
    let mut events = select_all(smp_events).fuse();
    let mempool = smp.mempool.clone();
    let peer_manager = smp.peer_manager.clone();
    let subscribers = &mut smp.subscribers.clone();
    let mut scheduled_broadcasts = FuturesUnordered::new();

    // Use a BoundedExecutor to restrict only `workers_available` concurrent
    // worker tasks that can process incoming transactions.
    let workers_available = smp.config.shared_mempool_max_concurrent_inbound_syncs;
    let bounded_executor = BoundedExecutor::new(workers_available, executor.clone());

    loop {
        ::futures::select! {
            (mut msg, callback) = client_events.select_next_some() => {
                trace_event!("mempool::client_event", {"txn", msg.sender(), msg.sequence_number()});
                bounded_executor
                .spawn(tasks::process_client_transaction_submission(
                    smp.clone(),
                    msg,
                    callback,
                ))
                .await;
            },
            msg = consensus_requests.select_next_some() => {
                tasks::process_consensus_request(&mempool, msg).await;
            }
            msg = state_sync_requests.select_next_some() => {
                tokio::spawn(tasks::process_state_sync_request(mempool.clone(), msg));
            }
            config_update = mempool_reconfig_events.select_next_some() => {
                bounded_executor
                .spawn(tasks::process_config_update(config_update, smp.validator.clone()))
                .await;
            },
            (peer, backoff) = scheduled_broadcasts.select_next_some() => {
                tasks::execute_broadcast(peer, backoff, &mut smp, &mut scheduled_broadcasts, executor.clone());
            },
            (network_id, event) = events.select_next_some() => {
                match event {
                    Ok(network_event) => {
                        match network_event {
                            Event::NewPeer(peer_id, _origin) => {
                                counters::SHARED_MEMPOOL_EVENTS
                                    .with_label_values(&["new_peer".to_string().deref()])
                                    .inc();
                                let peer = PeerNetworkId(network_id, peer_id);
                                let is_new_peer = peer_manager.add_peer(peer.clone());
                                notify_subscribers(SharedMempoolNotification::PeerStateChange, &subscribers);
                                if is_new_peer && peer_manager.is_upstream_peer(&peer) {
                                    tasks::execute_broadcast(peer, false, &mut smp, &mut scheduled_broadcasts, executor.clone());
                                }
                            }
                            Event::LostPeer(peer_id, _origin) => {
                                counters::SHARED_MEMPOOL_EVENTS
                                    .with_label_values(&["lost_peer".to_string().deref()])
                                    .inc();
                                peer_manager.disable_peer(PeerNetworkId(network_id, peer_id));
                                notify_subscribers(SharedMempoolNotification::PeerStateChange, &subscribers);
                            }
                            Event::Message((peer_id, msg)) => {
                                counters::SHARED_MEMPOOL_EVENTS
                                    .with_label_values(&["message".to_string().deref()])
                                    .inc();
                                match msg {
                                    MempoolSyncMsg::BroadcastTransactionsRequest{request_id, transactions} => {
                                        counters::SHARED_MEMPOOL_TRANSACTIONS_PROCESSED
                                            .with_label_values(&["received".to_string().deref(), peer_id.to_string().deref()])
                                            .inc_by(transactions.len() as i64);
                                        let smp_clone = smp.clone();
                                        let peer = PeerNetworkId(network_id, peer_id);
                                        let timeline_state = match peer_manager
                                            .is_upstream_peer(&peer)
                                        {
                                            true => TimelineState::NonQualified,
                                            false => TimelineState::NotReady,
                                        };
                                        bounded_executor
                                            .spawn(tasks::process_transaction_broadcast(
                                                smp_clone,
                                                transactions,
                                                request_id,
                                                timeline_state,
                                                peer
                                            ))
                                            .await;
                                    }
                                    MempoolSyncMsg::BroadcastTransactionsResponse{request_id, retry_txns, backoff} => {
                                        peer_manager.process_broadcast_ack(PeerNetworkId(network_id.clone(), peer_id), request_id, retry_txns, backoff);
                                        notify_subscribers(SharedMempoolNotification::ACK, &smp.subscribers);
                                    }
                                };
                            }
                            Event::RpcRequest((peer_id, msg, res_tx)) => {
                                send_struct_log!(security_log(security_events::INVALID_NETWORK_EVENT_MP)
                                    .data("message", &msg)
                                    .data("peer_id", &peer_id)
                                );
                                debug_assert!(false, "Unexpected network event rpc request");
                            }
                            Event::Message(message) => {
                                send_struct_log!(security_log(security_events::INVALID_NETWORK_EVENT_MP)
                                    .data("event_msg", &message)
                            );
                                debug_assert!(false, "Unexpected network event message");
                            }
                        }
                    },
                    Err(e) => {
                        send_struct_log!(security_log(security_events::INVALID_NETWORK_EVENT_MP)
                            .data("error", format!("{}", e))
                    );
                    }
                };
            },
            complete => break,
        }
    }
    crit!("[shared mempool] inbound_network_task terminated");
}

/// GC all expired transactions by SystemTTL
pub(crate) async fn gc_coordinator(mempool: Arc<Mutex<CoreMempool>>, gc_interval_ms: u64) {
    let mut interval = interval(Duration::from_millis(gc_interval_ms));
    while let Some(_interval) = interval.next().await {
        mempool
            .lock()
            .expect("[shared mempool] failed to acquire mempool lock")
            .gc();
    }

    crit!("SharedMempool gc_task terminated");
}
<commit_msg>[logging] removing useless case in shared-mempool match<commit_after>// Copyright (c) The Libra Core Contributors
// SPDX-License-Identifier: Apache-2.0

//! Processes that are directly spawned by shared mempool runtime initialization

use crate::{
    core_mempool::{CoreMempool, TimelineState},
    counters,
    network::{MempoolNetworkEvents, MempoolSyncMsg},
    shared_mempool::{
        tasks,
        types::{notify_subscribers, SharedMempool, SharedMempoolNotification},
    },
    CommitNotification, ConsensusRequest, SubmissionStatus,
};
use ::network::protocols::network::Event;
use anyhow::Result;
use bounded_executor::BoundedExecutor;
use channel::libra_channel;
use debug_interface::prelude::*;
use futures::{
    channel::{mpsc, oneshot},
    stream::{select_all, FuturesUnordered},
    StreamExt,
};
use libra_config::{config::PeerNetworkId, network_id::NetworkId};
use libra_logger::prelude::*;
use libra_types::{on_chain_config::OnChainConfigPayload, transaction::SignedTransaction};
use std::{
    ops::Deref,
    sync::{Arc, Mutex},
    time::Duration,
};
use tokio::{runtime::Handle, time::interval};
use vm_validator::vm_validator::TransactionValidation;

/// Coordinator that handles inbound network events and outbound txn broadcasts.
pub(crate) async fn coordinator<V>(
    mut smp: SharedMempool<V>,
    executor: Handle,
    network_events: Vec<(NetworkId, MempoolNetworkEvents)>,
    mut client_events: mpsc::Receiver<(
        SignedTransaction,
        oneshot::Sender<Result<SubmissionStatus>>,
    )>,
    mut consensus_requests: mpsc::Receiver<ConsensusRequest>,
    mut state_sync_requests: mpsc::Receiver<CommitNotification>,
    mut mempool_reconfig_events: libra_channel::Receiver<(), OnChainConfigPayload>,
) where
    V: TransactionValidation,
{
    let smp_events: Vec<_> = network_events
        .into_iter()
        .map(|(network_id, events)| events.map(move |e| (network_id.clone(), e)))
        .collect();
    let mut events = select_all(smp_events).fuse();
    let mempool = smp.mempool.clone();
    let peer_manager = smp.peer_manager.clone();
    let subscribers = &mut smp.subscribers.clone();
    let mut scheduled_broadcasts = FuturesUnordered::new();

    // Use a BoundedExecutor to restrict only `workers_available` concurrent
    // worker tasks that can process incoming transactions.
    let workers_available = smp.config.shared_mempool_max_concurrent_inbound_syncs;
    let bounded_executor = BoundedExecutor::new(workers_available, executor.clone());

    loop {
        ::futures::select! {
            (mut msg, callback) = client_events.select_next_some() => {
                trace_event!("mempool::client_event", {"txn", msg.sender(), msg.sequence_number()});
                bounded_executor
                .spawn(tasks::process_client_transaction_submission(
                    smp.clone(),
                    msg,
                    callback,
                ))
                .await;
            },
            msg = consensus_requests.select_next_some() => {
                tasks::process_consensus_request(&mempool, msg).await;
            }
            msg = state_sync_requests.select_next_some() => {
                tokio::spawn(tasks::process_state_sync_request(mempool.clone(), msg));
            }
            config_update = mempool_reconfig_events.select_next_some() => {
                bounded_executor
                .spawn(tasks::process_config_update(config_update, smp.validator.clone()))
                .await;
            },
            (peer, backoff) = scheduled_broadcasts.select_next_some() => {
                tasks::execute_broadcast(peer, backoff, &mut smp, &mut scheduled_broadcasts, executor.clone());
            },
            (network_id, event) = events.select_next_some() => {
                match event {
                    Ok(network_event) => {
                        match network_event {
                            Event::NewPeer(peer_id, _origin) => {
                                counters::SHARED_MEMPOOL_EVENTS
                                    .with_label_values(&["new_peer".to_string().deref()])
                                    .inc();
                                let peer = PeerNetworkId(network_id, peer_id);
                                let is_new_peer = peer_manager.add_peer(peer.clone());
                                notify_subscribers(SharedMempoolNotification::PeerStateChange, &subscribers);
                                if is_new_peer && peer_manager.is_upstream_peer(&peer) {
                                    tasks::execute_broadcast(peer, false, &mut smp, &mut scheduled_broadcasts, executor.clone());
                                }
                            }
                            Event::LostPeer(peer_id, _origin) => {
                                counters::SHARED_MEMPOOL_EVENTS
                                    .with_label_values(&["lost_peer".to_string().deref()])
                                    .inc();
                                peer_manager.disable_peer(PeerNetworkId(network_id, peer_id));
                                notify_subscribers(SharedMempoolNotification::PeerStateChange, &subscribers);
                            }
                            Event::Message((peer_id, msg)) => {
                                counters::SHARED_MEMPOOL_EVENTS
                                    .with_label_values(&["message".to_string().deref()])
                                    .inc();
                                match msg {
                                    MempoolSyncMsg::BroadcastTransactionsRequest{request_id, transactions} => {
                                        counters::SHARED_MEMPOOL_TRANSACTIONS_PROCESSED
                                            .with_label_values(&["received".to_string().deref(), peer_id.to_string().deref()])
                                            .inc_by(transactions.len() as i64);
                                        let smp_clone = smp.clone();
                                        let peer = PeerNetworkId(network_id, peer_id);
                                        let timeline_state = match peer_manager
                                            .is_upstream_peer(&peer)
                                        {
                                            true => TimelineState::NonQualified,
                                            false => TimelineState::NotReady,
                                        };
                                        bounded_executor
                                            .spawn(tasks::process_transaction_broadcast(
                                                smp_clone,
                                                transactions,
                                                request_id,
                                                timeline_state,
                                                peer
                                            ))
                                            .await;
                                    }
                                    MempoolSyncMsg::BroadcastTransactionsResponse{request_id, retry_txns, backoff} => {
                                        peer_manager.process_broadcast_ack(PeerNetworkId(network_id.clone(), peer_id), request_id, retry_txns, backoff);
                                        notify_subscribers(SharedMempoolNotification::ACK, &smp.subscribers);
                                    }
                                };
                            }
                            Event::RpcRequest((peer_id, msg, res_tx)) => {
                                send_struct_log!(security_log(security_events::INVALID_NETWORK_EVENT_MP)
                                    .data("message", &msg)
                                    .data("peer_id", &peer_id)
                                );
                                debug_assert!(false, "Unexpected network event rpc request");
                            }
                        }
                    },
                    Err(e) => {
                        send_struct_log!(security_log(security_events::INVALID_NETWORK_EVENT_MP)
                            .data("error", format!("{}", e))
                    );
                    }
                };
            },
            complete => break,
        }
    }
    crit!("[shared mempool] inbound_network_task terminated");
}

/// GC all expired transactions by SystemTTL
pub(crate) async fn gc_coordinator(mempool: Arc<Mutex<CoreMempool>>, gc_interval_ms: u64) {
    let mut interval = interval(Duration::from_millis(gc_interval_ms));
    while let Some(_interval) = interval.next().await {
        mempool
            .lock()
            .expect("[shared mempool] failed to acquire mempool lock")
            .gc();
    }

    crit!("SharedMempool gc_task terminated");
}
<|endoftext|>