<commit_before>//! Audit log types for administrative actions within guilds.

use std::{collections::HashMap, mem::transmute};

use serde::de::{self, Deserializer};
use serde::ser::{Serialize, Serializer};

mod change;
mod utils;

pub use change::{AffectedRole, Change, EntityType};
use utils::{optional_string, users, webhooks};

use crate::model::prelude::*;

/// Determines the action that was done on a target.
#[derive(Copy, Clone, Debug)]
#[non_exhaustive]
pub enum Action {
    GuildUpdate,
    Channel(ActionChannel),
    ChannelOverwrite(ActionChannelOverwrite),
    Member(ActionMember),
    Role(ActionRole),
    Invite(ActionInvite),
    Webhook(ActionWebhook),
    Emoji(ActionEmoji),
    Message(ActionMessage),
    Integration(ActionIntegration),
    StageInstance(ActionStageInstance),
    Sticker(ActionSticker),
    Thread(ActionThread),
}

impl Action {
    pub fn num(self) -> u8 {
        match self {
            Action::GuildUpdate => 1,
            Action::Channel(x) => x as u8,
            Action::ChannelOverwrite(x) => x as u8,
            Action::Member(x) => x as u8,
            Action::Role(x) => x as u8,
            Action::Invite(x) => x as u8,
            Action::Webhook(x) => x as u8,
            Action::Emoji(x) => x as u8,
            Action::Message(x) => x as u8,
            Action::Integration(x) => x as u8,
            Action::StageInstance(x) => x as u8,
            Action::Sticker(x) => x as u8,
            Action::Thread(x) => x as u8,
        }
    }

    pub fn from_value(value: u8) -> Option<Action> {
        let action = match value {
            1 => Action::GuildUpdate,
            10..=12 => Action::Channel(unsafe { transmute(value) }),
            13..=15 => Action::ChannelOverwrite(unsafe { transmute(value) }),
            20..=28 => Action::Member(unsafe { transmute(value) }),
            30..=32 => Action::Role(unsafe { transmute(value) }),
            40..=42 => Action::Invite(unsafe { transmute(value) }),
            50..=52 => Action::Webhook(unsafe { transmute(value) }),
            60..=62 => Action::Emoji(unsafe { transmute(value) }),
            72..=75 => Action::Message(unsafe { transmute(value) }),
            80..=82 => Action::Integration(unsafe { transmute(value) }),
            83..=85 => Action::StageInstance(unsafe { transmute(value) }),
            90..=92 => Action::Sticker(unsafe { transmute(value) }),
            110..=112 => Action::Thread(unsafe { transmute(value) }),
            _ => return None,
        };

        Some(action)
    }
}

impl<'de> Deserialize<'de> for Action {
    fn deserialize<D: Deserializer<'de>>(deserializer: D) -> StdResult<Self, D::Error> {
        let value = u8::deserialize(deserializer)?;
        Action::from_value(value)
            .ok_or_else(|| de::Error::custom(format!("Unexpected action number: {}", value)))
    }
}

impl Serialize for Action {
    fn serialize<S: Serializer>(&self, serializer: S) -> StdResult<S::Ok, S::Error> {
        serializer.serialize_u8(self.num())
    }
}

#[derive(Copy, Clone, Debug)]
#[non_exhaustive]
#[repr(u8)]
pub enum ActionChannel {
    Create = 10,
    Update = 11,
    Delete = 12,
}

#[derive(Copy, Clone, Debug)]
#[non_exhaustive]
#[repr(u8)]
pub enum ActionChannelOverwrite {
    Create = 13,
    Update = 14,
    Delete = 15,
}

#[derive(Copy, Clone, Debug)]
#[non_exhaustive]
#[repr(u8)]
pub enum ActionMember {
    Kick = 20,
    Prune = 21,
    BanAdd = 22,
    BanRemove = 23,
    Update = 24,
    RoleUpdate = 25,
    MemberMove = 26,
    MemberDisconnect = 27,
    BotAdd = 28,
}

#[derive(Copy, Clone, Debug)]
#[non_exhaustive]
#[repr(u8)]
pub enum ActionRole {
    Create = 30,
    Update = 31,
    Delete = 32,
}

#[derive(Copy, Clone, Debug)]
#[non_exhaustive]
#[repr(u8)]
pub enum ActionInvite {
    Create = 40,
    Update = 41,
    Delete = 42,
}

#[derive(Copy, Clone, Debug)]
#[non_exhaustive]
#[repr(u8)]
pub enum ActionWebhook {
    Create = 50,
    Update = 51,
    Delete = 52,
}

#[derive(Copy, Clone, Debug)]
#[non_exhaustive]
#[repr(u8)]
pub enum ActionEmoji {
    Create = 60,
    Update = 61,
    Delete = 62,
}

#[derive(Copy, Clone, Debug)]
#[non_exhaustive]
#[repr(u8)]
pub enum ActionMessage {
    Delete = 72,
    BulkDelete = 73,
    Pin = 74,
    Unpin = 75,
}

#[derive(Copy, Clone, Debug)]
#[non_exhaustive]
#[repr(u8)]
pub enum ActionIntegration {
    Create = 80,
    Update = 81,
    Delete = 82,
}

#[derive(Copy, Clone, Debug)]
#[non_exhaustive]
#[repr(u8)]
pub enum ActionStageInstance {
    Create = 83,
    Update = 84,
    Delete = 85,
}

#[derive(Copy, Clone, Debug)]
#[non_exhaustive]
#[repr(u8)]
pub enum ActionSticker {
    Create = 90,
    Update = 91,
    Delete = 92,
}

#[derive(Copy, Clone, Debug)]
#[non_exhaustive]
#[repr(u8)]
pub enum ActionThread {
    Create = 110,
    Update = 111,
    Delete = 112,
}

#[derive(Debug, Deserialize, Serialize)]
#[non_exhaustive]
pub struct AuditLogs {
    #[serde(rename = "audit_log_entries")]
    pub entries: Vec<AuditLogEntry>,
    #[serde(with = "users")]
    pub users: HashMap<UserId, User>,
    #[serde(with = "webhooks")]
    pub webhooks: HashMap<WebhookId, Webhook>,
}

#[derive(Debug, Deserialize, Serialize)]
#[non_exhaustive]
pub struct AuditLogEntry {
    /// Determines to what entity an [`Self::action`] was used on.
    #[serde(with = "optional_string")]
    pub target_id: Option<u64>,
    /// Determines what action was done on a [`Self::target_id`]
    #[serde(rename = "action_type")]
    pub action: Action,
    /// What was the reasoning by doing an action on a target? If there was one.
    pub reason: Option<String>,
    /// The user that did this action on a target.
    pub user_id: UserId,
    /// What changes were made.
    pub changes: Option<Vec<Change>>,
    /// The id of this entry.
    pub id: AuditLogEntryId,
    /// Some optional data associated with this entry.
    pub options: Option<Options>,
}

#[derive(Debug, Deserialize, Serialize)]
#[non_exhaustive]
pub struct Options {
    /// Number of days after which inactive members were kicked.
    #[serde(default, with = "optional_string")]
    pub delete_member_days: Option<u64>,
    /// Number of members removed by the prune
    #[serde(default, with = "optional_string")]
    pub members_removed: Option<u64>,
    /// Channel in which the messages were deleted
    #[serde(default)]
    pub channel_id: Option<ChannelId>,
    /// Number of deleted messages.
    #[serde(default, with = "optional_string")]
    pub count: Option<u64>,
    /// Id of the overwritten entity
    #[serde(default)]
    pub id: Option<GenericId>,
    /// Type of overwritten entity ("member" or "role").
    #[serde(default, rename = "type")]
    pub kind: Option<String>,
    /// Message that was pinned or unpinned.
    #[serde(default)]
    pub message_id: Option<MessageId>,
    /// Name of the role if type is "role"
    #[serde(default)]
    pub role_name: Option<String>,
}

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

    #[test]
    fn action_value() {
        macro_rules! assert_action {
            ($action:pat, $num:literal) => {{
                let a = Action::from_value($num).expect("invalid action value");
                assert!(matches!(a, $action), "{:?} didn't match the variant", a);
                assert_eq!(a.num(), $num);
            }};
        }

        assert_action!(Action::GuildUpdate, 1);
        assert_action!(Action::Channel(ActionChannel::Create), 10);
        assert_action!(Action::Channel(ActionChannel::Update), 11);
        assert_action!(Action::Channel(ActionChannel::Delete), 12);
        assert_action!(Action::ChannelOverwrite(ActionChannelOverwrite::Create), 13);
        assert_action!(Action::ChannelOverwrite(ActionChannelOverwrite::Update), 14);
        assert_action!(Action::ChannelOverwrite(ActionChannelOverwrite::Delete), 15);
        assert_action!(Action::Member(ActionMember::Kick), 20);
        assert_action!(Action::Member(ActionMember::Prune), 21);
        assert_action!(Action::Member(ActionMember::BanAdd), 22);
        assert_action!(Action::Member(ActionMember::BanRemove), 23);
        assert_action!(Action::Member(ActionMember::Update), 24);
        assert_action!(Action::Member(ActionMember::RoleUpdate), 25);
        assert_action!(Action::Member(ActionMember::MemberMove), 26);
        assert_action!(Action::Member(ActionMember::MemberDisconnect), 27);
        assert_action!(Action::Member(ActionMember::BotAdd), 28);
        assert_action!(Action::Role(ActionRole::Create), 30);
        assert_action!(Action::Role(ActionRole::Update), 31);
        assert_action!(Action::Role(ActionRole::Delete), 32);
        assert_action!(Action::Invite(ActionInvite::Create), 40);
        assert_action!(Action::Invite(ActionInvite::Update), 41);
        assert_action!(Action::Invite(ActionInvite::Delete), 42);
        assert_action!(Action::Webhook(ActionWebhook::Create), 50);
        assert_action!(Action::Webhook(ActionWebhook::Update), 51);
        assert_action!(Action::Webhook(ActionWebhook::Delete), 52);
        assert_action!(Action::Emoji(ActionEmoji::Create), 60);
        assert_action!(Action::Emoji(ActionEmoji::Update), 61);
        assert_action!(Action::Emoji(ActionEmoji::Delete), 62);
        assert_action!(Action::Message(ActionMessage::Delete), 72);
        assert_action!(Action::Message(ActionMessage::BulkDelete), 73);
        assert_action!(Action::Message(ActionMessage::Pin), 74);
        assert_action!(Action::Message(ActionMessage::Unpin), 75);
        assert_action!(Action::Integration(ActionIntegration::Create), 80);
        assert_action!(Action::Integration(ActionIntegration::Update), 81);
        assert_action!(Action::Integration(ActionIntegration::Delete), 82);
        assert_action!(Action::StageInstance(ActionStageInstance::Create), 83);
        assert_action!(Action::StageInstance(ActionStageInstance::Update), 84);
        assert_action!(Action::StageInstance(ActionStageInstance::Delete), 85);
        assert_action!(Action::Sticker(ActionSticker::Create), 90);
        assert_action!(Action::Sticker(ActionSticker::Update), 91);
        assert_action!(Action::Sticker(ActionSticker::Delete), 92);
        assert_action!(Action::Thread(ActionThread::Create), 110);
        assert_action!(Action::Thread(ActionThread::Update), 111);
        assert_action!(Action::Thread(ActionThread::Delete), 112);
    }
}
<commit_msg>Rename the enum sub types of the audit log actions (#1769)<commit_after>//! Audit log types for administrative actions within guilds.

use std::{collections::HashMap, mem::transmute};

use serde::de::{self, Deserializer};
use serde::ser::{Serialize, Serializer};

mod change;
mod utils;

pub use change::{AffectedRole, Change, EntityType};
use utils::{optional_string, users, webhooks};

use crate::model::prelude::*;

/// Determines the action that was done on a target.
#[derive(Copy, Clone, Debug)]
#[non_exhaustive]
pub enum Action {
    GuildUpdate,
    Channel(ChannelAction),
    ChannelOverwrite(ChannelOverwriteAction),
    Member(MemberAction),
    Role(RoleAction),
    Invite(InviteAction),
    Webhook(WebhookAction),
    Emoji(EmojiAction),
    Message(MessageAction),
    Integration(IntegrationAction),
    StageInstance(StageInstanceAction),
    Sticker(StickerAction),
    Thread(ThreadAction),
}

impl Action {
    pub fn num(self) -> u8 {
        match self {
            Action::GuildUpdate => 1,
            Action::Channel(x) => x as u8,
            Action::ChannelOverwrite(x) => x as u8,
            Action::Member(x) => x as u8,
            Action::Role(x) => x as u8,
            Action::Invite(x) => x as u8,
            Action::Webhook(x) => x as u8,
            Action::Emoji(x) => x as u8,
            Action::Message(x) => x as u8,
            Action::Integration(x) => x as u8,
            Action::StageInstance(x) => x as u8,
            Action::Sticker(x) => x as u8,
            Action::Thread(x) => x as u8,
        }
    }

    pub fn from_value(value: u8) -> Option<Action> {
        let action = match value {
            1 => Action::GuildUpdate,
            10..=12 => Action::Channel(unsafe { transmute(value) }),
            13..=15 => Action::ChannelOverwrite(unsafe { transmute(value) }),
            20..=28 => Action::Member(unsafe { transmute(value) }),
            30..=32 => Action::Role(unsafe { transmute(value) }),
            40..=42 => Action::Invite(unsafe { transmute(value) }),
            50..=52 => Action::Webhook(unsafe { transmute(value) }),
            60..=62 => Action::Emoji(unsafe { transmute(value) }),
            72..=75 => Action::Message(unsafe { transmute(value) }),
            80..=82 => Action::Integration(unsafe { transmute(value) }),
            83..=85 => Action::StageInstance(unsafe { transmute(value) }),
            90..=92 => Action::Sticker(unsafe { transmute(value) }),
            110..=112 => Action::Thread(unsafe { transmute(value) }),
            _ => return None,
        };

        Some(action)
    }
}

impl<'de> Deserialize<'de> for Action {
    fn deserialize<D: Deserializer<'de>>(deserializer: D) -> StdResult<Self, D::Error> {
        let value = u8::deserialize(deserializer)?;
        Action::from_value(value)
            .ok_or_else(|| de::Error::custom(format!("Unexpected action number: {}", value)))
    }
}

impl Serialize for Action {
    fn serialize<S: Serializer>(&self, serializer: S) -> StdResult<S::Ok, S::Error> {
        serializer.serialize_u8(self.num())
    }
}

#[deprecated(note = "use `ChannelAction`")]
pub type ActionChannel = ChannelAction;

#[derive(Copy, Clone, Debug)]
#[non_exhaustive]
#[repr(u8)]
pub enum ChannelAction {
    Create = 10,
    Update = 11,
    Delete = 12,
}

#[deprecated(note = "use `ChannelOverwriteAction`")]
pub type ActionChannelOverwrite = ChannelOverwriteAction;

#[derive(Copy, Clone, Debug)]
#[non_exhaustive]
#[repr(u8)]
pub enum ChannelOverwriteAction {
    Create = 13,
    Update = 14,
    Delete = 15,
}

#[deprecated(note = "use `MemberAction`")]
pub type ActionMember = MemberAction;

#[derive(Copy, Clone, Debug)]
#[non_exhaustive]
#[repr(u8)]
pub enum MemberAction {
    Kick = 20,
    Prune = 21,
    BanAdd = 22,
    BanRemove = 23,
    Update = 24,
    RoleUpdate = 25,
    MemberMove = 26,
    MemberDisconnect = 27,
    BotAdd = 28,
}

#[deprecated(note = "use `RoleAction`")]
pub type ActionRole = RoleAction;

#[derive(Copy, Clone, Debug)]
#[non_exhaustive]
#[repr(u8)]
pub enum RoleAction {
    Create = 30,
    Update = 31,
    Delete = 32,
}

#[deprecated(note = "use `InviteAction`")]
pub type ActionInvite = InviteAction;

#[derive(Copy, Clone, Debug)]
#[non_exhaustive]
#[repr(u8)]
pub enum InviteAction {
    Create = 40,
    Update = 41,
    Delete = 42,
}

#[deprecated(note = "use `WebhookAction`")]
pub type ActionWebhook = WebhookAction;

#[derive(Copy, Clone, Debug)]
#[non_exhaustive]
#[repr(u8)]
pub enum WebhookAction {
    Create = 50,
    Update = 51,
    Delete = 52,
}

#[deprecated(note = "use `EmojiAction`")]
pub type ActionEmoji = EmojiAction;

#[derive(Copy, Clone, Debug)]
#[non_exhaustive]
#[repr(u8)]
pub enum EmojiAction {
    Create = 60,
    Update = 61,
    Delete = 62,
}

#[deprecated(note = "use `MessageAction`")]
pub type ActionMessage = MessageAction;

#[derive(Copy, Clone, Debug)]
#[non_exhaustive]
#[repr(u8)]
pub enum MessageAction {
    Delete = 72,
    BulkDelete = 73,
    Pin = 74,
    Unpin = 75,
}

#[deprecated(note = "use `IntegrationAction`")]
pub type ActionIntegration = IntegrationAction;

#[derive(Copy, Clone, Debug)]
#[non_exhaustive]
#[repr(u8)]
pub enum IntegrationAction {
    Create = 80,
    Update = 81,
    Delete = 82,
}

#[deprecated(note = "use `StageInstanceAction`")]
pub type ActionStageInstance = StageInstanceAction;

#[derive(Copy, Clone, Debug)]
#[non_exhaustive]
#[repr(u8)]
pub enum StageInstanceAction {
    Create = 83,
    Update = 84,
    Delete = 85,
}

#[deprecated(note = "use `StickerAction`")]
pub type ActionSticker = StickerAction;

#[derive(Copy, Clone, Debug)]
#[non_exhaustive]
#[repr(u8)]
pub enum StickerAction {
    Create = 90,
    Update = 91,
    Delete = 92,
}

#[deprecated(note = "use `ThreadAction`")]
pub type ActionThread = ThreadAction;

#[derive(Copy, Clone, Debug)]
#[non_exhaustive]
#[repr(u8)]
pub enum ThreadAction {
    Create = 110,
    Update = 111,
    Delete = 112,
}

#[derive(Debug, Deserialize, Serialize)]
#[non_exhaustive]
pub struct AuditLogs {
    #[serde(rename = "audit_log_entries")]
    pub entries: Vec<AuditLogEntry>,
    #[serde(with = "users")]
    pub users: HashMap<UserId, User>,
    #[serde(with = "webhooks")]
    pub webhooks: HashMap<WebhookId, Webhook>,
}

#[derive(Debug, Deserialize, Serialize)]
#[non_exhaustive]
pub struct AuditLogEntry {
    /// Determines to what entity an [`Self::action`] was used on.
    #[serde(with = "optional_string")]
    pub target_id: Option<u64>,
    /// Determines what action was done on a [`Self::target_id`]
    #[serde(rename = "action_type")]
    pub action: Action,
    /// What was the reasoning by doing an action on a target? If there was one.
    pub reason: Option<String>,
    /// The user that did this action on a target.
    pub user_id: UserId,
    /// What changes were made.
    pub changes: Option<Vec<Change>>,
    /// The id of this entry.
    pub id: AuditLogEntryId,
    /// Some optional data associated with this entry.
    pub options: Option<Options>,
}

#[derive(Debug, Deserialize, Serialize)]
#[non_exhaustive]
pub struct Options {
    /// Number of days after which inactive members were kicked.
    #[serde(default, with = "optional_string")]
    pub delete_member_days: Option<u64>,
    /// Number of members removed by the prune
    #[serde(default, with = "optional_string")]
    pub members_removed: Option<u64>,
    /// Channel in which the messages were deleted
    #[serde(default)]
    pub channel_id: Option<ChannelId>,
    /// Number of deleted messages.
    #[serde(default, with = "optional_string")]
    pub count: Option<u64>,
    /// Id of the overwritten entity
    #[serde(default)]
    pub id: Option<GenericId>,
    /// Type of overwritten entity ("member" or "role").
    #[serde(default, rename = "type")]
    pub kind: Option<String>,
    /// Message that was pinned or unpinned.
    #[serde(default)]
    pub message_id: Option<MessageId>,
    /// Name of the role if type is "role"
    #[serde(default)]
    pub role_name: Option<String>,
}

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

    #[test]
    fn action_value() {
        macro_rules! assert_action {
            ($action:pat, $num:literal) => {{
                let a = Action::from_value($num).expect("invalid action value");
                assert!(matches!(a, $action), "{:?} didn't match the variant", a);
                assert_eq!(a.num(), $num);
            }};
        }

        assert_action!(Action::GuildUpdate, 1);
        assert_action!(Action::Channel(ChannelAction::Create), 10);
        assert_action!(Action::Channel(ChannelAction::Update), 11);
        assert_action!(Action::Channel(ChannelAction::Delete), 12);
        assert_action!(Action::ChannelOverwrite(ChannelOverwriteAction::Create), 13);
        assert_action!(Action::ChannelOverwrite(ChannelOverwriteAction::Update), 14);
        assert_action!(Action::ChannelOverwrite(ChannelOverwriteAction::Delete), 15);
        assert_action!(Action::Member(MemberAction::Kick), 20);
        assert_action!(Action::Member(MemberAction::Prune), 21);
        assert_action!(Action::Member(MemberAction::BanAdd), 22);
        assert_action!(Action::Member(MemberAction::BanRemove), 23);
        assert_action!(Action::Member(MemberAction::Update), 24);
        assert_action!(Action::Member(MemberAction::RoleUpdate), 25);
        assert_action!(Action::Member(MemberAction::MemberMove), 26);
        assert_action!(Action::Member(MemberAction::MemberDisconnect), 27);
        assert_action!(Action::Member(MemberAction::BotAdd), 28);
        assert_action!(Action::Role(RoleAction::Create), 30);
        assert_action!(Action::Role(RoleAction::Update), 31);
        assert_action!(Action::Role(RoleAction::Delete), 32);
        assert_action!(Action::Invite(InviteAction::Create), 40);
        assert_action!(Action::Invite(InviteAction::Update), 41);
        assert_action!(Action::Invite(InviteAction::Delete), 42);
        assert_action!(Action::Webhook(WebhookAction::Create), 50);
        assert_action!(Action::Webhook(WebhookAction::Update), 51);
        assert_action!(Action::Webhook(WebhookAction::Delete), 52);
        assert_action!(Action::Emoji(EmojiAction::Create), 60);
        assert_action!(Action::Emoji(EmojiAction::Update), 61);
        assert_action!(Action::Emoji(EmojiAction::Delete), 62);
        assert_action!(Action::Message(MessageAction::Delete), 72);
        assert_action!(Action::Message(MessageAction::BulkDelete), 73);
        assert_action!(Action::Message(MessageAction::Pin), 74);
        assert_action!(Action::Message(MessageAction::Unpin), 75);
        assert_action!(Action::Integration(IntegrationAction::Create), 80);
        assert_action!(Action::Integration(IntegrationAction::Update), 81);
        assert_action!(Action::Integration(IntegrationAction::Delete), 82);
        assert_action!(Action::StageInstance(StageInstanceAction::Create), 83);
        assert_action!(Action::StageInstance(StageInstanceAction::Update), 84);
        assert_action!(Action::StageInstance(StageInstanceAction::Delete), 85);
        assert_action!(Action::Sticker(StickerAction::Create), 90);
        assert_action!(Action::Sticker(StickerAction::Update), 91);
        assert_action!(Action::Sticker(StickerAction::Delete), 92);
        assert_action!(Action::Thread(ThreadAction::Create), 110);
        assert_action!(Action::Thread(ThreadAction::Update), 111);
        assert_action!(Action::Thread(ThreadAction::Delete), 112);
    }
}
<|endoftext|>


<commit_before>extern crate sdl2;

use sdl2::render::{Renderer, Texture};
use sdl2_image::LoadTexture;
use std::path::Path;
use std::collections::HashMap;
use std::cell::RefCell;
use std::rc::Rc;
use sdl2::rect;

trait ImageLoader {
    type Image;
    fn load(&self, path: &Path) -> Result<Self::Image, String>;
    fn draw(&mut self,
            image: &Self::Image,
            src: Option<rect::Rect>,
            dst: Option<rect::Rect>)
            -> Result<(), String>;
}

impl<'a> ImageLoader for Renderer<'a> {
    type Image = Texture;
    fn load(&self, path: &Path) -> Result<Texture, String> {
        self.load_texture(path)
    }

    fn draw(&mut self,
            image: &Texture,
            src: Option<rect::Rect>,
            dst: Option<rect::Rect>)
            -> Result<(), String> {
        self.copy(image, src, dst)
    }
}

struct ResourceManager<'a, I: ImageLoader> {
    texture_cache: RefCell<HashMap<&'a str, Rc<I::Image>>>,
    image_loader: I,
}

impl<'a, I: ImageLoader> ResourceManager<'a, I> {
    pub fn new(image_loader: I) -> Self {
        ResourceManager {
            texture_cache: RefCell::new(HashMap::new()),
            image_loader: image_loader,
        }
    }

    pub fn getTexture(&self, path: &'a str) -> Result<Rc<I::Image>, String> {
        {
            let cache = self.texture_cache.borrow();
            let image = cache.get(path);
            if let Some(x) = image {
                return Ok(x.clone());
            }
        }
        let mut cache = self.texture_cache.borrow_mut();
        let image_path = Path::new(path);
        let image = Rc::new(try!(self.image_loader.load(image_path)));
        cache.insert(path, image.clone());
        Ok(image.clone())
    }
}

#[cfg(test)]
mod test {
    use std::path::Path;
    use std::cell::RefCell;
    use sdl2::rect;
    use super::ImageLoader;
    use super::ResourceManager;

    #[derive(Debug)]
    struct MockImage {
        pub path: String,
        pub id: u16,
    }

    struct MockImageLoader {
        pub load_count: RefCell<u16>,
        pub error: Option<String>,
    }

    impl ImageLoader for MockImageLoader {
        type Image = MockImage;

        fn load(&self, path: &Path) -> Result<MockImage, String> {
            *self.load_count.borrow_mut() += 1;
            match self.error {
                None => {
                    Ok(MockImage {
                        path: path.to_str().unwrap_or("").into(),
                        id: *self.load_count.borrow(),
                    })
                }
                Some(ref e) => Err(e.clone()),
            }
        }

        fn draw(&mut self,
                image: &MockImage,
                src: Option<rect::Rect>,
                dst: Option<rect::Rect>)
                -> Result<(), String> {
            Ok(())
        }
    }

    #[test]
    fn loads_image() {
        let image_loader = MockImageLoader {
            load_count: RefCell::new(0),
            error: None,
        };
        let subject = ResourceManager::new(image_loader);
        let image = subject.getTexture("mypath/").unwrap();
        assert_eq!(image.path, String::from("mypath/"));
        assert_eq!(image.id, 1);
    }

    #[test]
    fn returns_error() {
        let image_loader = MockImageLoader {
            load_count: RefCell::new(0),
            error: Some("FAIL".into()),
        };
        let subject = ResourceManager::new(image_loader);
        let image = subject.getTexture("mypath/");
        assert_eq!(image.err(), Some("FAIL".into()));
    }

    #[test]
    fn caches_images() {
        let image_loader = MockImageLoader {
            load_count: RefCell::new(0),
            error: None,
        };
        let subject = ResourceManager::new(image_loader);

        // get a new image - ID will be 1
        let image1 = subject.getTexture("mypath/1").unwrap();
        assert_eq!(image1.path, String::from("mypath/1"));
        assert_eq!(image1.id, 1);

        // get the image again - ID should not change
        let image2 = subject.getTexture("mypath/1").unwrap();
        assert_eq!(image2.path, String::from("mypath/1"));
        assert_eq!(image2.id, 1);

        // get another time - ID should be a new one
        let image3 = subject.getTexture("mypath/2").unwrap();
        assert_eq!(image3.path, String::from("mypath/2"));
        assert_eq!(image3.id, 2);
    }
}
<commit_msg>adding renderer tracker<commit_after>extern crate sdl2;

use sdl2::render::Renderer as SdlRenderer;
use sdl2::render::Texture as SdlTexture;
use sdl2_image::LoadTexture;
use std::path::Path;
use std::collections::HashMap;
use std::cell::RefCell;
use std::rc::Rc;
use sdl2::rect;

trait Renderer {
    type Texture;
    fn load_texture(&self, path: &Path) -> Result<Self::Texture, String>;
    fn draw(&mut self,
            image: Rc<Self::Texture>,
            src: Option<rect::Rect>,
            dst: Option<rect::Rect>)
            -> Result<(), String>;
}

impl<'a> Renderer for SdlRenderer<'a> {
    type Texture = SdlTexture;

    fn load_texture(&self, path: &Path) -> Result<SdlTexture, String> {
        LoadTexture::load_texture(self, path)
    }

    fn draw(&mut self,
            image: Rc<SdlTexture>,
            src: Option<rect::Rect>,
            dst: Option<rect::Rect>)
            -> Result<(), String> {
        self.copy(&*image, src, dst)
    }
}

struct ResourceManager<'a, I: Renderer> {
    texture_cache: RefCell<HashMap<&'a str, Rc<I::Texture>>>,
    image_loader: I,
}

impl<'a, I: Renderer> ResourceManager<'a, I> {
    pub fn new(image_loader: I) -> Self {
        ResourceManager {
            texture_cache: RefCell::new(HashMap::new()),
            image_loader: image_loader,
        }
    }

    pub fn load_texture(&self, path: &'a str) -> Result<Rc<I::Texture>, String> {
        {
            let cache = self.texture_cache.borrow();
            let image = cache.get(path);
            if let Some(x) = image {
                return Ok(x.clone());
            }
        }
        let mut cache = self.texture_cache.borrow_mut();
        let image_path = Path::new(path);
        let image = Rc::new(try!(self.image_loader.load_texture(image_path)));
        cache.insert(path, image.clone());
        Ok(image.clone())
    }
}

#[cfg(test)]
mod test {
    use std::path::Path;
    use std::cell::RefCell;
    use std::rc::Rc;
    use sdl2::rect;
    use super::Renderer;
    use super::ResourceManager;

    #[derive(Debug)]
    struct MockTexture {
        path: String,
    }

    struct RendererTracker {
        load_count: u16,
        last_img: Rc<MockTexture>,
        last_src: Option<rect::Rect>,
        last_dst: Option<rect::Rect>,
    }

    impl RendererTracker {
        fn new() -> Self {
            RendererTracker {
                load_count: 0,
                last_img: Rc::new(MockTexture { path: "NULL".into() }),
                last_dst: None,
                last_src: None,
            }
        }
    }

    struct MockRenderer {
        error: Option<String>,
        tracker: Rc<RefCell<RendererTracker>>,
    }

    impl Renderer for MockRenderer {
        type Texture = MockTexture;

        fn load_texture(&self, path: &Path) -> Result<MockTexture, String> {
            self.tracker.borrow_mut().load_count += 1;
            match self.error {
                None => Ok(MockTexture { path: path.to_str().unwrap_or("").into() }),
                Some(ref e) => Err(e.clone()),
            }
        }

        fn draw(&mut self,
                image: Rc<MockTexture>,
                src: Option<rect::Rect>,
                dst: Option<rect::Rect>)
                -> Result<(), String> {
            match self.error {
                None => {
                    let mut tracker = self.tracker.borrow_mut();
                    tracker.last_img = image;
                    tracker.last_src = src;
                    tracker.last_dst = dst;
                    Ok(())
                }
                Some(ref e) => Err(e.clone()),
            }
        }
    }

    fn new_subject<'a>(error: Option<String>)
                       -> (ResourceManager<'a, MockRenderer>, Rc<RefCell<RendererTracker>>) {
        let tracker = Rc::new(RefCell::new(RendererTracker::new()));
        let image_loader = MockRenderer {
            error: error,
            tracker: tracker.clone(),
        };

        let subject = ResourceManager::new(image_loader);
        (subject, tracker)
    }

    #[test]
    fn loads_image() {
        let (subject, tracker) = new_subject(None);
        let image = subject.load_texture("mypath/").unwrap();
        assert_eq!(image.path, String::from("mypath/"));
        assert_eq!(tracker.borrow().load_count, 1);
    }

    #[test]
    fn returns_error() {
        let (subject, tracker) = new_subject(Some("FAIL".into()));
        let image = subject.load_texture("mypath/");
        assert_eq!(image.err(), Some("FAIL".into()));
        assert_eq!(tracker.borrow().load_count, 1);
    }

    #[test]
    fn caches_images() {
        let (subject, tracker) = new_subject(None);

        // get a new image - number of calls is 1
        let image1 = subject.load_texture("mypath/1").unwrap();
        assert_eq!(image1.path, String::from("mypath/1"));
        assert_eq!(tracker.borrow().load_count, 1);

        // load the same image - number of calls should still be 1
        let image2 = subject.load_texture("mypath/1").unwrap();
        assert_eq!(image2.path, String::from("mypath/1"));
        assert_eq!(tracker.borrow().load_count, 1);

        // load a different image - number of calls should increase
        let image3 = subject.load_texture("mypath/2").unwrap();
        assert_eq!(image3.path, String::from("mypath/2"));
        assert_eq!(tracker.borrow().load_count, 2);
    }

    #[test]
    fn draws_images() {
        let (subject, tracker) = new_subject(None);
        let image = subject.load_texture("mypath/").unwrap();
    }
}
<|endoftext|>


<commit_before>use seed::prelude::*;
use crate::Msg;

mod perfects_utils {
	pub struct Perfect<'a> {
		pub n: u64,
		pub p: u64,
		pub digits: u64,
		pub discovery: &'a str,
	}

	pub fn perfects<'a>() -> Vec<Perfect<'a>> {
        vec![
			Perfect {n: 51, p: 82589934, digits: 49724095, discovery: "2018 Laroche, Woltman, Kurowski, Blosser, et al."},
			Perfect {n: 50, p: 77232917, digits: 46498850, discovery: "2017 Jonathan Pace, George Woltman, Scott Kurowski, Aaron Blosser, et al.." },
			Perfect {n: 49, p: 74207281, digits: 44677235, discovery: "2016 Cooper, Woltman, Kurowski, Blosser et al." },
			Perfect {n: 48, p: 57885161, digits: 34850340, discovery: "2013 Cooper, Woltman, Kurowski, et al." },
			Perfect {n: 47, p: 43112609, digits: 25956377, discovery: "2008 Smith, Woltman, Kurowski, et al." },
			Perfect {n: 46, p: 42643801, digits: 42643801, discovery: "2009 Strindmo, Woltman, Kurowski, et al." },
			Perfect {n: 45, p: 37156667, digits: 22370543, discovery: "2008 Elvenich, Woltman, Kurowski, et al." },
			Perfect {n: 44, p: 32582657, digits: 19616714, discovery: "2006 Cooper, Boone, Woltman, Kurowski, et al." },
			Perfect {n: 43, p: 30402457, digits: 18304103, discovery: "2005 Cooper, Boone, Woltman, Kurowski, et al." },
			Perfect {n: 42, p: 25964951, digits: 15632458, discovery: "2005 Nowak, Woltman, Kurowski, et. al." },
			Perfect {n: 41, p: 24036583, digits: 14471465, discovery: "2004 Findley, Woltman, Kurowski, et. al." },
			Perfect {n: 40, p: 20996011, digits: 12640858, discovery: "2003 Shafer, Woltman, Kurowski, et. al." },
			Perfect {n: 39, p: 13466917, digits: 8107892, discovery: "2001 Cameron, Woltman, Kurowski, et. al." },
			Perfect {n: 38, p: 6972593, digits: 4197919, discovery: "1999 Hajratwala, Woltman, Kurowski, et. al." },
			Perfect {n: 37, p: 3021377, digits: 1819050, discovery: "1998 Clarkson, Woltman, Kurowski, et. al." },
			Perfect {n: 36, p: 2976221, digits: 1791864, discovery: "1997 Spence, Woltman, et. al." },
			Perfect {n: 35, p: 1398269, digits: 841842, discovery: "1996 Armengaud, Woltman, et. al." },
			Perfect {n: 34, p: 1257787, digits: 757263, discovery: "1996 Slowinski&Gage" },
			Perfect {n: 33, p: 859433, digits: 517430, discovery: "1994 Slowinski&Gage" },
			Perfect {n: 32, p: 756839, digits: 455663, discovery: "1992 Slowinski&Gage" },
			Perfect {n: 31, p: 216091, digits: 130100, discovery: "1985 Slowinski" },
			Perfect {n: 30, p: 132049, digits: 79502, discovery: "1983 Slowinski" },
			Perfect {n: 29, p: 110503, digits: 66530, discovery: "1988 Colquitt&Welsh" },
			Perfect {n: 28, p: 86243, digits: 51924, discovery: "1982 Slowinski" },
			Perfect {n: 27, p: 44497, digits: 26790, discovery: "1979 Nelson&Slowinski" },
			Perfect {n: 26, p: 23209, digits: 13973, discovery: "1979 Noll" },
			Perfect {n: 25, p: 21701, digits: 13066, discovery: "1978 Noll&Nickel" },
			Perfect {n: 24, p: 19937, digits: 12003, discovery: "1971 Tuckerman" },
			Perfect {n: 23, p: 11213, digits: 6751, discovery: "1963 Gillies" },
			Perfect {n: 22, p: 9941, digits: 5985, discovery: "1963 Gillies" },
			Perfect {n: 21, p: 9689, digits: 5834, discovery: "1963 Gillies" },
			Perfect {n: 20, p: 4423, digits: 2663, discovery: "1961 Hurwitz" },
			Perfect {n: 19, p: 4253, digits: 2561, discovery: "1961 Hurwitz" },
			Perfect {n: 18, p: 3217, digits: 1937, discovery: "1957 Riesel" },
			Perfect {n: 17, p: 2281, digits: 1373, discovery: "1952 Robinson" },
			Perfect {n: 16, p: 2203, digits: 1327, discovery: "1952 Robinson" },
			Perfect {n: 15, p: 1279, digits: 770, discovery: "1952 Robinson" },
			Perfect {n: 14, p: 607, digits: 366, discovery: "1952 Robinson" },
			Perfect {n: 13, p: 521, digits: 314, discovery: "1952 Robinson" },
			Perfect {n: 12, p: 127, digits: 77, discovery: "1876 Lucas" },
			Perfect {n: 11, p: 107, digits: 65, discovery: "1914 Powers" },
			Perfect {n: 10, p: 89, digits: 54, discovery: "1911 Powers" },
			Perfect {n: 9, p: 61, digits: 37, discovery: "1883 Pervushin" },
			Perfect {n: 8, p: 31, digits: 19, discovery: "1772 Euler" },
			Perfect {n: 7, p: 19, digits: 12, discovery: "1588 Cataldi" },
			Perfect {n: 6, p: 17, digits: 10, discovery: "1588 Cataldi" },
			Perfect {n: 5, p: 13, digits: 8, discovery: "1456 ?" },
			Perfect {n: 4, p: 7, digits: 4, discovery: "?" },
			Perfect {n: 3, p: 5, digits: 3, discovery: "?" },
			Perfect {n: 2, p: 3, digits: 2, discovery: "?" },
			Perfect {n: 1, p: 3, digits: 1, discovery: "?" },
		]
	}
}

pub fn render() -> seed::dom_types::Node<Msg> {
    let mut html = vec![];

    let perfects = perfects_utils::perfects();

    for n in 0..perfects.len() {
        let download_txt:String = format!("https://static.bigprimes.net/archive/perfect/{}.txt",&perfects[n].n.to_string());

		let equation:String = format!("2{} × (2{}-1)",&(perfects[n].p-1).to_string(),&(perfects[n].p).to_string());

        html.push(
            tr![
                td![perfects[n].n.to_string()],//rank
				td![El::from_html(&equation)],//perfect number as a formula

                td![perfects[n].digits.to_string()],//digits in length
                td![perfects[n].discovery],//disocvery
                a![attrs!{At::Href => download_txt},"TXT"]//downloads
            ]
        );
    }

    div![
        h1!["The Perfect Numbers"],
        br![],
        br![],
        br![],
        table![
            attrs!{At::Class => "perfecttable text"},
            tbody![
                tr![
                    td![
                        b!["No."]
                    ],
                    td![
                        b!["Prime"]
                    ],
                    td![
                        b!["Digits"]
                    ],
                    td![
                        b!["Discovered"]
                    ],
                    td![
                        b!["Download"]
                    ]
                ],
                html
            ]
        ]
    ]
}

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

    #[test]
    fn perfect_test<'a>() {
        let mut perfects:Vec<perfects_utils::Perfect<'a>> = perfects_utils::perfects();
		perfects.reverse();
        assert_eq!(perfects[49].digits, 46498850);
        assert_eq!(perfects[39].digits, 12640858);
    }
}<commit_msg>Downloads now generated by wasm. But it's too slow.<commit_after>use seed::prelude::*;
use crate::Msg;

use num_traits::{pow,ToPrimitive};
use num_bigint::{BigUint,ToBigUint};

mod perfects_utils {
    use seed::prelude::*;
    use crate::Msg;

	pub struct Perfect<'a> {
		pub n: u64,
		pub p: u64,
		pub digits: u64,
		pub discovery: &'a str,
	}

	pub fn perfects<'a>() -> Vec<Perfect<'a>> {
        vec![
			/*Perfect {n: 51, p: 82589934, digits: 49724095, discovery: "2018 Laroche, Woltman, Kurowski, Blosser, et al."},
			Perfect {n: 50, p: 77232917, digits: 46498850, discovery: "2017 Jonathan Pace, George Woltman, Scott Kurowski, Aaron Blosser, et al.." },
			Perfect {n: 49, p: 74207281, digits: 44677235, discovery: "2016 Cooper, Woltman, Kurowski, Blosser et al." },
			Perfect {n: 48, p: 57885161, digits: 34850340, discovery: "2013 Cooper, Woltman, Kurowski, et al." },
			Perfect {n: 47, p: 43112609, digits: 25956377, discovery: "2008 Smith, Woltman, Kurowski, et al." },
			Perfect {n: 46, p: 42643801, digits: 42643801, discovery: "2009 Strindmo, Woltman, Kurowski, et al." },
			Perfect {n: 45, p: 37156667, digits: 22370543, discovery: "2008 Elvenich, Woltman, Kurowski, et al." },
			Perfect {n: 44, p: 32582657, digits: 19616714, discovery: "2006 Cooper, Boone, Woltman, Kurowski, et al." },
			Perfect {n: 43, p: 30402457, digits: 18304103, discovery: "2005 Cooper, Boone, Woltman, Kurowski, et al." },
			Perfect {n: 42, p: 25964951, digits: 15632458, discovery: "2005 Nowak, Woltman, Kurowski, et. al." },
			Perfect {n: 41, p: 24036583, digits: 14471465, discovery: "2004 Findley, Woltman, Kurowski, et. al." },
			Perfect {n: 40, p: 20996011, digits: 12640858, discovery: "2003 Shafer, Woltman, Kurowski, et. al." },
			Perfect {n: 39, p: 13466917, digits: 8107892, discovery: "2001 Cameron, Woltman, Kurowski, et. al." },
			Perfect {n: 38, p: 6972593, digits: 4197919, discovery: "1999 Hajratwala, Woltman, Kurowski, et. al." },
			Perfect {n: 37, p: 3021377, digits: 1819050, discovery: "1998 Clarkson, Woltman, Kurowski, et. al." },
			Perfect {n: 36, p: 2976221, digits: 1791864, discovery: "1997 Spence, Woltman, et. al." },
			Perfect {n: 35, p: 1398269, digits: 841842, discovery: "1996 Armengaud, Woltman, et. al." },
			Perfect {n: 34, p: 1257787, digits: 757263, discovery: "1996 Slowinski&Gage" },
			Perfect {n: 33, p: 859433, digits: 517430, discovery: "1994 Slowinski&Gage" },
			Perfect {n: 32, p: 756839, digits: 455663, discovery: "1992 Slowinski&Gage" },
			Perfect {n: 31, p: 216091, digits: 130100, discovery: "1985 Slowinski" },*/
			Perfect {n: 30, p: 132049, digits: 79502, discovery: "1983 Slowinski" },
			Perfect {n: 29, p: 110503, digits: 66530, discovery: "1988 Colquitt&Welsh" },
			Perfect {n: 28, p: 86243, digits: 51924, discovery: "1982 Slowinski" },
			Perfect {n: 27, p: 44497, digits: 26790, discovery: "1979 Nelson&Slowinski" },
			Perfect {n: 26, p: 23209, digits: 13973, discovery: "1979 Noll" },
			Perfect {n: 25, p: 21701, digits: 13066, discovery: "1978 Noll&Nickel" },
			Perfect {n: 24, p: 19937, digits: 12003, discovery: "1971 Tuckerman" },
			Perfect {n: 23, p: 11213, digits: 6751, discovery: "1963 Gillies" },
			Perfect {n: 22, p: 9941, digits: 5985, discovery: "1963 Gillies" },
			Perfect {n: 21, p: 9689, digits: 5834, discovery: "1963 Gillies" },
			Perfect {n: 20, p: 4423, digits: 2663, discovery: "1961 Hurwitz" },
			Perfect {n: 19, p: 4253, digits: 2561, discovery: "1961 Hurwitz" },
			Perfect {n: 18, p: 3217, digits: 1937, discovery: "1957 Riesel" },
			Perfect {n: 17, p: 2281, digits: 1373, discovery: "1952 Robinson" },
			Perfect {n: 16, p: 2203, digits: 1327, discovery: "1952 Robinson" },
			Perfect {n: 15, p: 1279, digits: 770, discovery: "1952 Robinson" },
			Perfect {n: 14, p: 607, digits: 366, discovery: "1952 Robinson" },
			Perfect {n: 13, p: 521, digits: 314, discovery: "1952 Robinson" },
			Perfect {n: 12, p: 127, digits: 77, discovery: "1876 Lucas" },
			Perfect {n: 11, p: 107, digits: 65, discovery: "1914 Powers" },
			Perfect {n: 10, p: 89, digits: 54, discovery: "1911 Powers" },
			Perfect {n: 9, p: 61, digits: 37, discovery: "1883 Pervushin" },
			Perfect {n: 8, p: 31, digits: 19, discovery: "1772 Euler" },
			Perfect {n: 7, p: 19, digits: 12, discovery: "1588 Cataldi" },
			Perfect {n: 6, p: 17, digits: 10, discovery: "1588 Cataldi" },
			Perfect {n: 5, p: 13, digits: 8, discovery: "1456 ?" },
			Perfect {n: 4, p: 7, digits: 4, discovery: "?" },
			Perfect {n: 3, p: 5, digits: 3, discovery: "?" },
			Perfect {n: 2, p: 3, digits: 2, discovery: "?" },
			Perfect {n: 1, p: 3, digits: 1, discovery: "?" },
		]
	}

	pub fn save_as_file(filename:String, filecontent:String) -> seed::dom_types::Node<Msg> {
        let href:String = vec!["data:text/plain,",&filecontent].into_iter().collect();
        a![attrs!{At::Download => &filename, At::Href => &href}, "TXT"]
    }
}

pub fn render() -> seed::dom_types::Node<Msg> {
    let mut html = vec![];
	let two:BigUint = 2.to_biguint().unwrap();

    let perfects = perfects_utils::perfects();

    for n in 0..perfects.len() {
		let download_filename:String = format!("P{}.txt",&perfects[n].n.to_string());
        let download_txt:String = format!("https://static.bigprimes.net/archive/perfect/{}.txt",&perfects[n].n.to_string());

		let equation:String = format!("2{} × (2{}-1)",&(perfects[n].p-1).to_string(),&(perfects[n].p).to_string());

		let p:usize = perfects[n].p.to_usize().unwrap();
		let power:BigUint = pow(two.clone(), p-1);
        let perfect_value:BigUint = power.clone() * ((power.clone()*two.clone()) -1.to_biguint().unwrap()) ;

        html.push(
            tr![
                td![perfects[n].n.to_string()],//rank
				td![El::from_html(&equation)],//perfect number as a formula

                td![perfects[n].digits.to_string()],//digits in length
                td![perfects[n].discovery],//disocvery
                td![a![attrs!{At::Href => download_txt},"TXT"]],//downloads
				td![perfects_utils::save_as_file(String::from(download_filename),perfect_value.to_string())],
            ]
        );
    }

    div![
        h1!["The Perfect Numbers"],
        br![],
        br![],
        br![],
        table![
            attrs!{At::Class => "perfecttable text"},
            tbody![
                tr![
                    td![
                        b!["No."]
                    ],
                    td![
                        b!["Prime"]
                    ],
                    td![
                        b!["Digits"]
                    ],
                    td![
                        b!["Discovered"]
                    ],
                    td![
                        b!["Download"]
                    ]
                ],
                html
            ]
        ]
    ]
}

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

    #[test]
    fn perfect_test<'a>() {
        let mut perfects:Vec<perfects_utils::Perfect<'a>> = perfects_utils::perfects();
		perfects.reverse();
        assert_eq!(perfects[49].digits, 46498850);
        assert_eq!(perfects[39].digits, 12640858);
    }
}<|endoftext|>


<commit_before>extern crate raw_window_handle;

use self::raw_window_handle::{HasRawWindowHandle, RawWindowHandle};
use crate::{sys::SDL_Window, video::Window};

unsafe impl HasRawWindowHandle for Window {
    fn raw_window_handle(&self) -> RawWindowHandle {
        use self::SDL_SYSWM_TYPE::*;
        let mut wm_info = SDL_SysWMinfo::default();
        if unsafe { SDL_GetWindowWMInfo(self.raw(), &mut wm_info) } == SDL_bool::SDL_FALSE {
            panic!("Couldn't get SDL window info: {}", crate::get_error());
        }
        match wm_info.subsystem {
            #[cfg(target_os = "windows")]
            SDL_SYSWM_WINDOWS => {
                use self::raw_window_handle::windows::WindowsHandle;
                RawWindowHandle::Windows(WindowsHandle {
                    hwnd: unsafe { wm_info.info.win }.window as *mut libc::c_void,
                    ..WindowsHandle::empty()
                })
            },
            #[cfg(any(
                target_os = "linux",
                target_os = "dragonfly",
                target_os = "freebsd",
                target_os = "netbsd",
                target_os = "openbsd",
            ))]
            SDL_SYSWM_WAYLAND => {
                use self::raw_window_handle::unix::WaylandHandle;
                RawWindowHandle::Wayland(WaylandHandle {
                    surface: unsafe { wm_info.info.wl }.surface as *mut libc::c_void,
                    display: unsafe { wm_info.info.wl }.display as *mut libc::c_void,
                    ..WaylandHandle::empty()
                })
            },
            #[cfg(any(
                target_os = "linux",
                target_os = "dragonfly",
                target_os = "freebsd",
                target_os = "netbsd",
                target_os = "openbsd",
            ))]
            SDL_SYSWM_X11 => {
                use self::raw_window_handle::unix::XlibHandle;
                RawWindowHandle::Xlib(XlibHandle {
                    window: unsafe { wm_info.info.x11 }.window,
                    display: unsafe { wm_info.info.x11 }.display as *mut libc::c_void,
                    ..XlibHandle::empty()
                })
            },
            #[cfg(target_os = "macos")]
            SDL_SYSWM_COCOA => {
                use self::raw_window_handle::macos::MacOSHandle;
                RawWindowHandle::MacOS(MacOSHandle {
                    ns_window: unsafe { wm_info.info.cocoa }.window as *mut libc::c_void,
                    ns_view: 0 as *mut libc::c_void, // consumer of RawWindowHandle should determine this
                    ..MacOSHandle::empty()
                })
            },
            SDL_SYSWM_ANDROID | SDL_SYSWM_UIKIT => {
                let window_system = match wm_info.subsystem {
                    SDL_SYSWM_ANDROID => "Android",
                    SDL_SYSWM_UIKIT => "iOS",
                    _ => unreachable!(),
                };
                panic!("raw-window-handle support for {} not yet implemented", window_system);
            },
            x => {
                let window_system = match x {
                    SDL_SYSWM_DIRECTFB => "DirectFB",
                    SDL_SYSWM_MIR => "Mir",
                    SDL_SYSWM_WINRT => "WinRT",
                    SDL_SYSWM_VIVANTE => "Vivante",
                    _ => unreachable!(),
                };
                panic!("{} window system is not supported, please file issue with raw-window-handle: https://github.com/rust-windowing/raw-window-handle/issues/new", window_system);
            },
        }
    }
}

extern "C" {
    fn SDL_GetWindowWMInfo(window: *mut SDL_Window, info: *mut SDL_SysWMinfo) -> SDL_bool;
}

#[repr(u32)]
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
#[allow(non_camel_case_types, dead_code)]
pub enum SDL_bool {
    SDL_FALSE = 0,
    SDL_TRUE = 1,
}

#[repr(C)]
#[derive(Debug, Default, Copy, Clone, PartialEq)]
#[allow(non_camel_case_types, dead_code)]
pub struct SDL_version {
    pub major: u8,
    pub minor: u8,
    pub patch: u8,
}

#[repr(u32)]
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
#[allow(non_camel_case_types, dead_code)]
pub enum SDL_SYSWM_TYPE {
    SDL_SYSWM_UNKNOWN = 0,
    SDL_SYSWM_WINDOWS = 1,
    SDL_SYSWM_X11 = 2,
    SDL_SYSWM_DIRECTFB = 3,
    SDL_SYSWM_COCOA = 4,
    SDL_SYSWM_UIKIT = 5,
    SDL_SYSWM_WAYLAND = 6,
    SDL_SYSWM_MIR = 7,
    SDL_SYSWM_WINRT = 8,
    SDL_SYSWM_ANDROID = 9,
    SDL_SYSWM_VIVANTE = 10,
    SDL_SYSWM_OS2 = 11,
}

impl Default for SDL_SYSWM_TYPE {
    fn default() -> Self {
        SDL_SYSWM_TYPE::SDL_SYSWM_UNKNOWN
    }
}

#[repr(C)]
#[derive(Default, Copy, Clone)]
#[allow(non_camel_case_types)]
pub struct SDL_SysWMinfo {
    pub version: SDL_version,
    pub subsystem: SDL_SYSWM_TYPE,

    #[cfg(target_os = "windows")]
    pub info: windows::WindowsSysWMinfo,

    #[cfg(any(
        target_os = "linux",
        target_os = "dragonfly",
        target_os = "freebsd",
        target_os = "netbsd",
        target_os = "openbsd",
    ))]
    pub info: linux::LinuxSysWMinfo,

    #[cfg(target_os = "macos")]
    pub info: macos::MacOSSysWMinfo,
}

#[cfg(target_os = "windows")]
pub mod windows {
    #[repr(C)]
    #[derive(Copy, Clone)]
    pub union WindowsSysWMinfo {
        pub win: Handles,
        pub dummy: [u8; 64usize],
        _bindgen_union_align: [u64; 8usize],
    }

    impl Default for WindowsSysWMinfo {
        fn default() -> Self {
            WindowsSysWMinfo {
                win: Handles::default(),
            }
        }
    }

    #[repr(C)]
    #[derive(Debug, Copy, Clone, PartialEq)]
    pub struct Handles {
        pub window: *mut HWND,
        pub hdc: *mut HDC,
        pub hinstance: *mut HINSTANCE,
    }

    impl Default for Handles {
        fn default() -> Self {
            Handles {
                window: 0 as *mut HWND,
                hdc: 0 as *mut HDC,
                hinstance: 0 as *mut HINSTANCE,
            }
        }
    }

    #[repr(C)]
    #[derive(Debug, Default, Copy, Clone, PartialEq)]
    pub struct HWND {
        pub unused: libc::c_int,
    }

    #[repr(C)]
    #[derive(Debug, Default, Copy, Clone, PartialEq)]
    pub struct HDC {
        pub unused: libc::c_int,
    }

    #[repr(C)]
    #[derive(Debug, Default, Copy, Clone, PartialEq)]
    pub struct HINSTANCE {
        pub unused: libc::c_int,
    }
}

#[cfg(any(
    target_os = "linux",
    target_os = "dragonfly",
    target_os = "freebsd",
    target_os = "netbsd",
    target_os = "openbsd",
))]
pub mod linux {
    #[repr(C)]
    #[derive(Copy, Clone)]
    pub union LinuxSysWMinfo {
        pub x11: X11Info,
        pub wl: WaylandInfo,
        pub dummy: [u8; 64usize],
        _bindgen_union_align: [u32; 16usize],
    }

    impl Default for LinuxSysWMinfo {
        fn default() -> Self {
            LinuxSysWMinfo {
                wl: WaylandInfo::default(),
            }
        }
    }

    #[repr(C)]
    #[derive(Debug, Copy, Clone, PartialEq)]
    pub struct X11Info {
        pub display: *mut Display,
        pub window: Window,
    }

    #[repr(C)]
    #[derive(Debug, Copy, Clone, PartialEq)]
    pub struct WaylandInfo {
        pub display: *mut WLDisplay,
        pub surface: *mut WLSurface,
        pub shell_surface: *mut WLShellSurface,
    }

    impl Default for WaylandInfo {
        fn default() -> Self {
            WaylandInfo {
                display: 0 as *mut WLDisplay,
                surface: 0 as *mut WLSurface,
                shell_surface: 0 as *mut WLShellSurface,
            }
        }
    }

    #[repr(C)]
    #[derive(Debug, Default, Copy, Clone, PartialEq)]
    pub struct WLDisplay {
        pub _address: u8,
    }

    #[repr(C)]
    #[derive(Debug, Default, Copy, Clone, PartialEq)]
    pub struct WLSurface {
        pub _address: u8,
    }

    #[repr(C)]
    #[derive(Debug, Default, Copy, Clone, PartialEq)]
    pub struct WLShellSurface {
        pub _address: u8,
    }

    #[repr(C)]
    #[derive(Debug, Copy, Clone)]
    pub struct Display {
        _unused: [u8; 0],
    }

    pub type Window = libc::c_ulong;
}

#[cfg(target_os = "macos")]
pub mod macos {
    #[repr(C)]
    #[derive(Copy, Clone)]
    pub union MacOSSysWMinfo {
        pub cocoa: CocoaInfo,
        pub dummy: [u8; 64usize],
        _bindgen_union_align: [u64; 8usize],
    }

    impl Default for MacOSSysWMinfo {
        fn default() -> Self {
            MacOSSysWMinfo {
                cocoa: CocoaInfo::default(),
            }
        }
    }

    #[repr(C)]
    #[derive(Debug, Copy, Clone, PartialEq)]
    pub struct CocoaInfo {
        pub window: *mut NSWindow,
    }

    impl Default for CocoaInfo {
        fn default() -> Self {
            CocoaInfo {
                window: 0 as *mut NSWindow,
            }
        }
    }

    #[repr(C)]
    #[derive(Debug, Copy, Clone)]
    pub struct NSWindow {
        _unused: [u8; 0],
    }
}
<commit_msg>Fix version detection by calling GetVersion before GetWindowWMInfo<commit_after>extern crate raw_window_handle;

use self::raw_window_handle::{HasRawWindowHandle, RawWindowHandle};
use crate::{sys::SDL_Window, video::Window};

unsafe impl HasRawWindowHandle for Window {
    fn raw_window_handle(&self) -> RawWindowHandle {
        use self::SDL_SYSWM_TYPE::*;
        let mut wm_info = SDL_SysWMinfo::default();
        SDL_GetVersion(&mut wm_info.version);
        if unsafe { SDL_GetWindowWMInfo(self.raw(), &mut wm_info) } == SDL_bool::SDL_FALSE {
            panic!("Couldn't get SDL window info: {}", crate::get_error());
        }
        match wm_info.subsystem {
            #[cfg(target_os = "windows")]
            SDL_SYSWM_WINDOWS => {
                use self::raw_window_handle::windows::WindowsHandle;
                RawWindowHandle::Windows(WindowsHandle {
                    hwnd: unsafe { wm_info.info.win }.window as *mut libc::c_void,
                    ..WindowsHandle::empty()
                })
            },
            #[cfg(any(
                target_os = "linux",
                target_os = "dragonfly",
                target_os = "freebsd",
                target_os = "netbsd",
                target_os = "openbsd",
            ))]
            SDL_SYSWM_WAYLAND => {
                use self::raw_window_handle::unix::WaylandHandle;
                RawWindowHandle::Wayland(WaylandHandle {
                    surface: unsafe { wm_info.info.wl }.surface as *mut libc::c_void,
                    display: unsafe { wm_info.info.wl }.display as *mut libc::c_void,
                    ..WaylandHandle::empty()
                })
            },
            #[cfg(any(
                target_os = "linux",
                target_os = "dragonfly",
                target_os = "freebsd",
                target_os = "netbsd",
                target_os = "openbsd",
            ))]
            SDL_SYSWM_X11 => {
                use self::raw_window_handle::unix::XlibHandle;
                RawWindowHandle::Xlib(XlibHandle {
                    window: unsafe { wm_info.info.x11 }.window,
                    display: unsafe { wm_info.info.x11 }.display as *mut libc::c_void,
                    ..XlibHandle::empty()
                })
            },
            #[cfg(target_os = "macos")]
            SDL_SYSWM_COCOA => {
                use self::raw_window_handle::macos::MacOSHandle;
                RawWindowHandle::MacOS(MacOSHandle {
                    ns_window: unsafe { wm_info.info.cocoa }.window as *mut libc::c_void,
                    ns_view: 0 as *mut libc::c_void, // consumer of RawWindowHandle should determine this
                    ..MacOSHandle::empty()
                })
            },
            SDL_SYSWM_ANDROID | SDL_SYSWM_UIKIT => {
                let window_system = match wm_info.subsystem {
                    SDL_SYSWM_ANDROID => "Android",
                    SDL_SYSWM_UIKIT => "iOS",
                    _ => unreachable!(),
                };
                panic!("raw-window-handle support for {} not yet implemented", window_system);
            },
            x => {
                let window_system = match x {
                    SDL_SYSWM_DIRECTFB => "DirectFB",
                    SDL_SYSWM_MIR => "Mir",
                    SDL_SYSWM_WINRT => "WinRT",
                    SDL_SYSWM_VIVANTE => "Vivante",
                    _ => unreachable!(),
                };
                panic!("{} window system is not supported, please file issue with raw-window-handle: https://github.com/rust-windowing/raw-window-handle/issues/new", window_system);
            },
        }
    }
}

extern "C" {
    fn SDL_GetWindowWMInfo(window: *mut SDL_Window, info: *mut SDL_SysWMinfo) -> SDL_bool;
}

extern "C" {
    pub fn SDL_GetVersion(version: *mut SDL_version);
}

#[repr(u32)]
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
#[allow(non_camel_case_types, dead_code)]
pub enum SDL_bool {
    SDL_FALSE = 0,
    SDL_TRUE = 1,
}

#[repr(C)]
#[derive(Debug, Default, Copy, Clone, PartialEq)]
#[allow(non_camel_case_types, dead_code)]
pub struct SDL_version {
    pub major: u8,
    pub minor: u8,
    pub patch: u8,
}

#[repr(u32)]
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
#[allow(non_camel_case_types, dead_code)]
pub enum SDL_SYSWM_TYPE {
    SDL_SYSWM_UNKNOWN = 0,
    SDL_SYSWM_WINDOWS = 1,
    SDL_SYSWM_X11 = 2,
    SDL_SYSWM_DIRECTFB = 3,
    SDL_SYSWM_COCOA = 4,
    SDL_SYSWM_UIKIT = 5,
    SDL_SYSWM_WAYLAND = 6,
    SDL_SYSWM_MIR = 7,
    SDL_SYSWM_WINRT = 8,
    SDL_SYSWM_ANDROID = 9,
    SDL_SYSWM_VIVANTE = 10,
    SDL_SYSWM_OS2 = 11,
}

impl Default for SDL_SYSWM_TYPE {
    fn default() -> Self {
        SDL_SYSWM_TYPE::SDL_SYSWM_UNKNOWN
    }
}

#[repr(C)]
#[derive(Default, Copy, Clone)]
#[allow(non_camel_case_types)]
pub struct SDL_SysWMinfo {
    pub version: SDL_version,
    pub subsystem: SDL_SYSWM_TYPE,

    #[cfg(target_os = "windows")]
    pub info: windows::WindowsSysWMinfo,

    #[cfg(any(
        target_os = "linux",
        target_os = "dragonfly",
        target_os = "freebsd",
        target_os = "netbsd",
        target_os = "openbsd",
    ))]
    pub info: linux::LinuxSysWMinfo,

    #[cfg(target_os = "macos")]
    pub info: macos::MacOSSysWMinfo,
}

#[cfg(target_os = "windows")]
pub mod windows {
    #[repr(C)]
    #[derive(Copy, Clone)]
    pub union WindowsSysWMinfo {
        pub win: Handles,
        pub dummy: [u8; 64usize],
        _bindgen_union_align: [u64; 8usize],
    }

    impl Default for WindowsSysWMinfo {
        fn default() -> Self {
            WindowsSysWMinfo {
                win: Handles::default(),
            }
        }
    }

    #[repr(C)]
    #[derive(Debug, Copy, Clone, PartialEq)]
    pub struct Handles {
        pub window: *mut HWND,
        pub hdc: *mut HDC,
        pub hinstance: *mut HINSTANCE,
    }

    impl Default for Handles {
        fn default() -> Self {
            Handles {
                window: 0 as *mut HWND,
                hdc: 0 as *mut HDC,
                hinstance: 0 as *mut HINSTANCE,
            }
        }
    }

    #[repr(C)]
    #[derive(Debug, Default, Copy, Clone, PartialEq)]
    pub struct HWND {
        pub unused: libc::c_int,
    }

    #[repr(C)]
    #[derive(Debug, Default, Copy, Clone, PartialEq)]
    pub struct HDC {
        pub unused: libc::c_int,
    }

    #[repr(C)]
    #[derive(Debug, Default, Copy, Clone, PartialEq)]
    pub struct HINSTANCE {
        pub unused: libc::c_int,
    }
}

#[cfg(any(
    target_os = "linux",
    target_os = "dragonfly",
    target_os = "freebsd",
    target_os = "netbsd",
    target_os = "openbsd",
))]
pub mod linux {
    #[repr(C)]
    #[derive(Copy, Clone)]
    pub union LinuxSysWMinfo {
        pub x11: X11Info,
        pub wl: WaylandInfo,
        pub dummy: [u8; 64usize],
        _bindgen_union_align: [u32; 16usize],
    }

    impl Default for LinuxSysWMinfo {
        fn default() -> Self {
            LinuxSysWMinfo {
                wl: WaylandInfo::default(),
            }
        }
    }

    #[repr(C)]
    #[derive(Debug, Copy, Clone, PartialEq)]
    pub struct X11Info {
        pub display: *mut Display,
        pub window: Window,
    }

    #[repr(C)]
    #[derive(Debug, Copy, Clone, PartialEq)]
    pub struct WaylandInfo {
        pub display: *mut WLDisplay,
        pub surface: *mut WLSurface,
        pub shell_surface: *mut WLShellSurface,
    }

    impl Default for WaylandInfo {
        fn default() -> Self {
            WaylandInfo {
                display: 0 as *mut WLDisplay,
                surface: 0 as *mut WLSurface,
                shell_surface: 0 as *mut WLShellSurface,
            }
        }
    }

    #[repr(C)]
    #[derive(Debug, Default, Copy, Clone, PartialEq)]
    pub struct WLDisplay {
        pub _address: u8,
    }

    #[repr(C)]
    #[derive(Debug, Default, Copy, Clone, PartialEq)]
    pub struct WLSurface {
        pub _address: u8,
    }

    #[repr(C)]
    #[derive(Debug, Default, Copy, Clone, PartialEq)]
    pub struct WLShellSurface {
        pub _address: u8,
    }

    #[repr(C)]
    #[derive(Debug, Copy, Clone)]
    pub struct Display {
        _unused: [u8; 0],
    }

    pub type Window = libc::c_ulong;
}

#[cfg(target_os = "macos")]
pub mod macos {
    #[repr(C)]
    #[derive(Copy, Clone)]
    pub union MacOSSysWMinfo {
        pub cocoa: CocoaInfo,
        pub dummy: [u8; 64usize],
        _bindgen_union_align: [u64; 8usize],
    }

    impl Default for MacOSSysWMinfo {
        fn default() -> Self {
            MacOSSysWMinfo {
                cocoa: CocoaInfo::default(),
            }
        }
    }

    #[repr(C)]
    #[derive(Debug, Copy, Clone, PartialEq)]
    pub struct CocoaInfo {
        pub window: *mut NSWindow,
    }

    impl Default for CocoaInfo {
        fn default() -> Self {
            CocoaInfo {
                window: 0 as *mut NSWindow,
            }
        }
    }

    #[repr(C)]
    #[derive(Debug, Copy, Clone)]
    pub struct NSWindow {
        _unused: [u8; 0],
    }
}
<|endoftext|>


<commit_before>// Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

use std::convert::TryInto;
use std::fs::{File, OpenOptions};
use std::io;
use std::os::unix::fs::OpenOptionsExt;
use std::path::PathBuf;
use std::sync::{Mutex, MutexGuard};

use libc::O_NONBLOCK;

use rate_limiter::{RateLimiter, TokenBucket};

/// Wrapper for configuring the microVM boot source.
pub mod boot_source;
/// Wrapper for configuring the block devices.
pub mod drive;
/// Wrapper over the microVM general information attached to the microVM.
pub mod instance_info;
/// Wrapper for configuring the logger.
pub mod logger;
/// Wrapper for configuring the memory and CPU of the microVM.
pub mod machine_config;
/// Wrapper for configuring the metrics.
pub mod metrics;
/// Wrapper for configuring the network devices attached to the microVM.
pub mod net;
/// Wrapper for configuring the vsock devices attached to the microVM.
pub mod vsock;

// TODO: Migrate the VMM public-facing code (i.e. interface) to use stateless structures,
// for receiving data/args, such as the below `RateLimiterConfig` and `TokenBucketConfig`.
// Also todo: find a better suffix than `Config`; it should illustrate the static nature
// of the enclosed data.
// Currently, data is passed around using live/stateful objects. Switching to static/stateless
// objects will simplify both the ownership model and serialization.
// Public access would then be more tightly regulated via `VmmAction`s, consisting of tuples like
// (entry-point-into-VMM-logic, stateless-args-structure).

/// A public-facing, stateless structure, holding all the data we need to create a TokenBucket
/// (live) object.
#[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq)]
pub struct TokenBucketConfig {
    /// See TokenBucket::size.
    pub size: u64,
    /// See TokenBucket::one_time_burst.
    pub one_time_burst: Option<u64>,
    /// See TokenBucket::refill_time.
    pub refill_time: u64,
}

impl Into<TokenBucket> for TokenBucketConfig {
    fn into(self) -> TokenBucket {
        TokenBucket::new(self.size, self.one_time_burst, self.refill_time)
    }
}

/// A public-facing, stateless structure, holding all the data we need to create a RateLimiter
/// (live) object.
#[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq)]
#[serde(deny_unknown_fields)]
pub struct RateLimiterConfig {
    /// Data used to initialize the RateLimiter::bandwidth bucket.
    pub bandwidth: Option<TokenBucketConfig>,
    /// Data used to initialize the RateLimiter::ops bucket.
    pub ops: Option<TokenBucketConfig>,
}

impl RateLimiterConfig {
    /// Updates the configuration, merging in new options from `new_config`.
    pub fn update(&mut self, new_config: &RateLimiterConfig) {
        if new_config.bandwidth.is_some() {
            self.bandwidth = new_config.bandwidth;
        }
        if new_config.ops.is_some() {
            self.ops = new_config.ops;
        }
    }
}

impl TryInto<RateLimiter> for RateLimiterConfig {
    type Error = io::Error;

    fn try_into(self) -> std::result::Result<RateLimiter, Self::Error> {
        let bw = self.bandwidth.unwrap_or_default();
        let ops = self.ops.unwrap_or_default();
        RateLimiter::new(
            bw.size,
            bw.one_time_burst,
            bw.refill_time,
            ops.size,
            ops.one_time_burst,
            ops.refill_time,
        )
    }
}

type Result<T> = std::result::Result<T, std::io::Error>;

/// Structure `Writer` used for writing to a FIFO.
pub struct Writer {
    line_writer: Mutex<io::LineWriter<File>>,
}

impl Writer {
    /// Create and open a FIFO for writing to it.
    /// In order to not block the instance if nobody is consuming the message that is flushed to the
    /// two pipes, we are opening it with `O_NONBLOCK` flag. In this case, writing to a pipe will
    /// start failing when reaching 64K of unconsumed content. Simultaneously,
    /// the `missed_metrics_count` metric will get increased.
    pub fn new(fifo_path: PathBuf) -> Result<Writer> {
        OpenOptions::new()
            .custom_flags(O_NONBLOCK)
            .read(true)
            .write(true)
            .open(&fifo_path)
            .map(|t| Writer {
                line_writer: Mutex::new(io::LineWriter::new(t)),
            })
    }

    fn get_line_writer(&self) -> MutexGuard<io::LineWriter<File>> {
        match self.line_writer.lock() {
            Ok(guard) => guard,
            // If a thread panics while holding this lock, the writer within should still be usable.
            // (we might get an incomplete log line or something like that).
            Err(poisoned) => poisoned.into_inner(),
        }
    }
}

impl io::Write for Writer {
    fn write(&mut self, msg: &[u8]) -> Result<(usize)> {
        let mut line_writer = self.get_line_writer();
        line_writer.write_all(msg).map(|()| msg.len())
    }

    fn flush(&mut self) -> Result<()> {
        let mut line_writer = self.get_line_writer();
        line_writer.flush()
    }
}

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

    use utils::tempfile::TempFile;

    use super::*;

    #[test]
    fn test_rate_limiter_configs() {
        const SIZE: u64 = 1024 * 1024;
        const ONE_TIME_BURST: u64 = 1024;
        const REFILL_TIME: u64 = 1000;

        let b: TokenBucket = TokenBucketConfig {
            size: SIZE,
            one_time_burst: Some(ONE_TIME_BURST),
            refill_time: REFILL_TIME,
        }
        .into();
        assert_eq!(b.capacity(), SIZE);
        assert_eq!(b.one_time_burst(), ONE_TIME_BURST);
        assert_eq!(b.refill_time_ms(), REFILL_TIME);

        let mut rlconf = RateLimiterConfig {
            bandwidth: Some(TokenBucketConfig {
                size: SIZE,
                one_time_burst: Some(ONE_TIME_BURST),
                refill_time: REFILL_TIME,
            }),
            ops: Some(TokenBucketConfig {
                size: SIZE * 2,
                one_time_burst: None,
                refill_time: REFILL_TIME * 2,
            }),
        };
        let rl: RateLimiter = rlconf.try_into().unwrap();
        assert_eq!(rl.bandwidth().unwrap().capacity(), SIZE);
        assert_eq!(rl.bandwidth().unwrap().one_time_burst(), ONE_TIME_BURST);
        assert_eq!(rl.bandwidth().unwrap().refill_time_ms(), REFILL_TIME);
        assert_eq!(rl.ops().unwrap().capacity(), SIZE * 2);
        assert_eq!(rl.ops().unwrap().one_time_burst(), 0);
        assert_eq!(rl.ops().unwrap().refill_time_ms(), REFILL_TIME * 2);

        rlconf.update(&RateLimiterConfig {
            bandwidth: Some(TokenBucketConfig {
                size: SIZE * 2,
                one_time_burst: Some(ONE_TIME_BURST * 2),
                refill_time: REFILL_TIME * 2,
            }),
            ops: None,
        });
        assert_eq!(rlconf.bandwidth.unwrap().size, SIZE * 2);
        assert_eq!(
            rlconf.bandwidth.unwrap().one_time_burst,
            Some(ONE_TIME_BURST * 2)
        );
        assert_eq!(rlconf.bandwidth.unwrap().refill_time, REFILL_TIME * 2);
        assert_eq!(rlconf.ops.unwrap().size, SIZE * 2);
        assert_eq!(rlconf.ops.unwrap().one_time_burst, None);
        assert_eq!(rlconf.ops.unwrap().refill_time, REFILL_TIME * 2);
    }

    #[test]
    fn test_log_writer() {
        let log_file_temp =
            TempFile::new().expect("Failed to create temporary output logging file.");
        let good_file = log_file_temp.as_path().to_path_buf();
        let res = Writer::new(good_file);
        assert!(res.is_ok());

        let mut fw = res.unwrap();
        let msg = String::from("some message");
        assert!(fw.write(&msg.as_bytes()).is_ok());
        assert!(fw.flush().is_ok());
    }
}
<commit_msg>[vmm_config] remove Mutex from Writer::line_writer<commit_after>// Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

use std::convert::TryInto;
use std::fs::{File, OpenOptions};
use std::io;
use std::os::unix::fs::OpenOptionsExt;
use std::path::PathBuf;

use libc::O_NONBLOCK;

use rate_limiter::{RateLimiter, TokenBucket};

/// Wrapper for configuring the microVM boot source.
pub mod boot_source;
/// Wrapper for configuring the block devices.
pub mod drive;
/// Wrapper over the microVM general information attached to the microVM.
pub mod instance_info;
/// Wrapper for configuring the logger.
pub mod logger;
/// Wrapper for configuring the memory and CPU of the microVM.
pub mod machine_config;
/// Wrapper for configuring the metrics.
pub mod metrics;
/// Wrapper for configuring the network devices attached to the microVM.
pub mod net;
/// Wrapper for configuring the vsock devices attached to the microVM.
pub mod vsock;

// TODO: Migrate the VMM public-facing code (i.e. interface) to use stateless structures,
// for receiving data/args, such as the below `RateLimiterConfig` and `TokenBucketConfig`.
// Also todo: find a better suffix than `Config`; it should illustrate the static nature
// of the enclosed data.
// Currently, data is passed around using live/stateful objects. Switching to static/stateless
// objects will simplify both the ownership model and serialization.
// Public access would then be more tightly regulated via `VmmAction`s, consisting of tuples like
// (entry-point-into-VMM-logic, stateless-args-structure).

/// A public-facing, stateless structure, holding all the data we need to create a TokenBucket
/// (live) object.
#[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq)]
pub struct TokenBucketConfig {
    /// See TokenBucket::size.
    pub size: u64,
    /// See TokenBucket::one_time_burst.
    pub one_time_burst: Option<u64>,
    /// See TokenBucket::refill_time.
    pub refill_time: u64,
}

impl Into<TokenBucket> for TokenBucketConfig {
    fn into(self) -> TokenBucket {
        TokenBucket::new(self.size, self.one_time_burst, self.refill_time)
    }
}

/// A public-facing, stateless structure, holding all the data we need to create a RateLimiter
/// (live) object.
#[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq)]
#[serde(deny_unknown_fields)]
pub struct RateLimiterConfig {
    /// Data used to initialize the RateLimiter::bandwidth bucket.
    pub bandwidth: Option<TokenBucketConfig>,
    /// Data used to initialize the RateLimiter::ops bucket.
    pub ops: Option<TokenBucketConfig>,
}

impl RateLimiterConfig {
    /// Updates the configuration, merging in new options from `new_config`.
    pub fn update(&mut self, new_config: &RateLimiterConfig) {
        if new_config.bandwidth.is_some() {
            self.bandwidth = new_config.bandwidth;
        }
        if new_config.ops.is_some() {
            self.ops = new_config.ops;
        }
    }
}

impl TryInto<RateLimiter> for RateLimiterConfig {
    type Error = io::Error;

    fn try_into(self) -> std::result::Result<RateLimiter, Self::Error> {
        let bw = self.bandwidth.unwrap_or_default();
        let ops = self.ops.unwrap_or_default();
        RateLimiter::new(
            bw.size,
            bw.one_time_burst,
            bw.refill_time,
            ops.size,
            ops.one_time_burst,
            ops.refill_time,
        )
    }
}

type Result<T> = std::result::Result<T, std::io::Error>;

/// Structure `Writer` used for writing to a FIFO.
pub struct Writer {
    line_writer: io::LineWriter<File>,
}

impl Writer {
    /// Create and open a FIFO for writing to it.
    /// In order to not block the instance if nobody is consuming the message that is flushed to the
    /// two pipes, we are opening it with `O_NONBLOCK` flag. In this case, writing to a pipe will
    /// start failing when reaching 64K of unconsumed content. Simultaneously,
    /// the `missed_metrics_count` metric will get increased.
    pub fn new(fifo_path: PathBuf) -> Result<Writer> {
        OpenOptions::new()
            .custom_flags(O_NONBLOCK)
            .read(true)
            .write(true)
            .open(&fifo_path)
            .map(|t| Writer {
                line_writer: io::LineWriter::new(t),
            })
    }
}

impl io::Write for Writer {
    fn write(&mut self, msg: &[u8]) -> Result<(usize)> {
        self.line_writer.write_all(msg).map(|()| msg.len())
    }

    fn flush(&mut self) -> Result<()> {
        self.line_writer.flush()
    }
}

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

    use utils::tempfile::TempFile;

    use super::*;

    #[test]
    fn test_rate_limiter_configs() {
        const SIZE: u64 = 1024 * 1024;
        const ONE_TIME_BURST: u64 = 1024;
        const REFILL_TIME: u64 = 1000;

        let b: TokenBucket = TokenBucketConfig {
            size: SIZE,
            one_time_burst: Some(ONE_TIME_BURST),
            refill_time: REFILL_TIME,
        }
        .into();
        assert_eq!(b.capacity(), SIZE);
        assert_eq!(b.one_time_burst(), ONE_TIME_BURST);
        assert_eq!(b.refill_time_ms(), REFILL_TIME);

        let mut rlconf = RateLimiterConfig {
            bandwidth: Some(TokenBucketConfig {
                size: SIZE,
                one_time_burst: Some(ONE_TIME_BURST),
                refill_time: REFILL_TIME,
            }),
            ops: Some(TokenBucketConfig {
                size: SIZE * 2,
                one_time_burst: None,
                refill_time: REFILL_TIME * 2,
            }),
        };
        let rl: RateLimiter = rlconf.try_into().unwrap();
        assert_eq!(rl.bandwidth().unwrap().capacity(), SIZE);
        assert_eq!(rl.bandwidth().unwrap().one_time_burst(), ONE_TIME_BURST);
        assert_eq!(rl.bandwidth().unwrap().refill_time_ms(), REFILL_TIME);
        assert_eq!(rl.ops().unwrap().capacity(), SIZE * 2);
        assert_eq!(rl.ops().unwrap().one_time_burst(), 0);
        assert_eq!(rl.ops().unwrap().refill_time_ms(), REFILL_TIME * 2);

        rlconf.update(&RateLimiterConfig {
            bandwidth: Some(TokenBucketConfig {
                size: SIZE * 2,
                one_time_burst: Some(ONE_TIME_BURST * 2),
                refill_time: REFILL_TIME * 2,
            }),
            ops: None,
        });
        assert_eq!(rlconf.bandwidth.unwrap().size, SIZE * 2);
        assert_eq!(
            rlconf.bandwidth.unwrap().one_time_burst,
            Some(ONE_TIME_BURST * 2)
        );
        assert_eq!(rlconf.bandwidth.unwrap().refill_time, REFILL_TIME * 2);
        assert_eq!(rlconf.ops.unwrap().size, SIZE * 2);
        assert_eq!(rlconf.ops.unwrap().one_time_burst, None);
        assert_eq!(rlconf.ops.unwrap().refill_time, REFILL_TIME * 2);
    }

    #[test]
    fn test_log_writer() {
        let log_file_temp =
            TempFile::new().expect("Failed to create temporary output logging file.");
        let good_file = log_file_temp.as_path().to_path_buf();
        let res = Writer::new(good_file);
        assert!(res.is_ok());

        let mut fw = res.unwrap();
        let msg = String::from("some message");
        assert!(fw.write(&msg.as_bytes()).is_ok());
        assert!(fw.flush().is_ok());
    }
}
<|endoftext|>