<commit_before>use crate::model::prelude::*;
use serde_json::Value;
use std::collections::HashMap;
use super::context::Context;
use crate::client::bridge::gateway::event::*;
use async_trait::async_trait;

/// The core trait for handling events by serenity.
#[async_trait]
pub trait EventHandler: Send + Sync {
    /// Dispatched when the cache has received and inserted all data from
    /// guilds.
    ///
    /// This process happens upon starting your bot and should be fairly quick.
    /// However, cache actions performed prior this event may fail as the data
    /// could be not inserted yet.
    ///
    /// Provides the cached guilds' ids.
    #[cfg(feature = "cache")]
    async fn cache_ready(&self, _ctx: Context, _guilds: Vec<GuildId>) {}

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

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

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

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

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

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

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

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

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

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

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

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

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

    /// Dispatched when a guild is deleted.
    ///
    /// Provides the partial data of the guild sent by discord,
    /// and the full data from the cache, if available.
    ///
    /// The `unavailable` flag in the partial data determines the status of the guild.
    /// If the flag is false, the bot was removed from the guild, either by being
    /// kicked or banned. If the flag is true, the guild went offline.
    #[cfg(feature = "cache")]
    async fn guild_delete(&self, _ctx: Context, _incomplete: GuildUnavailable, _full: Option<Guild>) {}

    /// Dispatched when a guild is deleted.
    ///
    /// Provides the partial data of the guild sent by discord.
    ///
    /// The `unavailable` flag in the partial data determines the status of the guild.
    /// If the flag is false, the bot was removed from the guild, either by being
    /// kicked or banned. If the flag is true, the guild went offline.
    #[cfg(not(feature = "cache"))]
    async fn guild_delete(&self, _ctx: Context, _incomplete: GuildUnavailable) {}

    /* the emojis were updated. */

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

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

    /// Dispatched when a user joins a guild.
    ///
    /// Provides the guild's id and the user's member data.
    ///
    /// Note: This event will not trigger unless the "guild members" privileged intent
    /// is enabled on the bot application page.
    async fn guild_member_addition(&self, _ctx: Context, _guild_id: GuildId, _new_member: Member) {}

    /// Dispatched when a user's membership ends by leaving, getting kicked, or being banned.
    ///
    /// Provides the guild's id, the user's data, and the user's member data if available.
    ///
    /// Note: This event will not trigger unless the "guild members" privileged intent
    /// is enabled on the bot application page.
    #[cfg(feature = "cache")]
    async fn guild_member_removal(&self, _ctx: Context, _guild_id: GuildId, _user: User, _member_data_if_available: Option<Member>) {}

    /// Dispatched when a user's membership ends by leaving, getting kicked, or being banned.
    ///
    /// Provides the guild's id, the user's data.
    ///
    /// Note: This event will not trigger unless the "guild members" privileged intent
    /// is enabled on the bot application page.
    #[cfg(not(feature = "cache"))]
    async fn guild_member_removal(&self, _ctx: Context, _guild_id: GuildId, _kicked: User) {}

    /// Dispatched when a member is updated (e.g their nickname is updated).
    ///
    /// Provides the member's old data (if available) and the new data.
    ///
    /// Note: This event will not trigger unless the "guild members" privileged intent
    /// is enabled on the bot application page.
    #[cfg(feature = "cache")]
    async fn guild_member_update(&self, _ctx: Context, _old_if_available: Option<Member>, _new: Member) {}

    /// Dispatched when a member is updated (e.g their nickname is updated).
    ///
    /// Provides the new data.
    ///
    /// Note: This event will not trigger unless the "guild members" privileged intent
    /// is enabled on the bot application page.
    #[cfg(not(feature = "cache"))]
    async fn guild_member_update(&self, _ctx: Context, _new: GuildMemberUpdateEvent) {}

    /// Dispatched when the data for offline members was requested.
    ///
    /// Provides the guild's id and the data.
    async fn guild_members_chunk(&self, _ctx: Context, _chunk: GuildMembersChunkEvent) {}

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

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

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

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

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

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

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

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

    /// Dispatched when a invite is created.
    ///
    /// Provides data about the invite.
    async fn invite_create(&self, _ctx: Context, _data: InviteCreateEvent) {}

    /// Dispatched when a invite is deleted.
    ///
    /// Provides data about the invite.
    async fn invite_delete(&self, _ctx: Context, _data: InviteDeleteEvent) {}

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

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

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

    /// Dispatched when a message is updated.
    ///
    /// Provides the old message if available,
    /// the new message as an option in case of cache inconsistencies,
    /// and the raw [`MessageUpdateEvent`] as a fallback.
    #[cfg(feature = "cache")]
    async fn message_update(&self, _ctx: Context, _old_if_available: Option<Message>, _new: Option<Message>, _event: MessageUpdateEvent) {}

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

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

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

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

    /// This event is legacy, and likely no longer sent by discord.
    async fn presence_replace(&self, _ctx: Context, _: Vec<Presence>) {}

    /// Dispatched when a user's presence is updated (e.g off -> on).
    ///
    /// Provides the presence's new data.
    ///
    /// Note: This event will not trigger unless the "guild presences" privileged intent
    /// is enabled on the bot application page.
    async fn presence_update(&self, _ctx: Context, _new_data: PresenceUpdateEvent) {}

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

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

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

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

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

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

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

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

    /// Dispatched when a user joins, leaves or moves to a voice channel.
    ///
    /// Provides the guild's id (if available) and
    /// the old and the new state of the guild's voice channels.
    #[cfg(feature = "cache")]
    async fn voice_state_update(&self, _ctx: Context, _: Option<GuildId>, _old: Option<VoiceState>, _new: VoiceState) {}

    /// Dispatched when a user joins, leaves or moves to a voice channel.
    ///
    /// Provides the guild's id (if available) and
    /// the new state of the guild's voice channels.
    #[cfg(not(feature = "cache"))]
    async fn voice_state_update(&self, _ctx: Context, _: Option<GuildId>, _: VoiceState) {}

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

/// This core trait for handling raw events
#[async_trait]
pub trait RawEventHandler: Send + Sync {
    /// Dispatched when any event occurs
    async fn raw_event(&self, _ctx: Context, _ev: Event) {}
}
<commit_msg>Remove unused event handlers for groups (#1141)<commit_after>use crate::model::prelude::*;
use serde_json::Value;
use std::collections::HashMap;
use super::context::Context;
use crate::client::bridge::gateway::event::*;
use async_trait::async_trait;

/// The core trait for handling events by serenity.
#[async_trait]
pub trait EventHandler: Send + Sync {
    /// Dispatched when the cache has received and inserted all data from
    /// guilds.
    ///
    /// This process happens upon starting your bot and should be fairly quick.
    /// However, cache actions performed prior this event may fail as the data
    /// could be not inserted yet.
    ///
    /// Provides the cached guilds' ids.
    #[cfg(feature = "cache")]
    async fn cache_ready(&self, _ctx: Context, _guilds: Vec<GuildId>) {}

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

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

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

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

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

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

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

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

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

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

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

    /// Dispatched when a guild is deleted.
    ///
    /// Provides the partial data of the guild sent by discord,
    /// and the full data from the cache, if available.
    ///
    /// The `unavailable` flag in the partial data determines the status of the guild.
    /// If the flag is false, the bot was removed from the guild, either by being
    /// kicked or banned. If the flag is true, the guild went offline.
    #[cfg(feature = "cache")]
    async fn guild_delete(&self, _ctx: Context, _incomplete: GuildUnavailable, _full: Option<Guild>) {}

    /// Dispatched when a guild is deleted.
    ///
    /// Provides the partial data of the guild sent by discord.
    ///
    /// The `unavailable` flag in the partial data determines the status of the guild.
    /// If the flag is false, the bot was removed from the guild, either by being
    /// kicked or banned. If the flag is true, the guild went offline.
    #[cfg(not(feature = "cache"))]
    async fn guild_delete(&self, _ctx: Context, _incomplete: GuildUnavailable) {}

    /* the emojis were updated. */

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

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

    /// Dispatched when a user joins a guild.
    ///
    /// Provides the guild's id and the user's member data.
    ///
    /// Note: This event will not trigger unless the "guild members" privileged intent
    /// is enabled on the bot application page.
    async fn guild_member_addition(&self, _ctx: Context, _guild_id: GuildId, _new_member: Member) {}

    /// Dispatched when a user's membership ends by leaving, getting kicked, or being banned.
    ///
    /// Provides the guild's id, the user's data, and the user's member data if available.
    ///
    /// Note: This event will not trigger unless the "guild members" privileged intent
    /// is enabled on the bot application page.
    #[cfg(feature = "cache")]
    async fn guild_member_removal(&self, _ctx: Context, _guild_id: GuildId, _user: User, _member_data_if_available: Option<Member>) {}

    /// Dispatched when a user's membership ends by leaving, getting kicked, or being banned.
    ///
    /// Provides the guild's id, the user's data.
    ///
    /// Note: This event will not trigger unless the "guild members" privileged intent
    /// is enabled on the bot application page.
    #[cfg(not(feature = "cache"))]
    async fn guild_member_removal(&self, _ctx: Context, _guild_id: GuildId, _kicked: User) {}

    /// Dispatched when a member is updated (e.g their nickname is updated).
    ///
    /// Provides the member's old data (if available) and the new data.
    ///
    /// Note: This event will not trigger unless the "guild members" privileged intent
    /// is enabled on the bot application page.
    #[cfg(feature = "cache")]
    async fn guild_member_update(&self, _ctx: Context, _old_if_available: Option<Member>, _new: Member) {}

    /// Dispatched when a member is updated (e.g their nickname is updated).
    ///
    /// Provides the new data.
    ///
    /// Note: This event will not trigger unless the "guild members" privileged intent
    /// is enabled on the bot application page.
    #[cfg(not(feature = "cache"))]
    async fn guild_member_update(&self, _ctx: Context, _new: GuildMemberUpdateEvent) {}

    /// Dispatched when the data for offline members was requested.
    ///
    /// Provides the guild's id and the data.
    async fn guild_members_chunk(&self, _ctx: Context, _chunk: GuildMembersChunkEvent) {}

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

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

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

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

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

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

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

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

    /// Dispatched when a invite is created.
    ///
    /// Provides data about the invite.
    async fn invite_create(&self, _ctx: Context, _data: InviteCreateEvent) {}

    /// Dispatched when a invite is deleted.
    ///
    /// Provides data about the invite.
    async fn invite_delete(&self, _ctx: Context, _data: InviteDeleteEvent) {}

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

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

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

    /// Dispatched when a message is updated.
    ///
    /// Provides the old message if available,
    /// the new message as an option in case of cache inconsistencies,
    /// and the raw [`MessageUpdateEvent`] as a fallback.
    #[cfg(feature = "cache")]
    async fn message_update(&self, _ctx: Context, _old_if_available: Option<Message>, _new: Option<Message>, _event: MessageUpdateEvent) {}

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

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

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

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

    /// This event is legacy, and likely no longer sent by discord.
    async fn presence_replace(&self, _ctx: Context, _: Vec<Presence>) {}

    /// Dispatched when a user's presence is updated (e.g off -> on).
    ///
    /// Provides the presence's new data.
    ///
    /// Note: This event will not trigger unless the "guild presences" privileged intent
    /// is enabled on the bot application page.
    async fn presence_update(&self, _ctx: Context, _new_data: PresenceUpdateEvent) {}

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

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

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

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

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

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

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

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

    /// Dispatched when a user joins, leaves or moves to a voice channel.
    ///
    /// Provides the guild's id (if available) and
    /// the old and the new state of the guild's voice channels.
    #[cfg(feature = "cache")]
    async fn voice_state_update(&self, _ctx: Context, _: Option<GuildId>, _old: Option<VoiceState>, _new: VoiceState) {}

    /// Dispatched when a user joins, leaves or moves to a voice channel.
    ///
    /// Provides the guild's id (if available) and
    /// the new state of the guild's voice channels.
    #[cfg(not(feature = "cache"))]
    async fn voice_state_update(&self, _ctx: Context, _: Option<GuildId>, _: VoiceState) {}

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

/// This core trait for handling raw events
#[async_trait]
pub trait RawEventHandler: Send + Sync {
    /// Dispatched when any event occurs
    async fn raw_event(&self, _ctx: Context, _ev: Event) {}
}
<|endoftext|>


<commit_before>use glium::{DisplayBuild, Surface};
use glium::backend::glutin_backend::GlutinFacade;
use glium::glutin::{WindowBuilder, GlProfile, Event};

use {AppError, Result};

pub struct App {
    facade: GlutinFacade,
}

impl App {
    pub fn run() -> Result<()> {
        info!("Starting the application");
        let app = App { facade: try!(build_display()) };
        try!(app.main_loop());
        info!("The application has stopped");
        Ok(())
    }

    fn main_loop(&self) -> Result<()> {
        loop {
            let mut target = self.facade.draw();
            target.clear_color(0.0, 0.0, 1.0, 1.0);
            try!(target.finish());

            for ev in self.facade.poll_events() {
                match ev {
                    Event::Closed => return Ok(()),
                    _ => {}
                }
            }
        }
    }
}

fn build_display() -> Result<GlutinFacade> {
    WindowBuilder::new()
        .with_dimensions(800, 600)
        .with_depth_buffer(24)
        .with_gl_profile(GlProfile::Core)
        .build_glium()
        .map_err(|e| AppError::InitializationError { cause: Box::new(e) })
}
<commit_msg>handle Escape key<commit_after>use glium::{DisplayBuild, Surface};
use glium::backend::glutin_backend::GlutinFacade;
use glium::glutin::{WindowBuilder, GlProfile, Event, VirtualKeyCode};

use {AppError, Result};

pub struct App {
    facade: GlutinFacade,
}

impl App {
    pub fn run() -> Result<()> {
        info!("Starting the application");
        let app = App { facade: try!(build_display()) };
        try!(app.main_loop());
        info!("The application has stopped");
        Ok(())
    }

    fn main_loop(&self) -> Result<()> {
        loop {
            let mut target = self.facade.draw();
            target.clear_color(0.2, 0.35, 0.35, 1.0);
            try!(target.finish());

            for ev in self.facade.poll_events() {
                match ev {
                    Event::Closed | Event::KeyboardInput(_, _, Some(VirtualKeyCode::Escape)) => {
                        return Ok(())
                    }
                    _ => {}
                }
            }
        }
    }
}

fn build_display() -> Result<GlutinFacade> {
    WindowBuilder::new()
        .with_dimensions(800, 600)
        .with_depth_buffer(24)
        .with_gl_profile(GlProfile::Core)
        .build_glium()
        .map_err(|e| AppError::InitializationError { cause: Box::new(e) })
}
<|endoftext|>


<commit_before>
//-----------------------------

use texture_cache::TextureCache;
use button_controller::ButtonController;
use piston::event::*;
use piston::input::{ Button, Key };
use opengl_graphics::*;
use graphics::Image;

//-----------------------------

pub struct App {
    window_size: (f64, f64),
    gl: GlGraphics,
    input_manager: ButtonController,
    texture_cache: TextureCache,
    player: Image
}

impl App {
    pub fn new(gl_version: OpenGL, window_size: (f64, f64)) -> App {

        let mut tex_cache = TextureCache::new(String::from_str("./assets"));
        let player_image = Image::new()

        App {
            window_size: window_size,
            gl: GlGraphics::new(gl_version),
            input_manager: ButtonController::new(),
            texture_cache:
        }
    }

    pub fn render(&mut self, args: &RenderArgs) {
        use graphics::*;

        //the classic, courtesy XNA
        const CORNFLOWER_BLUE: [f32; 4] = [0.391, 0.584, 0.929, 1.0];

        self.gl.draw(args.viewport(), |c, gl| {
            clear(CORNFLOWER_BLUE, gl);
        });
    }

    pub fn update(&mut self, args: &UpdateArgs) {

        //remaining as an example of how to use button controller for myself when i go to use it
        if self.input_manager.pressed_state(&Button::Keyboard(Key::A), false, true) {
        }
        else if self.input_manager.pressed_state(&Button::Keyboard(Key::S), false, true) {
        }

        self.input_manager.update();
    }

    pub fn handle_press(&mut self, button : &Button) {
        self.input_manager.register_press(button);
    }

    pub fn handle_release(&mut self, button : &Button) {
        self.input_manager.register_release(button);
    }
}
<commit_msg>added basic functioning drawing code using TextureCache and Textures/Images<commit_after>
//-----------------------------

use texture_cache::TextureCache;
use button_controller::ButtonController;
use piston::event::*;
use piston::input::{ Button, Key };
use opengl_graphics::*;
use graphics::types::Rectangle;
use graphics::Rectangle::*;
use graphics::Image;

//-----------------------------

pub struct App {
    window_size: (f64, f64),
    gl: GlGraphics,
    input_manager: ButtonController,
    texture_cache: TextureCache,
    player: Image
}

impl App {
    pub fn new(gl_version: OpenGL, window_size: (f64, f64)) -> App {

        let mut tex_cache = TextureCache::new(String::from_str("./assets"));
        let player_image = Image::new().rect([0.0, 0.0, 16.0, 16.0]);

        App {
            window_size: window_size,
            gl: GlGraphics::new(gl_version),
            input_manager: ButtonController::new(),
            texture_cache: tex_cache,
            player: player_image,
        }
    }

    pub fn render(&mut self, args: &RenderArgs) {
        use graphics::*;

        //the classic, courtesy XNA
        const CORNFLOWER_BLUE: [f32; 4] = [0.391, 0.584, 0.929, 1.0];
        let player = &self.player;
        let player_texture = self.texture_cache.get_asset(&String::from_str("Player.png"));

        self.gl.draw(args.viewport(), |c, gl| {
            clear(CORNFLOWER_BLUE, gl);
            let transform = c.transform.trans(100.0, 100.0);
            player.draw(player_texture, default_draw_state(), transform, gl);
        });
    }

    pub fn update(&mut self, args: &UpdateArgs) {

        //remaining as an example of how to use button controller for myself when i go to use it
        if self.input_manager.pressed_state(&Button::Keyboard(Key::A), false, true) {
        }
        else if self.input_manager.pressed_state(&Button::Keyboard(Key::S), false, true) {
        }

        self.input_manager.update();
    }

    pub fn handle_press(&mut self, button : &Button) {
        self.input_manager.register_press(button);
    }

    pub fn handle_release(&mut self, button : &Button) {
        self.input_manager.register_release(button);
    }
}
<|endoftext|>


<commit_before>// TODO: Clean code, please
// TODO: Check types used. Try to minimize the memory used
// TODO: Add more tests.
// TODO: Better UI. Colors? Num of matches?
// TODO: Try to do the fuzzy search async?
extern crate scout;
extern crate docopt;
extern crate termios;
extern crate termion;

use std::env;

use termios::{Termios, TCSANOW, ECHO, ICANON, tcsetattr};
use termion::event::Key;
use termion::input::TermRead;
use termion::screen::*;
use termion::{color, style};
use std::io::{self, Read, Write};
use std::os::unix::io::AsRawFd;

const USAGE: &'static str = "
Scout: Small fuzzy finder

This program expects a list of items in the
standard input, so it is better to use it
with pipes.

Usage:
  scout [options]

Options:
  -h --help      Show this screen.
  -v --version   Show version.

Example:
  $ ls | scout
";

fn magic() -> Result<String, io::Error> {
    // Collect initial input
    let mut buffer = String::new();
    try!(io::stdin().read_to_string(&mut buffer));
    let input: Vec<&str> = buffer.split("\n")
        .map(|s| s.trim())
        .filter(|s| !s.is_empty())
        .collect();
    let width = format!("{}", input.len()).len();

    // I need to transform tty into raw mode to get chars byte by byte.
    // Check termios crate
    // see: http://stackoverflow.com/a/37416107
    let tty = termion::get_tty().unwrap();
    let fd = tty.as_raw_fd();
    let original_tty = Termios::from_fd(fd).unwrap();
    let mut new_tty = original_tty.clone();  // make a mutable copy of termios
                                            // that we will modify
    new_tty.c_lflag &= !(ICANON | ECHO); // no echo and canonical mode
    tcsetattr(fd, TCSANOW, &mut new_tty).unwrap();

    // Then we can use this tty to create our screen with the help
    // of termion crate
    let mut screen = AlternateScreen::from(tty);
    let mut result = String::new();
    let mut query: Vec<char> = vec![];
    let mut buffer: Vec<u8> = Vec::with_capacity(2);

    'event: loop {
        let s: String = query.iter().cloned().collect();
        let query_chars: Vec<char> = query.iter().cloned().collect();
        let choices = scout::explore(&input, &query_chars);

        // Clear the screen and put the cursor at the beginning
        writeln!(
            &mut screen,
            "{}{}",
            termion::clear::All,
            termion::cursor::Goto(1, 1),
        ).unwrap();

        // Print all the choices
        for (i, choice) in choices.iter().take(21).cloned().enumerate() {
            // Split the string in different areas
            // to highlight the matching part
            let string = choice.to_string();
            let (before, rest) = string.split_at(choice.start());
            let (middle, after) = if rest.len() <= choice.end() {
                (rest, "")
            } else {
                rest.split_at(choice.end())
            };

            let line = format!(
                "{before}{highlight}{middle}{reset}{after}",
                before = before,
                highlight = color::Fg(color::LightGreen),
                middle = middle,
                reset = color::Fg(color::Reset),
                after = after
            );

            if i == 0 {
                writeln!(&mut screen, "{}{}{}", style::Invert, line, style::Reset).unwrap();
            } else {
                writeln!(&mut screen, "{}", line).unwrap();
            }
        }

        // Go to the beginning again and redraw the prompt.
        // This will put the cursor at the end of it
        let prompt = format!("{:width$} > {}", choices.len(), s, width = width);
        write!(&mut screen, "{}{}", termion::cursor::Goto(1, 1), prompt).unwrap();

        screen.flush().unwrap();

        // Some chars are 2 bytes at a time, so better
        // to read 2 by 2. E.g: Arrow keys
        let mut int_buffer = [0;2];
        buffer.clear();
        // Ensure that we read 2 bytes in an intermediate buffer
        //
        // If the amount of real bytes read is less than 2,
        // put only that byte in the buffer so we don't carry
        // junk.
        //
        // If the amount is 2, put both in the buffer
        match screen.read(&mut int_buffer) {
            Ok(1) => {
                buffer.push(int_buffer[0])
            },
            Ok(2) => {
                buffer = int_buffer.iter().map(|&x| x).collect()
            }
            Ok(_) => {},
            Err(_) => {}
        };

        // Now this buffer has the exact amount of bytes from
        // the input so we can use the #keys() function from termion
        // to read and transform those bytes into proper keys
        for c in buffer.keys() {
            match c.unwrap() {
                Key::Esc => break 'event,
                Key::Backspace => {
                    let _ = query.pop();
                },
                Key::Char('\n') => {
                    if let Some(choice) = choices.first() {
                        result = choice.to_string();
                    }
                    break 'event
                },
                Key::Char(c) => {
                    query.push(c as char);
                },
                _ => {},
            }
        }
    };

    tcsetattr(fd, TCSANOW, & original_tty).unwrap();  // reset the stdin to

    Ok(result)
}

pub fn main() {
    docopt::Docopt::new(USAGE)
        .and_then(|doc| {
            doc.argv(env::args())
                .version(Some(scout::version()))
                .parse()
        })
        .unwrap_or_else(|e| e.exit());;

    match magic() {
        Ok(result) => println!("{}", result),
        Err(e) => panic!(e),
    }
}
<commit_msg>Move through the list with arrow keys<commit_after>// TODO: Clean code, please
// TODO: Check types used. Try to minimize the memory used
// TODO: Add more tests.
// TODO: Better UI. Colors? Num of matches?
// TODO: Try to do the fuzzy search async?
extern crate scout;
extern crate docopt;
extern crate termios;
extern crate termion;

use std::env;

use termios::{Termios, TCSANOW, ECHO, ICANON, tcsetattr};
use termion::event::Key;
use termion::input::TermRead;
use termion::screen::*;
use termion::{color, style};
use std::io::{self, Read, Write};
use std::os::unix::io::AsRawFd;

const USAGE: &'static str = "
Scout: Small fuzzy finder

This program expects a list of items in the
standard input, so it is better to use it
with pipes.

Usage:
  scout [options]

Options:
  -h --help      Show this screen.
  -v --version   Show version.

Example:
  $ ls | scout
";

fn magic() -> Result<String, io::Error> {
    // Collect initial input
    let mut buffer = String::new();
    try!(io::stdin().read_to_string(&mut buffer));
    let input: Vec<&str> = buffer.split("\n")
        .map(|s| s.trim())
        .filter(|s| !s.is_empty())
        .collect();
    let width = format!("{}", input.len()).len();
    let mut selection = 0; // current selected item

    // I need to transform tty into raw mode to get chars byte by byte.
    // Check termios crate
    // see: http://stackoverflow.com/a/37416107
    let tty = termion::get_tty().unwrap();
    let fd = tty.as_raw_fd();
    let original_tty = Termios::from_fd(fd).unwrap();
    let mut new_tty = original_tty.clone();  // make a mutable copy of termios
                                            // that we will modify
    new_tty.c_lflag &= !(ICANON | ECHO); // no echo and canonical mode
    tcsetattr(fd, TCSANOW, &mut new_tty).unwrap();

    // Then we can use this tty to create our screen with the help
    // of termion crate
    let mut screen = AlternateScreen::from(tty);
    let mut result = String::new();
    let mut query: Vec<char> = vec![];
    let mut buffer: Vec<u8> = vec![];

    'event: loop {
        let s: String = query.iter().cloned().collect();
        let query_chars: Vec<char> = query.iter().cloned().collect();
        let choices = scout::explore(&input, &query_chars);

        // Clear the screen and put the cursor at the beginning
        writeln!(
            &mut screen,
            "{}{}",
            termion::clear::All,
            termion::cursor::Goto(1, 1),
        ).unwrap();

        // Print all the choices
        for (i, choice) in choices.iter().take(21).cloned().enumerate() {
            // Split the string in different areas
            // to highlight the matching part
            let string = choice.to_string();
            let (before, rest) = string.split_at(choice.start());
            let (middle, after) = if rest.len() <= choice.end() {
                (rest, "")
            } else {
                rest.split_at(choice.end())
            };

            let line = format!(
                "{before}{highlight}{middle}{reset}{after}",
                before = before,
                highlight = color::Fg(color::LightGreen),
                middle = middle,
                reset = color::Fg(color::Reset),
                after = after
            );

            if i == selection {
                writeln!(&mut screen, "{}{}{}", style::Invert, line, style::Reset).unwrap();
            } else {
                writeln!(&mut screen, "{}", line).unwrap();
            }
        }

        // Go to the beginning again and redraw the prompt.
        // This will put the cursor at the end of it
        let prompt = format!("{:width$} > {}", choices.len(), s, width = width);
        write!(&mut screen, "{}{}", termion::cursor::Goto(1, 1), prompt).unwrap();

        screen.flush().unwrap();

        // Read a maximum of 3 bytes to handle special keys like
        // Up and Down arrow keys
        let mut int_buffer = [0;3];
        buffer.clear();
        // Ensure that we read 2 bytes in an intermediate buffer
        //
        // If the amount of real bytes read is less than 2,
        // put only that byte in the buffer so we don't carry
        // junk.
        //
        // If the amount is 2, put both in the buffer
        match screen.read(&mut int_buffer) {
            Ok(n) => {
                buffer = int_buffer.iter().take(n).map(|&x| x).collect()
            },
            Err(_) => {}
        };

        // Now this buffer has the exact amount of bytes from
        // the input so we can use the #keys() function from termion
        // to read and transform those bytes into proper keys
        for c in buffer.keys() {
            match c.unwrap() {
                Key::Backspace => {
                    let _ = query.pop();
                },
                Key::Ctrl('p') | Key::Up => {
                    selection = if selection == 0 {
                        // TODO: This should be only over the visible
                        // window
                        choices.len() - 1
                    } else {
                        selection - 1
                    };
                },
                Key::Ctrl('n') | Key::Down => {
                    // TODO: This should be only over the visible
                    // window
                    // TODO: The loop shouldn't be trigger again,
                    // we should render the screen without
                    // doing a full search. In the next event loop
                    // iteration the selection should be reset to
                    // 0 again
                    selection = if selection == (choices.len() - 1) {
                        0
                    } else {
                        selection + 1
                    };
                },
                Key::Char('\n') | Key::Ctrl('j') | Key::Ctrl('m') => {
                    let choice = choices[selection];
                    result = choice.to_string();

                    break 'event
                },
                Key::Ctrl('u') => {
                    query.clear();
                },
                Key::Char(c) => {
                    query.push(c);
                },
                Key::Esc => break 'event,
                _ => {},
            }
        }
    };

    tcsetattr(fd, TCSANOW, & original_tty).unwrap();  // reset the stdin to

    Ok(result)
}

pub fn main() {
    docopt::Docopt::new(USAGE)
        .and_then(|doc| {
            doc.argv(env::args())
                .version(Some(scout::version()))
                .parse()
        })
        .unwrap_or_else(|e| e.exit());;

    match magic() {
        Ok(result) => println!("{}", result),
        Err(e) => panic!(e),
    }
}
<|endoftext|>


<commit_before>const INTERPRETER_START: u32 = 0x000;
const INTERPRETER_END: u32 = 0x1FF;
const INTERPRETER_LENGTH: u32 = INTERPRETER_END - INTERPRETER_START;

const FONT_SET_START: usize = 0x050;
const FONT_SET_END: usize = 0x0A0;
const FONT_SET_LENGTH: usize = FONT_SET_END - FONT_SET_START;

const DATA_START: usize = 0x200;
const DATA_END: usize = 0xFFF;
const DATA_LENGTH: usize = DATA_END - DATA_START;

const MEM_LENGTH: usize = 4096;
const STACK_LEVEL: usize = 16;
const REG_NUM: usize = 16;
const GFX_WIDTH: usize = 64;
const GFX_HEIGHT: usize = 32;

const FONTSET: [u8; 80] = [
    0xF0, 0x90, 0x90, 0x90, 0xF0, // 0
    0x20, 0x60, 0x20, 0x20, 0x70, // 1
    0xF0, 0x10, 0xF0, 0x80, 0xF0, // 2
    0xF0, 0x10, 0xF0, 0x10, 0xF0, // 3
    0x90, 0x90, 0xF0, 0x10, 0x10, // 4
    0xF0, 0x80, 0xF0, 0x10, 0xF0, // 5
    0xF0, 0x80, 0xF0, 0x90, 0xF0, // 6
    0xF0, 0x10, 0x20, 0x40, 0x40, // 7
    0xF0, 0x90, 0xF0, 0x90, 0xF0, // 8
    0xF0, 0x90, 0xF0, 0x10, 0xF0, // 9
    0xF0, 0x90, 0xF0, 0x90, 0x90, // A
    0xE0, 0x90, 0xE0, 0x90, 0xE0, // B
    0xF0, 0x80, 0x80, 0x80, 0xF0, // C
    0xE0, 0x90, 0x90, 0x90, 0xE0, // D
    0xF0, 0x80, 0xF0, 0x80, 0xF0, // E
    0xF0, 0x80, 0xF0, 0x80, 0x80  // F
];

pub fn init() -> Cpu {
    let mut ret = Cpu {
        pc: 0x200,
        opcode: 0,
        memory: vec![0; MEM_LENGTH],
        reg: vec![0; REG_NUM],
        index: 0,
        sp: 0,
        stack: vec![0; STACK_LEVEL],
        // gfx: vec![vec![0; GFX_HEIGHT]; GFX_WIDTH],
        gfx: vec![0; GFX_WIDTH * GFX_HEIGHT],
        delay_timer: 0,
        sound_timer: 0,
        key: vec![0; STACK_LEVEL],
    };
    ret.load_fontset();
    ret
}

#[derive(Debug)]
pub struct Cpu {
    pc: usize,
    opcode: u16,
    memory: Vec<u8>,
    reg: Vec<u8>,
    index: usize,
    // gfx: Vec<Vec<u8>>,
    gfx: Vec<u8>,
    delay_timer: u8,
    sound_timer: u8,
    stack: Vec<usize>,
    sp: usize,
    key: Vec<u8>,
}

impl Cpu {
    fn load_fontset(&mut self) {
        for i in 0..FONT_SET_LENGTH {
            self.memory[FONT_SET_START + i] = FONTSET[i];
        }
    }

    pub fn load_rom(&mut self, rom: Box<[u8]>) {
        for i in 0..rom.len() {
            self.memory[DATA_START + i] = rom[i];
        }
    }

    pub fn emulate_cycle(&mut self) {
        self.opcode = (self.memory[self.pc] as u16) << 8 | (self.memory[self.pc+1] as u16);

        match self.opcode & 0xF000 {
            0x2000 => { // 2NNN: Calls subroutine at NNN
                let addr = self.opcode & 0x0FFF;
                self.stack[self.sp] = self.pc;
                self.sp += 1;
                self.pc = addr as usize;
            },
            0x6000 => { // 6XNN: Sets VX to NN
                let index = (self.opcode & 0x0F00) >> 8;
                let val = self.opcode & 0x00FF;
                self.reg[index as usize] = val as u8;
                self.pc += 2;
            },
            0x7000 => { // 7XNN: Adds NN to VX
                let x = (self.opcode & 0x0F00) >> 8;
                let nn = self.opcode & 0x00FF;
                self.reg[x as usize] += nn as u8;
                self.pc += 2;
            },
            0xA000 => { // ANNN: Sets I to the address NNN
                let val = self.opcode & 0x0FFF;
                self.index = val as usize;
                self.pc += 2;
            },
            0xD000 => { // DXYN: Draws sprite at cordinate VX, VY with height N
                let x = (self.opcode & 0x0F00) >> 8;
                let y = (self.opcode & 0x00F0) >> 4;
                let n = self.opcode & 0x000F;
                let mut pix: u8;

                let tmp = self.index as usize;
                let sprite = &self.memory[tmp..(tmp + (n as usize))];

                self.reg[0xF] = 0;
                for i in 0..n {
                    pix = (&sprite)[i as usize];
                    for j in 0..8 {
                        if (pix & (0x80 >> j)) != 0 {
                            if self.gfx[(x + j + ((y + i) * 64)) as usize] == 1 {
                                self.reg[0xF] = 1;
                            }
                            self.gfx[(x + j + ((y + i) * 64)) as usize] ^= 1;
                        }
                    }
                }
                self.pc += 2;
            },
            0xF000 => {
                match self.opcode & 0x00FF {
                    0x0029 => { // FX29: set I to character sprite in VX
                        let x = (self.opcode & 0x0F00) >> 8;
                        let tmp = FONT_SET_START + ((self.reg[x as usize] * 5) as usize);
                        self.index = self.memory[tmp] as usize;
                        self.pc += 2;
                    },
                    0x0033 => { // FX33: set memory at I:I+2 to VX
                        let x = (self.opcode & 0x0F00) >> 8;
                        let val = self.reg[x as usize];
                        self.memory[self.index] = val / 100;
                        self.memory[self.index + 1] = (val / 10) % 10;
                        self.memory[self.index + 2] = val % 10;
                        self.pc += 2;
                    },
                    0x0065 => { // FX65: fill V0 to VX with values at I:I+X
                        let x = (self.opcode & 0x0F00) >> 8;
                        for i in 0..x {
                            self.reg[i as usize] = self.memory[self.index + (i as usize)];
                        }
                        self.pc += 2;
                    },
                    _ => panic!("Unknown opcode or not implemented yet: {:X}", self.opcode)
                }
            },
            _ => panic!("Unknown opcode or not implemented yet: {:X}", self.opcode)
        }
    }
}
<commit_msg>Add opcode 00EE (return from subroutine)<commit_after>const INTERPRETER_START: u32 = 0x000;
const INTERPRETER_END: u32 = 0x1FF;
const INTERPRETER_LENGTH: u32 = INTERPRETER_END - INTERPRETER_START;

const FONT_SET_START: usize = 0x050;
const FONT_SET_END: usize = 0x0A0;
const FONT_SET_LENGTH: usize = FONT_SET_END - FONT_SET_START;

const DATA_START: usize = 0x200;
const DATA_END: usize = 0xFFF;
const DATA_LENGTH: usize = DATA_END - DATA_START;

const MEM_LENGTH: usize = 4096;
const STACK_LEVEL: usize = 16;
const REG_NUM: usize = 16;
const GFX_WIDTH: usize = 64;
const GFX_HEIGHT: usize = 32;

const FONTSET: [u8; 80] = [
    0xF0, 0x90, 0x90, 0x90, 0xF0, // 0
    0x20, 0x60, 0x20, 0x20, 0x70, // 1
    0xF0, 0x10, 0xF0, 0x80, 0xF0, // 2
    0xF0, 0x10, 0xF0, 0x10, 0xF0, // 3
    0x90, 0x90, 0xF0, 0x10, 0x10, // 4
    0xF0, 0x80, 0xF0, 0x10, 0xF0, // 5
    0xF0, 0x80, 0xF0, 0x90, 0xF0, // 6
    0xF0, 0x10, 0x20, 0x40, 0x40, // 7
    0xF0, 0x90, 0xF0, 0x90, 0xF0, // 8
    0xF0, 0x90, 0xF0, 0x10, 0xF0, // 9
    0xF0, 0x90, 0xF0, 0x90, 0x90, // A
    0xE0, 0x90, 0xE0, 0x90, 0xE0, // B
    0xF0, 0x80, 0x80, 0x80, 0xF0, // C
    0xE0, 0x90, 0x90, 0x90, 0xE0, // D
    0xF0, 0x80, 0xF0, 0x80, 0xF0, // E
    0xF0, 0x80, 0xF0, 0x80, 0x80  // F
];

pub fn init() -> Cpu {
    let mut ret = Cpu {
        pc: 0x200,
        opcode: 0,
        memory: vec![0; MEM_LENGTH],
        reg: vec![0; REG_NUM],
        index: 0,
        sp: 0,
        stack: vec![0; STACK_LEVEL],
        // gfx: vec![vec![0; GFX_HEIGHT]; GFX_WIDTH],
        gfx: vec![0; GFX_WIDTH * GFX_HEIGHT],
        delay_timer: 0,
        sound_timer: 0,
        key: vec![0; STACK_LEVEL],
    };
    ret.load_fontset();
    ret
}

#[derive(Debug)]
pub struct Cpu {
    pc: usize,
    opcode: u16,
    memory: Vec<u8>,
    reg: Vec<u8>,
    index: usize,
    // gfx: Vec<Vec<u8>>,
    gfx: Vec<u8>,
    delay_timer: u8,
    sound_timer: u8,
    stack: Vec<usize>,
    sp: usize,
    key: Vec<u8>,
}

impl Cpu {
    fn load_fontset(&mut self) {
        for i in 0..FONT_SET_LENGTH {
            self.memory[FONT_SET_START + i] = FONTSET[i];
        }
    }

    pub fn load_rom(&mut self, rom: Box<[u8]>) {
        for i in 0..rom.len() {
            self.memory[DATA_START + i] = rom[i];
        }
    }

    pub fn emulate_cycle(&mut self) {
        self.opcode = (self.memory[self.pc] as u16) << 8 | (self.memory[self.pc+1] as u16);

        match self.opcode & 0xF000 {
            0x0000 => {
                match self.opcode & 0x0F00 {
                    0x0000 => {
                        match self.opcode & 0x00FF {
                            0x00EE => {
                                self.sp -= 1;
                                self.pc = self.stack[self.sp];
                            },
                            _ => panic!("Unknown opcode or not implemented yet: {:X}", self.opcode)
                        }
                    },
                    _ => panic!("Unknown opcode or not implemented yet: {:X}", self.opcode)
                }
            },
            0x2000 => { // 2NNN: Calls subroutine at NNN
                let addr = self.opcode & 0x0FFF;
                self.stack[self.sp] = self.pc;
                self.sp += 1;
                self.pc = addr as usize;
            },
            0x6000 => { // 6XNN: Sets VX to NN
                let index = (self.opcode & 0x0F00) >> 8;
                let val = self.opcode & 0x00FF;
                self.reg[index as usize] = val as u8;
                self.pc += 2;
            },
            0x7000 => { // 7XNN: Adds NN to VX
                let x = (self.opcode & 0x0F00) >> 8;
                let nn = self.opcode & 0x00FF;
                self.reg[x as usize] += nn as u8;
                self.pc += 2;
            },
            0xA000 => { // ANNN: Sets I to the address NNN
                let val = self.opcode & 0x0FFF;
                self.index = val as usize;
                self.pc += 2;
            },
            0xD000 => { // DXYN: Draws sprite at cordinate VX, VY with height N
                let x = (self.opcode & 0x0F00) >> 8;
                let y = (self.opcode & 0x00F0) >> 4;
                let n = self.opcode & 0x000F;
                let mut pix: u8;

                let tmp = self.index as usize;
                let sprite = &self.memory[tmp..(tmp + (n as usize))];

                self.reg[0xF] = 0;
                for i in 0..n {
                    pix = (&sprite)[i as usize];
                    for j in 0..8 {
                        if (pix & (0x80 >> j)) != 0 {
                            if self.gfx[(x + j + ((y + i) * 64)) as usize] == 1 {
                                self.reg[0xF] = 1;
                            }
                            self.gfx[(x + j + ((y + i) * 64)) as usize] ^= 1;
                        }
                    }
                }
                self.pc += 2;
            },
            0xF000 => {
                match self.opcode & 0x00FF {
                    0x0029 => { // FX29: set I to character sprite in VX
                        let x = (self.opcode & 0x0F00) >> 8;
                        let tmp = FONT_SET_START + ((self.reg[x as usize] * 5) as usize);
                        self.index = self.memory[tmp] as usize;
                        self.pc += 2;
                    },
                    0x0033 => { // FX33: set memory at I:I+2 to VX
                        let x = (self.opcode & 0x0F00) >> 8;
                        let val = self.reg[x as usize];
                        self.memory[self.index] = val / 100;
                        self.memory[self.index + 1] = (val / 10) % 10;
                        self.memory[self.index + 2] = val % 10;
                        self.pc += 2;
                    },
                    0x0065 => { // FX65: fill V0 to VX with values at I:I+X
                        let x = (self.opcode & 0x0F00) >> 8;
                        for i in 0..x {
                            self.reg[i as usize] = self.memory[self.index + (i as usize)];
                        }
                        self.pc += 2;
                    },
                    _ => panic!("Unknown opcode or not implemented yet: {:X}", self.opcode)
                }
            },
            _ => panic!("Unknown opcode or not implemented yet: {:X}", self.opcode)
        }
    }
}
<|endoftext|>


<commit_before>use memory::Memory;

/// Z80 status flags
const CF : i32 = 1<<0;      // carry flag
const NF : i32 = 1<<1;      // add/subtract flag
const VF : i32 = 1<<2;      // parity/overflow flag
//const PF : i32 = 1<<2;      // parity/overflow flag
const XF : i32 = 1<<3;      // undocumented flag bit 3
const HF : i32 = 1<<4;      // half-carry flag
const YF : i32 = 1<<5;      // undocumented flag bit 5
const ZF : i32 = 1<<6;      // zero flag
const SF : i32 = 1<<7;      // sign flag

/// the Z80 CPU state
pub struct CPU {
    pub a: u8, pub f: u8,
    pub b: u8, pub c: u8,
    pub d: u8, pub e: u8,
    pub h: u8, pub l: u8,

    pub ix: u16,
    pub iy: u16,
    pub wz: u16,

    pub af_: u16,
    pub bc_: u16,
    pub de_: u16,
    pub hl_: u16,
    pub wz_: u16,

    pub sp: u16,
    pub pc: u16,

    pub i: u8,
    pub r: u8,
    pub im: u8,

    pub halt: bool,
    pub iff1: bool,
    pub iff2: bool,

    pub mem : Memory,
}

impl CPU {
    /// initialize a new Z80 CPU object
    pub fn new() -> CPU {
        CPU {
            a: 0, f: 0, b: 0, c: 0, d: 0, e: 0, h: 0, l: 0,
            ix: 0, iy: 0, wz: 0,
            af_: 0, bc_: 0, de_: 0, hl_: 0, wz_: 0,
            sp: 0, pc: 0,
            i: 0, r: 0, im: 0,
            halt: false, iff1: false, iff2: false,
            mem: Memory::new()
        }
    }

    /// reset the cpu
    pub fn reset(&mut self) {
        self.pc = 0;
        self.wz = 0;
        self.im = 0;
        self.halt = false;
        self.iff1 = false;
        self.iff2 = false;
        self.i = 0;
        self.r = 0;
    }

    /// go into HALT state
    pub fn halt(&mut self) {
        self.halt = true;
        self.pc -= 1;
    }

    /// implement RST instruction
    pub fn rst(&mut self, v: u8) {
        self.sp -= 2;
        self.mem.w16(self.sp, self.pc);
        self.pc = v as u16;
        self.wz = self.pc;
    }

    /// compute flags for add/adc
    pub fn flags_add(acc: i32, add: i32, res: i32) -> u8 {
        let f : i32 =
            (if (res & 0xFF)==0 { SF } else { res & ZF }) |
            (res & (YF|XF)) |
            ((res>>8) & CF) |
            ((acc^add^res) & HF) |
            ((((acc^add^0x80) & (add^res))>>5) & VF);
        f as u8
    }

    /// compute flags for sub/sbc
    pub fn flags_sub(acc: i32, sub: i32, res: i32) -> u8 {
        let f : i32 = NF |
            (if (res & 0xFF)==0 { SF } else { res & ZF }) |
            (res & (YF|XF)) |
            ((res>>8) & CF) |
            ((acc^sub^res) & HF) |
            ((((acc^sub)&(res^acc))>>5)&VF);
        f as u8
    }

}

#[cfg(test)]
mod tests {

    use super::*;

    #[test]
    fn cpu_new() {
        let cpu = CPU::new();
        assert!((0 == cpu.a) && (0 == cpu.f));
        assert!((0 == cpu.b) && (0 == cpu.c));
        assert!((0 == cpu.d) && (0 == cpu.e));
        assert!((0 == cpu.h) && (0 == cpu.l));
        assert!((0 == cpu.ix) && (0 == cpu.iy));
        assert!(0 == cpu.wz);
        assert!((0 == cpu.af_) && (0 == cpu.bc_));
        assert!((0 == cpu.de_) && (0 == cpu.hl_));
        assert!(0 == cpu.wz_);
        assert!((0 == cpu.sp) && (0 == cpu.pc));
        assert!((0 == cpu.i) && (0 == cpu.r));
        assert!(0 == cpu.im);
        assert!(!cpu.halt);
        assert!(!cpu.iff1);
        assert!(!cpu.iff2);
    }

    #[test]
    fn cpu_reset() {
        let mut cpu = CPU::new();
        cpu.pc = 0x1234;
        cpu.wz = 1234;
        cpu.im = 45;
        cpu.halt = true;
        cpu.iff1 = true;
        cpu.iff2 = true;
        cpu.i = 2;
        cpu.r = 3;
        cpu.reset();
        assert!(0 == cpu.pc);
        assert!(0 == cpu.wz);
        assert!(0 == cpu.im);
        assert!(!cpu.halt);
        assert!(!cpu.iff1);
        assert!(!cpu.iff2);
        assert!(0 == cpu.i);
        assert!(0 == cpu.r);
    }

    #[test]
    fn cpu_halt() {
        let mut cpu = CPU::new();
        cpu.pc = 0x1234;
        cpu.halt();
        assert!(cpu.halt);
        assert!(0x1233 == cpu.pc);
    }

    #[test]
    fn cpu_rst() {
        let mut cpu = CPU::new();
        cpu.pc = 0x123;
        cpu.sp = 0x100;
        cpu.rst(0x38);
        assert!(0xFE == cpu.sp);
        assert!(cpu.mem.r16(cpu.sp) == 0x123);
        assert!(0x38 == cpu.pc);
        assert!(0x38 == cpu.wz);
    }
}

<commit_msg>CPU: add8, adc8, sub8, sbc8 plus tests<commit_after>use memory::Memory;

/// Z80 status flags
pub const CF : i32 = 1<<0;      // carry flag
pub const NF : i32 = 1<<1;      // add/subtract flag
pub const VF : i32 = 1<<2;      // parity/overflow flag
//const PF : i32 = 1<<2;      // parity/overflow flag
pub const XF : i32 = 1<<3;      // undocumented flag bit 3
pub const HF : i32 = 1<<4;      // half-carry flag
pub const YF : i32 = 1<<5;      // undocumented flag bit 5
pub const ZF : i32 = 1<<6;      // zero flag
pub const SF : i32 = 1<<7;      // sign flag

/// 8-bit register indices
pub const B : usize = 0;
pub const C : usize = 1;
pub const D : usize = 2;
pub const E : usize = 3;
pub const H : usize = 4;
pub const L : usize = 5;
pub const F : usize = 6;
pub const A : usize = 7;
pub const IXH : usize = 8;
pub const IXL : usize = 9;
pub const IYH : usize = 10;
pub const IYL : usize = 11;
pub const SPH : usize = 12;
pub const SPL : usize = 13;
pub const NUM_REGS : usize = 14;

/// 16-bit register indices
pub const BC : usize = 0;
pub const DE : usize = 2;
pub const HL : usize = 4;
pub const AF : usize = 6;
pub const IX : usize = 8;
pub const IY : usize = 10;
pub const SP : usize = 12;

/// the Z80 CPU state
pub struct CPU {
    pub reg : [u8; NUM_REGS],

    pub wz: u16,
    pub pc: u16,

    pub af_: u16,
    pub bc_: u16,
    pub de_: u16,
    pub hl_: u16,
    pub wz_: u16,

    pub i: u8,
    pub r: u8,
    pub im: u8,

    pub halt: bool,
    pub iff1: bool,
    pub iff2: bool,

    pub mem : Memory,
}

impl CPU {
    /// initialize a new Z80 CPU object
    pub fn new() -> CPU {
        CPU {
            reg: [0; NUM_REGS],
            af_: 0, bc_: 0, de_: 0, hl_: 0, wz_: 0,
            wz: 0, pc: 0,
            i: 0, r: 0, im: 0,
            halt: false, iff1: false, iff2: false,
            mem: Memory::new()
        }
    }

    /// reset the cpu
    pub fn reset(&mut self) {
        self.pc = 0;
        self.wz = 0;
        self.im = 0;
        self.halt = false;
        self.iff1 = false;
        self.iff2 = false;
        self.i = 0;
        self.r = 0;
    }

    /// write 16-bit register value
    pub fn w16(&mut self, r: usize, val: u16) {
        self.reg[r]   = (val>>8) as u8;
        self.reg[r+1] = (val&0xFF) as u8;
    }

    /// read 16-bit register value
    pub fn r16(&self, r: usize) -> u16 {
        let h : u16 = (self.reg[r] as u16) <<8;
        let l : u16 = self.reg[r+1] as u16;
        h | l
    }

    /// go into HALT state
    pub fn halt(&mut self) {
        self.halt = true;
        self.pc -= 1;
    }

    /// push a value on the stack
    pub fn push(&mut self, val: u16) {
        let sp = self.r16(SP).wrapping_sub(2);
        self.w16(SP, sp);
        self.mem.w16(sp, val);
    }

    /// implement RST instruction
    pub fn rst(&mut self, val: u8) {
        // workaround for https://github.com/rust-lang/rust/issues/29975
        let pc = self.pc;
        self.push(pc);
        self.pc = val as u16;
        self.wz = self.pc;
    }

    /// compute flags for add/adc
    pub fn flags_add(acc: i32, add: i32, res: i32) -> u8 {
        let f : i32 =
            (if (res & 0xFF)==0 { ZF } else { res & SF }) |
            (res & (YF|XF)) |
            ((res>>8) & CF) |
            ((acc^add^res) & HF) |
            ((((acc^add^0x80) & (add^res))>>5) & VF);
        f as u8
    }

    /// compute flags for sub/sbc
    pub fn flags_sub(acc: i32, sub: i32, res: i32) -> u8 {
        let f : i32 = NF |
            (if (res & 0xFF)==0 { ZF } else { res & SF }) |
            (res & (YF|XF)) |
            ((res>>8) & CF) |
            ((acc^sub^res) & HF) |
            ((((acc^sub)&(res^acc))>>5)&VF);
        f as u8
    }

    /// 8-bit addition
    pub fn add8(&mut self, v: u8) {
        let acc = self.reg[A] as i32;
        let add = v as i32;
        let res = acc + add;
        self.reg[F] = CPU::flags_add(acc, add, res);
        self.reg[A] = (res&0xFF) as u8;
    }

    /// 8-bit addition with carry
    pub fn adc8(&mut self, v: u8) {
        let acc = self.reg[A] as i32;
        let add = v as i32;
        let res = acc + add + ((self.reg[F] as i32) & CF);
        self.reg[F] = CPU::flags_add(acc, add, res);
        self.reg[A] = (res&0xFF) as u8;
    }

    /// 8-bit subtraction
    pub fn sub8(&mut self, v: u8) {
        let acc = self.reg[A] as i32;
        let sub = v as i32;
        let res = acc - sub;
        self.reg[F] = CPU::flags_sub(acc, sub, res);
        self.reg[A] = (res&0xFF) as u8;
    }

    /// 8-bit subtraction with carry
    pub fn sbc8(&mut self, v: u8) {
        let acc = self.reg[A] as i32;
        let sub = v as i32;
        let res = acc - sub - ((self.reg[F] as i32) & CF);
        self.reg[F] = CPU::flags_sub(acc, sub, res);
        self.reg[A] = (res&0xFF) as u8;
    }
}

#[cfg(test)]
mod tests {

    use super::*;

    #[test]
    fn new() {
        let cpu = CPU::new();
        assert!((0 == cpu.reg[A]) && (0 == cpu.reg[F]));
        assert!((0 == cpu.reg[B]) && (0 == cpu.reg[C]));
        assert!((0 == cpu.reg[D]) && (0 == cpu.reg[E]));
        assert!((0 == cpu.reg[H]) && (0 == cpu.reg[L]));
        assert!((0 == cpu.reg[IXH]) && (0 == cpu.reg[IXL]));
        assert!((0 == cpu.reg[IYH]) && (0 == cpu.reg[IYL]));
        assert!((0 == cpu.reg[SPH]) && (0 == cpu.reg[SPL]));
        assert!(0 == cpu.wz);
        assert!(0 == cpu.pc);
        assert!((0 == cpu.af_) && (0 == cpu.bc_));
        assert!((0 == cpu.de_) && (0 == cpu.hl_));
        assert!(0 == cpu.wz_);
        assert!((0 == cpu.i) && (0 == cpu.r));
        assert!(0 == cpu.im);
        assert!(!cpu.halt);
        assert!(!cpu.iff1);
        assert!(!cpu.iff2);
    }

    #[test]
    fn reset() {
        let mut cpu = CPU::new();
        cpu.pc = 0x1234;
        cpu.wz = 1234;
        cpu.im = 45;
        cpu.halt = true;
        cpu.iff1 = true;
        cpu.iff2 = true;
        cpu.i = 2;
        cpu.r = 3;
        cpu.reset();
        assert!(0 == cpu.pc);
        assert!(0 == cpu.wz);
        assert!(0 == cpu.im);
        assert!(!cpu.halt);
        assert!(!cpu.iff1);
        assert!(!cpu.iff2);
        assert!(0 == cpu.i);
        assert!(0 == cpu.r);
    }

    #[test]
    fn reg16_rw() {
        let mut cpu = CPU::new();
        cpu.w16(BC, 0x1234);
        cpu.w16(DE, 0x5678);
        cpu.w16(HL, 0x1357);
        cpu.w16(AF, 0x1122);
        assert!(0x12 == cpu.reg[B]);
        assert!(0x34 == cpu.reg[C]);
        assert!(0x1234 == cpu.r16(BC));
        assert!(0x56 == cpu.reg[D]);
        assert!(0x78 == cpu.reg[E]);
        assert!(0x5678 == cpu.r16(DE));
        assert!(0x13 == cpu.reg[H]);
        assert!(0x57 == cpu.reg[L]);
        assert!(0x1357 == cpu.r16(HL));
        assert!(0x11 == cpu.reg[F]);
        assert!(0x22 == cpu.reg[A]);
    }

    #[test]
    fn halt() {
        let mut cpu = CPU::new();
        cpu.pc = 0x1234;
        cpu.halt();
        assert!(cpu.halt);
        assert!(0x1233 == cpu.pc);
    }

    #[test]
    fn rst() {
        let mut cpu = CPU::new();
        cpu.pc = 0x123;
        cpu.w16(SP, 0x100);
        cpu.rst(0x38);
        assert!(0xFE == cpu.r16(SP));
        assert!(cpu.mem.r16(cpu.r16(SP)) == 0x123);
        assert!(0x38 == cpu.pc);
        assert!(0x38 == cpu.wz);
    }

    #[test]
    fn push() {
        let mut cpu = CPU::new();
        cpu.w16(SP, 0x100);
        cpu.push(0x1234);
        assert!(0xFE == cpu.r16(SP));
        assert!(cpu.mem.r16(cpu.r16(SP)) == 0x1234);
    }

    fn test_flags(flags: u8, mask: i32) -> bool {
        (((flags as i32) & !(XF|YF)) & mask) == mask
    }

    #[test]
    fn add8() {
        let mut cpu = CPU::new();
        cpu.reg[A] = 0xF;
        cpu.add8(0xF);
        assert!(0x1E == cpu.reg[A]);
        assert!(test_flags(cpu.reg[F], HF));
        cpu.add8(0xE0);
        assert!(0xFE == cpu.reg[A]);
        assert!(test_flags(cpu.reg[F], SF));
        cpu.reg[A] = 0x81;
        cpu.add8(0x80);
        assert!(0x01 == cpu.reg[A]);
        assert!(test_flags(cpu.reg[F], VF|CF));
        cpu.add8(0xFF);
        assert!(0x00 == cpu.reg[A]);
        assert!(test_flags(cpu.reg[F], ZF|HF|CF));
    }

    #[test]
    fn adc8() {
        let mut cpu = CPU::new();
        cpu.reg[A] = 0x00;
        cpu.adc8(0x00);
        assert!(0x00 == cpu.reg[A]);
        assert!(test_flags(cpu.reg[F], ZF));
        cpu.adc8(0x41);
        assert!(0x41 == cpu.reg[A]);
        assert!(test_flags(cpu.reg[F], 0));
        cpu.adc8(0x61);
        assert!(0xA2 == cpu.reg[A]);
        assert!(test_flags(cpu.reg[F], SF|VF));
        cpu.adc8(0x81);
        assert!(0x23 == cpu.reg[A]);
        assert!(test_flags(cpu.reg[F], VF|CF));
        cpu.adc8(0x41);
        assert!(0x65 == cpu.reg[A]);
        assert!(test_flags(cpu.reg[F], 0));
    }

    #[test]
    fn sub8() {
        let mut cpu = CPU::new();
        cpu.reg[A] = 0x04;
        cpu.sub8(0x04);
        assert!(0x00 == cpu.reg[A]);
        assert!(test_flags(cpu.reg[F], ZF|NF));
        cpu.sub8(0x01);
        assert!(0xFF == cpu.reg[A]);
        assert!(test_flags(cpu.reg[F], SF|HF|NF|CF));
        cpu.sub8(0xF8);
        assert!(0x07 == cpu.reg[A]);
        assert!(test_flags(cpu.reg[F], NF));
        cpu.sub8(0x0F);
        assert!(0xF8 == cpu.reg[A]);
        assert!(test_flags(cpu.reg[F], SF|HF|NF|CF));
    }

    #[test]
    fn sbc8() {
        let mut cpu = CPU::new();
        cpu.reg[A] = 0x04;
        cpu.sbc8(0x04);
        assert!(0x00 == cpu.reg[A]);
        assert!(test_flags(cpu.reg[F], ZF|NF));
        cpu.sbc8(0x01);
        assert!(0xFF == cpu.reg[A]);
        assert!(test_flags(cpu.reg[F], SF|HF|NF|CF));
        cpu.sbc8(0xF8);
        assert!(0x06 == cpu.reg[A]);
        assert!(test_flags(cpu.reg[F], NF));
    }
}

<|endoftext|>


<commit_before>use vmx;
use collections::vec::Vec;
use core::sync::atomic::{ATOMIC_U16_INIT, AtomicU16, Ordering};
use spin;

static CPU_COUNT: spin::Once<u16> = spin::Once::new();
static CPU_ASSIGNMENT: AtomicU16 = ATOMIC_U16_INIT;

pub fn init(count: u16) {
    CPU_COUNT.call_once(|| count);
}

fn get_cpu_count() -> u16 {
    *CPU_COUNT.call_once(|| {
        panic!("Must initialize CPU count before requesting it");
    })
}

pub fn bring_core_online() {
    let cpu_num = CPU_ASSIGNMENT.fetch_add(1, Ordering::Relaxed);
    set_number(cpu_num);
}

fn set_number(num: u16) {
    vmx::write_es(num);
}

pub fn get_number() -> u16 {
    vmx::read_es()
}

pub struct PerCoreVariable<T> {
    vars: Vec<T>,
}

impl<T: Default> Default for PerCoreVariable<T> {
    fn default() -> Self {
        let mut vars = vec![];
        for _ in 0..get_cpu_count() {
            vars.push(Default::default());
        }
        PerCoreVariable { vars: vars }
    }
}

impl<T> PerCoreVariable<T> {
    pub fn get(&self) -> &T {
        &self.vars[get_number() as usize]
    }

    pub fn get_mut(&mut self) -> &mut T {
        &mut self.vars[get_number() as usize]
    }

}

impl<T: Clone> PerCoreVariable<T> {
    pub fn new(item: T) -> Self {
        PerCoreVariable { vars: vec![item; get_cpu_count() as usize] }
    }
}

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

    #[test]
    fn test_new() {
        init(4);
        let pcv: PerCoreVariable<u32> = PerCoreVariable::new(42);
        assert_eq!(pcv.vars.len(), 4);
        for variable in &pcv.vars {
            assert_eq!(*variable, 42);
        }
    }

    #[test]
    fn test_default() {
        init(4);
        let pcv: PerCoreVariable<u32> = Default::default();
        let def: u32 = Default::default();
        assert_eq!(pcv.vars.len(), 4);
        for variable in &pcv.vars {
            assert_eq!(*variable, def);
        }
    }

    #[test]
    fn test_get_mut() {
        init(4);
        let mut pcv: PerCoreVariable<u32> = Default::default();
        assert_eq!(*pcv.get(), 0);
        {
            let mut b = pcv.get_mut();
            *b = 42;
        }
        assert_eq!(*pcv.get(), 42);
    }

    #[test]
    fn test_get() {
        init(4);
        let mut pcv: PerCoreVariable<u32> = Default::default();
        assert_eq!(*pcv.get(), 0);
        pcv.vars[0] = 42;
        assert_eq!(*pcv.get(), 42);
    }

    #[test]
    fn test_atomic() {
        use core::sync::atomic::{AtomicUsize};
        init(4);
        let mut pcv: PerCoreVariable<AtomicUsize> = Default::default();
        assert_eq!(pcv.get().load(Ordering::Relaxed), 0);
        {
            let b = pcv.get_mut();
            b.store(42, Ordering::Relaxed);
        }
        assert_eq!(pcv.get().load(Ordering::Relaxed), 42);

    }

}
<commit_msg>CPU runtime tests<commit_after>use vmx;
use collections::vec::Vec;
use core::sync::atomic::{ATOMIC_U16_INIT, AtomicU16, Ordering};
use spin;

static CPU_COUNT: spin::Once<u16> = spin::Once::new();
static CPU_ASSIGNMENT: AtomicU16 = ATOMIC_U16_INIT;

pub fn init(count: u16) {
    CPU_COUNT.call_once(|| count);
}

fn get_cpu_count() -> u16 {
    *CPU_COUNT.call_once(|| {
        panic!("Must initialize CPU count before requesting it");
    })
}

pub fn bring_core_online() {
    let cpu_num = CPU_ASSIGNMENT.fetch_add(1, Ordering::Relaxed);
    set_number(cpu_num);
}

fn set_number(num: u16) {
    vmx::write_es(num);
}

pub fn get_number() -> u16 {
    vmx::read_es()
}

pub struct PerCoreVariable<T> {
    vars: Vec<T>,
}

impl<T: Default> Default for PerCoreVariable<T> {
    fn default() -> Self {
        let mut vars = vec![];
        for _ in 0..get_cpu_count() {
            vars.push(Default::default());
        }
        PerCoreVariable { vars: vars }
    }
}

impl<T> PerCoreVariable<T> {
    pub fn get(&self) -> &T {
        &self.vars[get_number() as usize]
    }

    pub fn get_mut(&mut self) -> &mut T {
        &mut self.vars[get_number() as usize]
    }

}

impl<T: Clone> PerCoreVariable<T> {
    pub fn new(item: T) -> Self {
        PerCoreVariable { vars: vec![item; get_cpu_count() as usize] }
    }
}

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

    #[test]
    fn test_new() {
        init(4);
        let pcv: PerCoreVariable<u32> = PerCoreVariable::new(42);
        assert_eq!(pcv.vars.len(), 4);
        for variable in &pcv.vars {
            assert_eq!(*variable, 42);
        }
    }

    #[test]
    fn test_default() {
        init(4);
        let pcv: PerCoreVariable<u32> = Default::default();
        let def: u32 = Default::default();
        assert_eq!(pcv.vars.len(), 4);
        for variable in &pcv.vars {
            assert_eq!(*variable, def);
        }
    }

    #[test]
    fn test_get_mut() {
        init(4);
        let mut pcv: PerCoreVariable<u32> = Default::default();
        assert_eq!(*pcv.get(), 0);
        {
            let mut b = pcv.get_mut();
            *b = 42;
        }
        assert_eq!(*pcv.get(), 42);
    }

    #[test]
    fn test_get() {
        init(4);
        let mut pcv: PerCoreVariable<u32> = Default::default();
        assert_eq!(*pcv.get(), 0);
        pcv.vars[0] = 42;
        assert_eq!(*pcv.get(), 42);
    }

    #[test]
    fn test_atomic() {
        use core::sync::atomic::{AtomicUsize};
        init(4);
        let mut pcv: PerCoreVariable<AtomicUsize> = Default::default();
        assert_eq!(pcv.get().load(Ordering::Relaxed), 0);
        {
            let b = pcv.get_mut();
            b.store(42, Ordering::Relaxed);
        }
        assert_eq!(pcv.get().load(Ordering::Relaxed), 42);

    }
}

#[cfg(feature = "runtime_tests")]
pub mod runtime_tests {
    use super::*;

    pub fn run() {
        info!("Running CPU tests...");
        test_atomic();
        info!("CPU succeeded.");
    }

    fn test_atomic() {
        use core::sync::atomic::{AtomicUsize};
        init(1);
        let mut pcv: PerCoreVariable<AtomicUsize> = Default::default();
        assert_eq!(pcv.get().load(Ordering::Relaxed), 0);
        {
            let b = pcv.get_mut();
            b.store(42, Ordering::Relaxed);
        }
        assert_eq!(pcv.get().load(Ordering::Relaxed), 42);

    }
}
<|endoftext|>


<commit_before>use anyhow::{Context, Error};
use flate2::read::GzDecoder;
use std::collections::HashMap;
use std::fs::File;
use std::io::Read;
use std::path::{Path, PathBuf};
use tar::Archive;

const DEFAULT_TARGET: &str = "x86_64-unknown-linux-gnu";

#[derive(Debug, Hash, Eq, PartialEq, Clone)]
pub(crate) enum PkgType {
    Rust,
    RustSrc,
    Cargo,
    Rls,
    RustAnalyzer,
    Clippy,
    Rustfmt,
    LlvmTools,
    Miri,
    Other(String),
}

impl PkgType {
    pub(crate) fn from_component(component: &str) -> Self {
        match component {
            "rust" => PkgType::Rust,
            "rust-src" => PkgType::RustSrc,
            "cargo" => PkgType::Cargo,
            "rls" | "rls-preview" => PkgType::Rls,
            "rust-analyzer" | "rust-analyzer-preview" => PkgType::RustAnalyzer,
            "clippy" | "clippy-preview" => PkgType::Clippy,
            "rustfmt" | "rustfmt-preview" => PkgType::Rustfmt,
            "llvm-tools" | "llvm-tools-preview" => PkgType::LlvmTools,
            "miri" | "miri-preview" => PkgType::Miri,
            other => PkgType::Other(other.into()),
        }
    }

    fn rust_monorepo_path(&self) -> Option<&'static str> {
        match self {
            PkgType::Cargo => Some("src/tools/cargo"),
            PkgType::Rls => Some("src/tools/rls"),
            PkgType::RustAnalyzer => Some("src/tools/rust-analyzer/crates/rust-analyzer"),
            PkgType::Clippy => Some("src/tools/clippy"),
            PkgType::Rustfmt => Some("src/tools/rustfmt"),
            PkgType::Miri => Some("src/tools/miri"),
            PkgType::Rust => None,
            PkgType::RustSrc => None,
            PkgType::LlvmTools => None,
            PkgType::Other(_) => None,
        }
    }

    fn tarball_component_name(&self) -> &str {
        match self {
            PkgType::Rust => "rust",
            PkgType::RustSrc => "rust-src",
            PkgType::Cargo => "cargo",
            PkgType::Rls => "rls",
            PkgType::RustAnalyzer => "rust-analyzer",
            PkgType::Clippy => "clippy",
            PkgType::Rustfmt => "rustfmt",
            PkgType::LlvmTools => "llvm-tools",
            PkgType::Miri => "miri",
            PkgType::Other(component) => component,
        }
    }

    fn should_use_rust_version(&self) -> bool {
        match self {
            PkgType::Cargo => false,
            PkgType::Rls => false,
            PkgType::RustAnalyzer => false,
            PkgType::Clippy => false,
            PkgType::Rustfmt => false,
            PkgType::LlvmTools => false,
            PkgType::Miri => false,

            PkgType::Rust => true,
            PkgType::RustSrc => true,
            PkgType::Other(_) => true,
        }
    }
}

#[derive(Debug, Default, Clone)]
pub(crate) struct VersionInfo {
    pub(crate) version: Option<String>,
    pub(crate) git_commit: Option<String>,
    pub(crate) present: bool,
}

pub(crate) struct Versions {
    channel: String,
    rustc_version: String,
    monorepo_root: PathBuf,
    dist_path: PathBuf,
    package_versions: HashMap<PkgType, String>,
    versions: HashMap<PkgType, VersionInfo>,
}

impl Versions {
    pub(crate) fn new(
        channel: &str,
        dist_path: &Path,
        monorepo_root: &Path,
    ) -> Result<Self, Error> {
        Ok(Self {
            channel: channel.into(),
            rustc_version: std::fs::read_to_string(monorepo_root.join("src").join("version"))
                .context("failed to read the rustc version from src/version")?
                .trim()
                .to_string(),
            monorepo_root: monorepo_root.into(),
            dist_path: dist_path.into(),
            package_versions: HashMap::new(),
            versions: HashMap::new(),
        })
    }

    pub(crate) fn channel(&self) -> &str {
        &self.channel
    }

    pub(crate) fn version(&mut self, mut package: &PkgType) -> Result<VersionInfo, Error> {
        if package.should_use_rust_version() {
            package = &PkgType::Rust;
        }

        match self.versions.get(package) {
            Some(version) => Ok(version.clone()),
            None => {
                let version_info = self.load_version_from_tarball(package)?;
                self.versions.insert(package.clone(), version_info.clone());
                Ok(version_info)
            }
        }
    }

    fn load_version_from_tarball(&mut self, package: &PkgType) -> Result<VersionInfo, Error> {
        let tarball_name = self.tarball_name(package, DEFAULT_TARGET)?;
        let tarball = self.dist_path.join(tarball_name);

        let file = match File::open(&tarball) {
            Ok(file) => file,
            Err(err) if err.kind() == std::io::ErrorKind::NotFound => {
                // Missing tarballs do not return an error, but return empty data.
                return Ok(VersionInfo::default());
            }
            Err(err) => return Err(err.into()),
        };
        let mut tar = Archive::new(GzDecoder::new(file));

        let mut version = None;
        let mut git_commit = None;
        for entry in tar.entries()? {
            let mut entry = entry?;

            let dest;
            match entry.path()?.components().nth(1).and_then(|c| c.as_os_str().to_str()) {
                Some("version") => dest = &mut version,
                Some("git-commit-hash") => dest = &mut git_commit,
                _ => continue,
            }
            let mut buf = String::new();
            entry.read_to_string(&mut buf)?;
            *dest = Some(buf);

            // Short circuit to avoid reading the whole tar file if not necessary.
            if version.is_some() && git_commit.is_some() {
                break;
            }
        }

        Ok(VersionInfo { version, git_commit, present: true })
    }

    pub(crate) fn disable_version(&mut self, package: &PkgType) {
        match self.versions.get_mut(package) {
            Some(version) => {
                *version = VersionInfo::default();
            }
            None => {
                self.versions.insert(package.clone(), VersionInfo::default());
            }
        }
    }

    pub(crate) fn tarball_name(
        &mut self,
        package: &PkgType,
        target: &str,
    ) -> Result<String, Error> {
        Ok(format!(
            "{}-{}-{}.tar.gz",
            package.tarball_component_name(),
            self.package_version(package).with_context(|| format!(
                "failed to get the package version for component {:?}",
                package,
            ))?,
            target
        ))
    }

    pub(crate) fn package_version(&mut self, package: &PkgType) -> Result<String, Error> {
        match self.package_versions.get(package) {
            Some(release) => Ok(release.clone()),
            None => {
                let version = match package.rust_monorepo_path() {
                    Some(path) => {
                        let path = self.monorepo_root.join(path).join("Cargo.toml");
                        let cargo_toml: CargoToml = toml::from_slice(&std::fs::read(path)?)?;
                        cargo_toml.package.version
                    }
                    None => self.rustc_version.clone(),
                };

                let release = match self.channel.as_str() {
                    "stable" => version,
                    "beta" => "beta".into(),
                    "nightly" => "nightly".into(),
                    _ => format!("{}-dev", version),
                };

                self.package_versions.insert(package.clone(), release.clone());
                Ok(release)
            }
        }
    }
}

#[derive(serde::Deserialize)]
struct CargoToml {
    package: CargoTomlPackage,
}

#[derive(serde::Deserialize)]
struct CargoTomlPackage {
    version: String,
}
<commit_msg>build-manifest: add documentation on the PkgType methods<commit_after>use anyhow::{Context, Error};
use flate2::read::GzDecoder;
use std::collections::HashMap;
use std::fs::File;
use std::io::Read;
use std::path::{Path, PathBuf};
use tar::Archive;

const DEFAULT_TARGET: &str = "x86_64-unknown-linux-gnu";

#[derive(Debug, Hash, Eq, PartialEq, Clone)]
pub(crate) enum PkgType {
    Rust,
    RustSrc,
    Cargo,
    Rls,
    RustAnalyzer,
    Clippy,
    Rustfmt,
    LlvmTools,
    Miri,
    Other(String),
}

impl PkgType {
    pub(crate) fn from_component(component: &str) -> Self {
        match component {
            "rust" => PkgType::Rust,
            "rust-src" => PkgType::RustSrc,
            "cargo" => PkgType::Cargo,
            "rls" | "rls-preview" => PkgType::Rls,
            "rust-analyzer" | "rust-analyzer-preview" => PkgType::RustAnalyzer,
            "clippy" | "clippy-preview" => PkgType::Clippy,
            "rustfmt" | "rustfmt-preview" => PkgType::Rustfmt,
            "llvm-tools" | "llvm-tools-preview" => PkgType::LlvmTools,
            "miri" | "miri-preview" => PkgType::Miri,
            other => PkgType::Other(other.into()),
        }
    }

    /// The directory containing the `Cargo.toml` of this component inside the monorepo, to
    /// retrieve the source code version. If `None` is returned Rust's version will be used.
    fn rust_monorepo_path(&self) -> Option<&'static str> {
        match self {
            PkgType::Cargo => Some("src/tools/cargo"),
            PkgType::Rls => Some("src/tools/rls"),
            PkgType::RustAnalyzer => Some("src/tools/rust-analyzer/crates/rust-analyzer"),
            PkgType::Clippy => Some("src/tools/clippy"),
            PkgType::Rustfmt => Some("src/tools/rustfmt"),
            PkgType::Miri => Some("src/tools/miri"),
            PkgType::Rust => None,
            PkgType::RustSrc => None,
            PkgType::LlvmTools => None,
            PkgType::Other(_) => None,
        }
    }

    /// First part of the tarball name.
    fn tarball_component_name(&self) -> &str {
        match self {
            PkgType::Rust => "rust",
            PkgType::RustSrc => "rust-src",
            PkgType::Cargo => "cargo",
            PkgType::Rls => "rls",
            PkgType::RustAnalyzer => "rust-analyzer",
            PkgType::Clippy => "clippy",
            PkgType::Rustfmt => "rustfmt",
            PkgType::LlvmTools => "llvm-tools",
            PkgType::Miri => "miri",
            PkgType::Other(component) => component,
        }
    }

    /// Whether this package has the same version as Rust itself, or has its own `version` and
    /// `git-commit-hash` files inside the tarball.
    fn should_use_rust_version(&self) -> bool {
        match self {
            PkgType::Cargo => false,
            PkgType::Rls => false,
            PkgType::RustAnalyzer => false,
            PkgType::Clippy => false,
            PkgType::Rustfmt => false,
            PkgType::LlvmTools => false,
            PkgType::Miri => false,

            PkgType::Rust => true,
            PkgType::RustSrc => true,
            PkgType::Other(_) => true,
        }
    }
}

#[derive(Debug, Default, Clone)]
pub(crate) struct VersionInfo {
    pub(crate) version: Option<String>,
    pub(crate) git_commit: Option<String>,
    pub(crate) present: bool,
}

pub(crate) struct Versions {
    channel: String,
    rustc_version: String,
    monorepo_root: PathBuf,
    dist_path: PathBuf,
    package_versions: HashMap<PkgType, String>,
    versions: HashMap<PkgType, VersionInfo>,
}

impl Versions {
    pub(crate) fn new(
        channel: &str,
        dist_path: &Path,
        monorepo_root: &Path,
    ) -> Result<Self, Error> {
        Ok(Self {
            channel: channel.into(),
            rustc_version: std::fs::read_to_string(monorepo_root.join("src").join("version"))
                .context("failed to read the rustc version from src/version")?
                .trim()
                .to_string(),
            monorepo_root: monorepo_root.into(),
            dist_path: dist_path.into(),
            package_versions: HashMap::new(),
            versions: HashMap::new(),
        })
    }

    pub(crate) fn channel(&self) -> &str {
        &self.channel
    }

    pub(crate) fn version(&mut self, mut package: &PkgType) -> Result<VersionInfo, Error> {
        if package.should_use_rust_version() {
            package = &PkgType::Rust;
        }

        match self.versions.get(package) {
            Some(version) => Ok(version.clone()),
            None => {
                let version_info = self.load_version_from_tarball(package)?;
                self.versions.insert(package.clone(), version_info.clone());
                Ok(version_info)
            }
        }
    }

    fn load_version_from_tarball(&mut self, package: &PkgType) -> Result<VersionInfo, Error> {
        let tarball_name = self.tarball_name(package, DEFAULT_TARGET)?;
        let tarball = self.dist_path.join(tarball_name);

        let file = match File::open(&tarball) {
            Ok(file) => file,
            Err(err) if err.kind() == std::io::ErrorKind::NotFound => {
                // Missing tarballs do not return an error, but return empty data.
                return Ok(VersionInfo::default());
            }
            Err(err) => return Err(err.into()),
        };
        let mut tar = Archive::new(GzDecoder::new(file));

        let mut version = None;
        let mut git_commit = None;
        for entry in tar.entries()? {
            let mut entry = entry?;

            let dest;
            match entry.path()?.components().nth(1).and_then(|c| c.as_os_str().to_str()) {
                Some("version") => dest = &mut version,
                Some("git-commit-hash") => dest = &mut git_commit,
                _ => continue,
            }
            let mut buf = String::new();
            entry.read_to_string(&mut buf)?;
            *dest = Some(buf);

            // Short circuit to avoid reading the whole tar file if not necessary.
            if version.is_some() && git_commit.is_some() {
                break;
            }
        }

        Ok(VersionInfo { version, git_commit, present: true })
    }

    pub(crate) fn disable_version(&mut self, package: &PkgType) {
        match self.versions.get_mut(package) {
            Some(version) => {
                *version = VersionInfo::default();
            }
            None => {
                self.versions.insert(package.clone(), VersionInfo::default());
            }
        }
    }

    pub(crate) fn tarball_name(
        &mut self,
        package: &PkgType,
        target: &str,
    ) -> Result<String, Error> {
        Ok(format!(
            "{}-{}-{}.tar.gz",
            package.tarball_component_name(),
            self.package_version(package).with_context(|| format!(
                "failed to get the package version for component {:?}",
                package,
            ))?,
            target
        ))
    }

    pub(crate) fn package_version(&mut self, package: &PkgType) -> Result<String, Error> {
        match self.package_versions.get(package) {
            Some(release) => Ok(release.clone()),
            None => {
                let version = match package.rust_monorepo_path() {
                    Some(path) => {
                        let path = self.monorepo_root.join(path).join("Cargo.toml");
                        let cargo_toml: CargoToml = toml::from_slice(&std::fs::read(path)?)?;
                        cargo_toml.package.version
                    }
                    None => self.rustc_version.clone(),
                };

                let release = match self.channel.as_str() {
                    "stable" => version,
                    "beta" => "beta".into(),
                    "nightly" => "nightly".into(),
                    _ => format!("{}-dev", version),
                };

                self.package_versions.insert(package.clone(), release.clone());
                Ok(release)
            }
        }
    }
}

#[derive(serde::Deserialize)]
struct CargoToml {
    package: CargoTomlPackage,
}

#[derive(serde::Deserialize)]
struct CargoTomlPackage {
    version: String,
}
<|endoftext|>