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

use std::collections::HashMap;
use std::mem::transmute;

use serde::de::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),
    Unknown(u8),
}

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

    // TODO: Change function to `from_value(u8) -> Action` in the next version and return
    // `Action::Unknown(u8)` for unknown values.
    #[must_use]
    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)?;
        Ok(Action::from_value(value).unwrap_or(Action::Unknown(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);

        // TODO: use `assert_action!` when `Action::from_value` returns `Action::Unknown`
        assert_eq!(Action::Unknown(234).num(), 234);
    }

    #[test]
    fn action_serde() {
        use serde_json::json;

        #[derive(Debug, Deserialize, Serialize)]
        struct T {
            action: Action,
        }

        let value = json!({
            "action": 234,
        });

        let value = serde_json::from_value::<T>(value).unwrap();
        assert_eq!(value.action.num(), 234);

        assert!(matches!(value.action, Action::Unknown(234)));
    }
}
<commit_msg>Add audit log action types for scheduled events (#1978)<commit_after>//! Audit log types for administrative actions within guilds.

use std::collections::HashMap;
use std::mem::transmute;

use serde::de::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),
    ScheduledEvent(ScheduledEventAction),
    Thread(ThreadAction),
    Unknown(u8),
}

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

    // TODO: Change function to `from_value(u8) -> Action` in the next version and return
    // `Action::Unknown(u8)` for unknown values.
    #[must_use]
    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) }),
            100..=102 => Action::ScheduledEvent(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)?;
        Ok(Action::from_value(value).unwrap_or(Action::Unknown(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,
}

#[derive(Copy, Clone, Debug)]
#[non_exhaustive]
#[repr(u8)]
pub enum ScheduledEventAction {
    Create = 100,
    Update = 101,
    Delete = 102,
}

#[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::ScheduledEvent(ScheduledEventAction::Create), 100);
        assert_action!(Action::ScheduledEvent(ScheduledEventAction::Update), 101);
        assert_action!(Action::ScheduledEvent(ScheduledEventAction::Delete), 102);
        assert_action!(Action::Thread(ThreadAction::Create), 110);
        assert_action!(Action::Thread(ThreadAction::Update), 111);
        assert_action!(Action::Thread(ThreadAction::Delete), 112);

        // TODO: use `assert_action!` when `Action::from_value` returns `Action::Unknown`
        assert_eq!(Action::Unknown(234).num(), 234);
    }

    #[test]
    fn action_serde() {
        use serde_json::json;

        #[derive(Debug, Deserialize, Serialize)]
        struct T {
            action: Action,
        }

        let value = json!({
            "action": 234,
        });

        let value = serde_json::from_value::<T>(value).unwrap();
        assert_eq!(value.action.num(), 234);

        assert!(matches!(value.action, Action::Unknown(234)));
    }
}
<|endoftext|>


<commit_before>extern crate curl;

use std;
use std::ascii::AsciiExt;
use std::collections::hash_map::HashMap;
use curl::http;
use curl::http::handle::Method;

pub fn method_from_str(s: &str) -> Method {
    match s.to_ascii_lowercase().as_str() {
        "options" => Method::Options,
        "get" => Method::Get,
        "head" => Method::Head,
        "post" => Method::Post,
        "put" => Method::Put,
        "patch" => Method::Patch,
        "delete" => Method::Delete,
        "trace" => Method::Trace,
        "connect" => Method::Connect,
        _ => { panic!(format!("Invalid method string {}", s)); }
    }
}

pub fn method_to_str(m: Method) -> &'static str {
    match m {
        Method::Options => "Options",
        Method::Get => "Get",
        Method::Head => "Head",
        Method::Post => "Post",
        Method::Put => "Put",
        Method::Patch => "Patch",
        Method::Delete => "Delete",
        Method::Trace => "Trace",
        Method::Connect => "Connect",
    }
}

pub struct SpagRequest {
    pub method: Method,
    pub headers: HashMap<String, String>,
    pub body: String,
    pub endpoint: String,
    pub uri: String,
}

impl SpagRequest {

    pub fn new(method: Method, endpoint: String, uri: String) -> SpagRequest {
        SpagRequest {
            method: method,
            body: String::new(),
            headers: HashMap::new(),
            endpoint: endpoint,
            uri: uri
        }
    }

    pub fn get_method_string(&self) -> &'static str {
        method_to_str(self.method)
    }

    pub fn set_body(&mut self, body: String) {
        self.body = body;
    }

    /// Headers is some iterable of Strings like "Content-type: application/json".
    /// Attempts to split on the ':' and panics if this fails.
    pub fn add_headers<'a, I: Iterator<Item=&'a String>>(&mut self, headers: I) {
        for header in headers {
            match http::header::parse(header.clone().into_bytes().as_slice()) {
                Some((name, val)) => { self.headers.insert(name.to_string(), val.to_string()); }
                None => { panic!(format!("Invalid header {:?}", header)); }
            }
        }
    }

    pub fn prepare<'a, 'b>(&'b self, handle: &'a mut http::Handle) -> http::Request<'a, 'b> {
        let uri = self.endpoint.to_string() + &self.uri;
        let headers = self.headers.iter().map(|(a, b)| (a.as_str(), b.as_str()));
        http::Request::new(handle, self.method)
            .uri(uri.to_string())
            .headers(headers)
            .body(&self.body)
    }
}

impl std::fmt::Debug for SpagRequest {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        f.debug_struct("SpagRequest")
            .field("method", &self.get_method_string())
            .field("headers", &self.headers)
            .field("endpoint", &self.endpoint)
            .field("uri", &self.uri)
            .field("body", &self.body)
            .finish()
    }
}
<commit_msg>Ensure that the right Content-Type header gets through<commit_after>extern crate curl;

use std;
use std::ascii::AsciiExt;
use std::collections::hash_map::HashMap;
use curl::http;
use curl::http::handle::Method;

pub fn method_from_str(s: &str) -> Method {
    match s.to_ascii_lowercase().as_str() {
        "options" => Method::Options,
        "get" => Method::Get,
        "head" => Method::Head,
        "post" => Method::Post,
        "put" => Method::Put,
        "patch" => Method::Patch,
        "delete" => Method::Delete,
        "trace" => Method::Trace,
        "connect" => Method::Connect,
        _ => { panic!(format!("Invalid method string {}", s)); }
    }
}

pub fn method_to_str(m: Method) -> &'static str {
    match m {
        Method::Options => "Options",
        Method::Get => "Get",
        Method::Head => "Head",
        Method::Post => "Post",
        Method::Put => "Put",
        Method::Patch => "Patch",
        Method::Delete => "Delete",
        Method::Trace => "Trace",
        Method::Connect => "Connect",
    }
}

pub struct SpagRequest {
    pub method: Method,
    pub headers: HashMap<String, String>,
    pub body: String,
    pub endpoint: String,
    pub uri: String,
}

impl SpagRequest {

    pub fn new(method: Method, endpoint: String, uri: String) -> SpagRequest {
        SpagRequest {
            method: method,
            body: String::new(),
            headers: HashMap::new(),
            endpoint: endpoint,
            uri: uri
        }
    }

    pub fn get_method_string(&self) -> &'static str {
        method_to_str(self.method)
    }

    pub fn set_body(&mut self, body: String) {
        self.body = body;
    }

    /// Headers is some iterable of Strings like "Content-type: application/json".
    /// Attempts to split on the ':' and panics if this fails.
    pub fn add_headers<'a, I: Iterator<Item=&'a String>>(&mut self, headers: I) {
        for rawheader in headers {
            // Strip out any spaces and separate the components
            let header = rawheader.replace(" ", "");
            let h: Vec<&str> = header.split(":").collect();

            // If the header didn't have a colon, or had more than 1, it's invalid
            if h.len() != 2 {
                panic!(format!("Invalid header {:?}", header));
            }

            // curl-rust handles content-type in an odd way, so this is necessary
            let lcname = h[0].to_lowercase();
            if lcname == "content-type" {
                self.headers.insert("Content-Type".to_string(), h[1].to_string());
            } else {
                self.headers.insert(h[0].to_string(), h[1].to_string());
            }
        }
    }

    pub fn prepare<'a, 'b>(&'b self, handle: &'a mut http::Handle) -> http::Request<'a, 'b> {
        let uri = self.endpoint.to_string() + &self.uri;
        let headers = self.headers.iter().map(|(a, b)| (a.as_str(), b.as_str()));
        http::Request::new(handle, self.method)
            .uri(uri.to_string())
            .headers(headers)
            .body(&self.body)
    }
}

impl std::fmt::Debug for SpagRequest {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        f.debug_struct("SpagRequest")
            .field("method", &self.get_method_string())
            .field("headers", &self.headers)
            .field("endpoint", &self.endpoint)
            .field("uri", &self.uri)
            .field("body", &self.body)
            .finish()
    }
}
<|endoftext|>


<commit_before>// "Tifflin" Kernel
// - By John Hodge (thePowersGang)
//
// Core/metadevs/storage.rs
// - Storage (block device) subsystem
use _common::*;
use core::atomic::{AtomicUint,ATOMIC_UINT_INIT};
use sync::mutex::LazyMutex;
use async::{ReadHandle,WriteHandle};
use lib::VecMap;

module_define!{Storage, [], init}

/// A unique handle to a storage volume (logical)
pub struct VolumeHandle
{
	lv_idx: usize,
}

pub struct PhysicalVolumeReg
{
	idx: usize,
}

/// Physical volume instance provided by driver
///
/// Provides the low-level methods to manipulate the underlying storage
pub trait PhysicalVolume
{
	/// Returns the volume name (must be unique to the system)
	fn name(&self) -> &str;	// Local lifetime string
	/// Returns the size of a filesystem block, must be a power of two >512
	fn blocksize(&self) -> usize;
	/// Returns the number of blocks in this volume (i.e. the capacity)
	fn capacity(&self) -> u64;

	/// Reads a number of blocks from the volume into the provided buffer
	///
	/// Reads `count` blocks starting with `blockidx` into the buffer `dst` (which will/should
	/// be the size of `count` blocks). The read is performed with the provided priority, where
	/// 0 is higest, and 255 is lowest.
	fn read<'a>(&'a self, prio: u8, blockidx: u64, count: usize, dst: &'a mut [u8]) -> Result<ReadHandle<'a,'a>, ()>;
	/// Writer a number of blocks to the volume
	fn write<'a>(&'a self, prio: u8, blockidx: u64, count: usize, src: &'a [u8]) -> Result<WriteHandle<'a,'a>,()>;
	/// Erases a number of blocks from the volume
	///
	/// Erases (requests the underlying storage forget about) `count` blocks starting at `blockidx`.
	/// This is functionally equivalent to the SSD "TRIM" command.
	fn wipe(&mut self, blockidx: u64, count: usize);
}

/// Registration for a physical volume handling driver
trait Mapper: Send + Sync
{
	fn name(&self) -> &str;
	fn handles_pv(&self, pv: &PhysicalVolume) -> usize;
}

/// A single logical volume, composed of 1 or more physical blocks
struct LogicalVolume
{
	block_size: usize,	///< Logical block size (max physical block size)
	region_size: Option<usize>,	///< Number of bytes in each physical region, None = JBOD
	regions: Vec<PhysicalRegion>,
}
/// Physical region used by a logical volume
struct PhysicalRegion
{
	volume: usize,
	block_count: usize,	// usize to save space in average case
	first_block: u64,
}

static s_next_pv_idx: AtomicUint = ATOMIC_UINT_INIT;
static s_physical_volumes: LazyMutex<VecMap<usize,Box<PhysicalVolume+Send>>> = lazymutex_init!();
static s_logical_volumes: LazyMutex<VecMap<usize,LogicalVolume>> = lazymutex_init!();
static s_mappers: LazyMutex<Vec<&'static Mapper>> = lazymutex_init!();

// TODO: Maintain a set of registered volumes. Mappers can bind onto a volume and register new LVs
// TODO: Maintain set of active mappings (set of PVs -> set of LVs)
// NOTE: Should unbinding of LVs be allowed? (Yes, for volume removal)

fn init()
{
	s_physical_volumes.init( || VecMap::new() );
}

pub fn register_pv(pv: Box<PhysicalVolume+Send>) -> PhysicalVolumeReg
{
	let pv_id = s_next_pv_idx.fetch_add(1, ::core::atomic::Ordering::Relaxed);

	// Now that a new PV has been inserted, handlers should be informed
	let mut best_mapper: Option<&Mapper> = None;
	let mut best_mapper_level = 0;
	let mappers = s_mappers.lock();
	for &mapper in mappers.iter()
	{
		let level = mapper.handles_pv(&*pv);
		if level == 0
		{
			// Ignore (doesn't handle)
		}
		else if level < best_mapper_level
		{
			// Ignore (weaker handle)
		}
		else if level == best_mapper_level
		{
			// Fight!
			log_warning!("LV Mappers {} and {} are fighting over {}",
				mapper.name(), best_mapper.unwrap().name(), pv.name());
		}
		else
		{
			best_mapper = Some(mapper);
			best_mapper_level = level;
		}
	}
	if let Some(mapper) = best_mapper
	{
		// Poke mapper
		unimplemented!();
	}

	// Wait until after checking for a handler before we add the PV to the list
	s_physical_volumes.lock().insert(pv_id, pv);

	PhysicalVolumeReg { idx: pv_id }
}

/// Function called when a new volume is registered (physical or logical)
fn new_volume(volidx: usize)
{
}

pub fn enum_pvs() -> Vec<(usize,String)>
{
	s_physical_volumes.lock().iter().map(|(k,v)| (*k, String::from_str(v.name())) ).collect()
}

// vim: ft=rust
<commit_msg>Core/Meta Storage - Add size printer, some debug, and initialisation of globals<commit_after>// "Tifflin" Kernel
// - By John Hodge (thePowersGang)
//
// Core/metadevs/storage.rs
// - Storage (block device) subsystem
use _common::*;
use core::atomic::{AtomicUint,ATOMIC_UINT_INIT};
use sync::mutex::LazyMutex;
use async::{ReadHandle,WriteHandle};
use lib::VecMap;

module_define!{Storage, [], init}

/// A unique handle to a storage volume (logical)
pub struct VolumeHandle
{
	lv_idx: usize,
}

pub struct PhysicalVolumeReg
{
	idx: usize,
}

/// Helper to print out the size of a volume/size as a pretty SI base 2 number
pub struct SizePrinter(pub u64);

/// Physical volume instance provided by driver
///
/// Provides the low-level methods to manipulate the underlying storage
pub trait PhysicalVolume
{
	/// Returns the volume name (must be unique to the system)
	fn name(&self) -> &str;	// Local lifetime string
	/// Returns the size of a filesystem block, must be a power of two >512
	fn blocksize(&self) -> usize;
	/// Returns the number of blocks in this volume (i.e. the capacity)
	fn capacity(&self) -> u64;

	/// Reads a number of blocks from the volume into the provided buffer
	///
	/// Reads `count` blocks starting with `blockidx` into the buffer `dst` (which will/should
	/// be the size of `count` blocks). The read is performed with the provided priority, where
	/// 0 is higest, and 255 is lowest.
	fn read<'a>(&'a self, prio: u8, blockidx: u64, count: usize, dst: &'a mut [u8]) -> Result<ReadHandle<'a,'a>, ()>;
	/// Writer a number of blocks to the volume
	fn write<'a>(&'a self, prio: u8, blockidx: u64, count: usize, src: &'a [u8]) -> Result<WriteHandle<'a,'a>,()>;
	/// Erases a number of blocks from the volume
	///
	/// Erases (requests the underlying storage forget about) `count` blocks starting at `blockidx`.
	/// This is functionally equivalent to the SSD "TRIM" command.
	fn wipe(&mut self, blockidx: u64, count: usize);
}

/// Registration for a physical volume handling driver
pub trait Mapper: Send + Sync
{
	fn name(&self) -> &str;
	fn handles_pv(&self, pv: &PhysicalVolume) -> usize;
}

/// A single logical volume, composed of 1 or more physical blocks
struct LogicalVolume
{
	block_size: usize,	///< Logical block size (max physical block size)
	region_size: Option<usize>,	///< Number of bytes in each physical region, None = JBOD
	regions: Vec<PhysicalRegion>,
}
/// Physical region used by a logical volume
struct PhysicalRegion
{
	volume: usize,
	block_count: usize,	// usize to save space in average case
	first_block: u64,
}

static s_next_pv_idx: AtomicUint = ATOMIC_UINT_INIT;
static s_physical_volumes: LazyMutex<VecMap<usize,Box<PhysicalVolume+Send>>> = lazymutex_init!();
static s_logical_volumes: LazyMutex<VecMap<usize,LogicalVolume>> = lazymutex_init!();
static s_mappers: LazyMutex<Vec<&'static Mapper>> = lazymutex_init!();

// TODO: Maintain a set of registered volumes. Mappers can bind onto a volume and register new LVs
// TODO: Maintain set of active mappings (set of PVs -> set of LVs)
// NOTE: Should unbinding of LVs be allowed? (Yes, for volume removal)

fn init()
{
	s_physical_volumes.init( || VecMap::new() );
	s_logical_volumes.init( || VecMap::new() );
	s_mappers.init( || Vec::new() );
}

pub fn register_pv(pv: Box<PhysicalVolume+Send>) -> PhysicalVolumeReg
{
	log_trace!("register_pv(pv = \"{}\")", pv.name());
	let pv_id = s_next_pv_idx.fetch_add(1, ::core::atomic::Ordering::Relaxed);

	// Now that a new PV has been inserted, handlers should be informed
	let mut best_mapper: Option<&Mapper> = None;
	let mut best_mapper_level = 0;
	let mappers = s_mappers.lock();
	for &mapper in mappers.iter()
	{
		let level = mapper.handles_pv(&*pv);
		if level == 0
		{
			// Ignore (doesn't handle)
		}
		else if level < best_mapper_level
		{
			// Ignore (weaker handle)
		}
		else if level == best_mapper_level
		{
			// Fight!
			log_warning!("LV Mappers {} and {} are fighting over {}",
				mapper.name(), best_mapper.unwrap().name(), pv.name());
		}
		else
		{
			best_mapper = Some(mapper);
			best_mapper_level = level;
		}
	}
	if let Some(mapper) = best_mapper
	{
		// Poke mapper
		unimplemented!();
	}

	// Wait until after checking for a handler before we add the PV to the list
	s_physical_volumes.lock().insert(pv_id, pv);

	PhysicalVolumeReg { idx: pv_id }
}

/// Function called when a new volume is registered (physical or logical)
fn new_volume(volidx: usize)
{
}

pub fn enum_pvs() -> Vec<(usize,String)>
{
	s_physical_volumes.lock().iter().map(|(k,v)| (*k, String::from_str(v.name())) ).collect()
}

impl ::core::fmt::Display for SizePrinter
{
	fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result
	{
		if self.0 < 4096
		{
			write!(f, "{}B", self.0)
		}
		else if self.0 < 4096 * 1024
		{
			write!(f, "{}KiB", self.0/1024)
		}
		else if self.0 < 4096 * 1024 * 1024
		{
			write!(f, "{}MiB", self.0/(1024*1024))
		}
		else //if self.0 < 4096 * 1024 * 1024
		{
			write!(f, "{}GiB", self.0/(1024*1024*1024))
		}
	}
}

// vim: ft=rust
<|endoftext|>


<commit_before>use kstring::KString;
use serde::{self, Serialize};

use super::Value;
use crate::model::scalar::ser::ScalarSerializer;
use crate::model::ser::{SerError, SerializeMap, SerializeStructVariant, SerializeTupleVariant};
use crate::model::Object;

/// Convert a `T` into `liquid_core::model::Value`.
///
/// # Examples
///
/// ```rust
/// let s = "foo";
/// let value = liquid_core::model::to_value(&s).unwrap();
/// assert_eq!(value, liquid_core::model::Value::scalar(s));
/// ```
pub fn to_value<T>(value: &T) -> Result<Value, crate::error::Error>
where
    T: Serialize,
{
    value.serialize(ValueSerializer).map_err(|e| e.into())
}

pub(crate) struct ValueSerializer;

impl serde::Serializer for ValueSerializer {
    type Ok = Value;
    type Error = SerError;

    type SerializeSeq = SerializeVec;
    type SerializeTuple = SerializeVec;
    type SerializeTupleStruct = SerializeVec;
    type SerializeTupleVariant = SerializeTupleVariant<Value>;
    type SerializeMap = SerializeMap<Value>;
    type SerializeStruct = SerializeMap<Value>;
    type SerializeStructVariant = SerializeStructVariant<Value>;

    #[inline]
    fn serialize_bool(self, value: bool) -> Result<Value, SerError> {
        ScalarSerializer.serialize_bool(value).map(Value::Scalar)
    }

    #[inline]
    fn serialize_i8(self, value: i8) -> Result<Value, SerError> {
        ScalarSerializer.serialize_i8(value).map(Value::Scalar)
    }

    #[inline]
    fn serialize_i16(self, value: i16) -> Result<Value, SerError> {
        ScalarSerializer.serialize_i16(value).map(Value::Scalar)
    }

    #[inline]
    fn serialize_i32(self, value: i32) -> Result<Value, SerError> {
        ScalarSerializer.serialize_i32(value).map(Value::Scalar)
    }

    fn serialize_i64(self, value: i64) -> Result<Value, SerError> {
        ScalarSerializer.serialize_i64(value).map(Value::Scalar)
    }

    #[inline]
    fn serialize_u8(self, value: u8) -> Result<Value, SerError> {
        ScalarSerializer.serialize_u8(value).map(Value::Scalar)
    }

    #[inline]
    fn serialize_u16(self, value: u16) -> Result<Value, SerError> {
        ScalarSerializer.serialize_u16(value).map(Value::Scalar)
    }

    #[inline]
    fn serialize_u32(self, value: u32) -> Result<Value, SerError> {
        ScalarSerializer.serialize_u32(value).map(Value::Scalar)
    }

    #[inline]
    fn serialize_u64(self, value: u64) -> Result<Value, SerError> {
        ScalarSerializer.serialize_u64(value).map(Value::Scalar)
    }

    #[inline]
    fn serialize_f32(self, value: f32) -> Result<Value, SerError> {
        ScalarSerializer.serialize_f32(value).map(Value::Scalar)
    }

    #[inline]
    fn serialize_f64(self, value: f64) -> Result<Value, SerError> {
        ScalarSerializer.serialize_f64(value).map(Value::Scalar)
    }

    #[inline]
    fn serialize_char(self, value: char) -> Result<Value, SerError> {
        ScalarSerializer.serialize_char(value).map(Value::Scalar)
    }

    #[inline]
    fn serialize_str(self, value: &str) -> Result<Value, SerError> {
        ScalarSerializer.serialize_str(value).map(Value::Scalar)
    }

    fn serialize_bytes(self, value: &[u8]) -> Result<Value, SerError> {
        let vec = value.iter().map(|&b| Value::scalar(i32::from(b))).collect();
        Ok(Value::Array(vec))
    }

    #[inline]
    fn serialize_unit(self) -> Result<Value, SerError> {
        Ok(Value::Nil)
    }

    #[inline]
    fn serialize_unit_struct(self, _name: &'static str) -> Result<Value, SerError> {
        self.serialize_unit()
    }

    #[inline]
    fn serialize_unit_variant(
        self,
        name: &'static str,
        variant_index: u32,
        variant: &'static str,
    ) -> Result<Value, SerError> {
        ScalarSerializer
            .serialize_unit_variant(name, variant_index, variant)
            .map(Value::Scalar)
    }

    #[inline]
    fn serialize_newtype_struct<T: ?Sized>(
        self,
        _name: &'static str,
        value: &T,
    ) -> Result<Value, SerError>
    where
        T: Serialize,
    {
        value.serialize(ValueSerializer)
    }

    fn serialize_newtype_variant<T: ?Sized>(
        self,
        _name: &'static str,
        _variant_index: u32,
        variant: &'static str,
        value: &T,
    ) -> Result<Value, SerError>
    where
        T: Serialize,
    {
        let mut values = Object::new();
        values.insert(
            KString::from_static(variant),
            value.serialize(ValueSerializer)?,
        );
        Ok(Value::Object(values))
    }

    #[inline]
    fn serialize_none(self) -> Result<Value, SerError> {
        self.serialize_unit()
    }

    #[inline]
    fn serialize_some<T: ?Sized>(self, value: &T) -> Result<Value, SerError>
    where
        T: Serialize,
    {
        value.serialize(ValueSerializer)
    }

    fn serialize_seq(self, len: Option<usize>) -> Result<Self::SerializeSeq, SerError> {
        Ok(SerializeVec {
            vec: Vec::with_capacity(len.unwrap_or(0)),
        })
    }

    fn serialize_tuple(self, len: usize) -> Result<Self::SerializeTuple, SerError> {
        self.serialize_seq(Some(len))
    }

    fn serialize_tuple_struct(
        self,
        _name: &'static str,
        len: usize,
    ) -> Result<Self::SerializeTupleStruct, SerError> {
        Ok(SerializeVec {
            vec: Vec::with_capacity(len),
        })
    }

    fn serialize_tuple_variant(
        self,
        _name: &'static str,
        _variant_index: u32,
        variant: &'static str,
        len: usize,
    ) -> Result<Self::SerializeTupleVariant, SerError> {
        Ok(SerializeTupleVariant {
            name: KString::from_static(variant),
            vec: Vec::with_capacity(len),
            other: std::marker::PhantomData,
        })
    }

    fn serialize_map(self, _len: Option<usize>) -> Result<Self::SerializeMap, SerError> {
        Ok(SerializeMap::Map {
            map: Object::new(),
            next_key: None,
            other: std::marker::PhantomData,
        })
    }

    fn serialize_struct(
        self,
        _name: &'static str,
        len: usize,
    ) -> Result<Self::SerializeStruct, SerError> {
        self.serialize_map(Some(len))
    }

    fn serialize_struct_variant(
        self,
        _name: &'static str,
        _variant_index: u32,
        variant: &'static str,
        _len: usize,
    ) -> Result<Self::SerializeStructVariant, SerError> {
        Ok(SerializeStructVariant {
            name: KString::from_static(variant),
            map: Object::new(),
            other: std::marker::PhantomData,
        })
    }
}

pub(crate) struct SerializeVec {
    vec: Vec<Value>,
}

impl serde::ser::SerializeSeq for SerializeVec {
    type Ok = Value;
    type Error = SerError;

    fn serialize_element<T: ?Sized>(&mut self, value: &T) -> Result<(), SerError>
    where
        T: Serialize,
    {
        self.vec.push(value.serialize(ValueSerializer)?);
        Ok(())
    }

    fn end(self) -> Result<Value, SerError> {
        Ok(Value::Array(self.vec))
    }
}

impl serde::ser::SerializeTuple for SerializeVec {
    type Ok = Value;
    type Error = SerError;

    fn serialize_element<T: ?Sized>(&mut self, value: &T) -> Result<(), SerError>
    where
        T: Serialize,
    {
        serde::ser::SerializeSeq::serialize_element(self, value)
    }

    fn end(self) -> Result<Value, SerError> {
        serde::ser::SerializeSeq::end(self)
    }
}

impl serde::ser::SerializeTupleStruct for SerializeVec {
    type Ok = Value;
    type Error = SerError;

    fn serialize_field<T: ?Sized>(&mut self, value: &T) -> Result<(), SerError>
    where
        T: Serialize,
    {
        serde::ser::SerializeSeq::serialize_element(self, value)
    }

    fn end(self) -> Result<Value, SerError> {
        serde::ser::SerializeSeq::end(self)
    }
}

#[cfg(test)]
mod test {
    use std::f64;

    use serde_yaml;

    #[test]
    pub fn serialize_num() {
        let actual = crate::model::Value::scalar(1f64);
        let actual = serde_yaml::to_string(&actual).unwrap();
        difference::assert_diff!(&actual, "---\n1.0", "", 0);

        let actual = crate::model::Value::scalar(-100f64);
        let actual = serde_yaml::to_string(&actual).unwrap();
        difference::assert_diff!(&actual, "---\n-100.0", "", 0);

        let actual = crate::model::Value::scalar(3.14e_10f64);
        let actual = serde_yaml::to_string(&actual).unwrap();
        difference::assert_diff!(&actual, "---\n31400000000.0", "", 0);

        let actual = crate::model::Value::scalar(f64::NAN);
        let actual = serde_yaml::to_string(&actual).unwrap();
        difference::assert_diff!(&actual, "---\n.nan", "", 0);

        let actual = crate::model::Value::scalar(f64::INFINITY);
        let actual = serde_yaml::to_string(&actual).unwrap();
        difference::assert_diff!(&actual, "---\n.inf", "", 0);
    }

    #[test]
    pub fn deserialize_num() {
        let actual: crate::model::Value = serde_yaml::from_str("---\n1").unwrap();
        assert_eq!(actual, crate::model::Value::scalar(1f64));

        let actual: crate::model::Value = serde_yaml::from_str("---\n-100").unwrap();
        assert_eq!(actual, crate::model::Value::scalar(-100f64));

        let actual: crate::model::Value = serde_yaml::from_str("---\n31399999488").unwrap();
        assert_eq!(actual, crate::model::Value::scalar(31399999488.0f64));

        // Skipping NaN since equality fails

        let actual: crate::model::Value = serde_yaml::from_str("---\ninf").unwrap();
        assert_eq!(actual, crate::model::Value::scalar(f64::INFINITY));
    }

    #[test]
    pub fn serialize_bool() {
        let actual = crate::model::Value::scalar(true);
        let actual = serde_yaml::to_string(&actual).unwrap();
        difference::assert_diff!(&actual, "---\ntrue", "", 0);

        let actual = crate::model::Value::scalar(false);
        let actual = serde_yaml::to_string(&actual).unwrap();
        difference::assert_diff!(&actual, "---\nfalse", "", 0);
    }

    #[test]
    pub fn deserialize_bool() {
        let actual: crate::model::Value = serde_yaml::from_str("---\ntrue").unwrap();
        assert_eq!(actual, crate::model::Value::scalar(true));

        let actual: crate::model::Value = serde_yaml::from_str("---\nfalse").unwrap();
        assert_eq!(actual, crate::model::Value::scalar(false));
    }

    #[test]
    pub fn serialize_nil() {
        let actual = crate::model::Value::Nil;
        let actual = serde_yaml::to_string(&actual).unwrap();
        difference::assert_diff!(&actual, "---\n~", "", 0);
    }

    #[test]
    pub fn deserialize_nil() {
        let actual: crate::model::Value = serde_yaml::from_str("---\n~").unwrap();
        assert_eq!(actual, crate::model::Value::Nil);

        let actual: crate::model::Value = serde_yaml::from_str("---\n- ").unwrap();
        assert_eq!(
            actual,
            crate::model::Value::Array(vec![crate::model::Value::Nil])
        );
    }

    #[test]
    pub fn serialize_str() {
        let actual = crate::model::Value::scalar("Hello");
        let actual = serde_yaml::to_string(&actual).unwrap();
        difference::assert_diff!(&actual, "---\nHello", "", 0);

        let actual = crate::model::Value::scalar("10");
        let actual = serde_yaml::to_string(&actual).unwrap();
        difference::assert_diff!(&actual, "---\n\"10\"", "", 0);

        let actual = crate::model::Value::scalar("false");
        let actual = serde_yaml::to_string(&actual).unwrap();
        difference::assert_diff!(&actual, "---\n\"false\"", "", 0);
    }

    #[test]
    pub fn deserialize_str() {
        let actual: crate::model::Value = serde_yaml::from_str("---\nHello").unwrap();
        assert_eq!(actual, crate::model::Value::scalar("Hello"));

        let actual: crate::model::Value = serde_yaml::from_str("\"10\"\n").unwrap();
        assert_eq!(actual, crate::model::Value::scalar("10"));

        let actual: crate::model::Value = serde_yaml::from_str("---\n\"false\"").unwrap();
        assert_eq!(actual, crate::model::Value::scalar("false"));
    }

    #[test]
    pub fn serialize_array() {
        let actual = vec![
            crate::model::Value::scalar(1f64),
            crate::model::Value::scalar(true),
            crate::model::Value::scalar("true"),
        ];
        let actual = crate::model::Value::Array(actual);
        let actual = serde_yaml::to_string(&actual).unwrap();
        difference::assert_diff!(&actual, "---\n- 1.0\n- true\n- \"true\"", "", 0);
    }

    #[test]
    pub fn deserialize_array() {
        let actual: crate::model::Value =
            serde_yaml::from_str("---\n- 1\n- true\n- \"true\"").unwrap();
        let expected = vec![
            crate::model::Value::scalar(1f64),
            crate::model::Value::scalar(true),
            crate::model::Value::scalar("true"),
        ];
        let expected = crate::model::Value::Array(expected);
        assert_eq!(actual, expected);
    }

    #[test]
    pub fn serialize_object() {
        // Skipping due to HashMap ordering issues
    }

    #[test]
    pub fn deserialize_object() {
        let actual: crate::model::Value =
            serde_yaml::from_str("---\nNum: 1\nBool: true\nStr: \"true\"").unwrap();
        let expected: crate::model::Object = [
            ("Num".into(), crate::model::Value::scalar(1f64)),
            ("Bool".into(), crate::model::Value::scalar(true)),
            ("Str".into(), crate::model::Value::scalar("true")),
        ]
        .iter()
        .cloned()
        .collect();
        let expected = crate::model::Value::Object(expected);
        assert_eq!(actual, expected);
    }
}
<commit_msg>test: No idea why we can't serialize inf anymore<commit_after>use kstring::KString;
use serde::{self, Serialize};

use super::Value;
use crate::model::scalar::ser::ScalarSerializer;
use crate::model::ser::{SerError, SerializeMap, SerializeStructVariant, SerializeTupleVariant};
use crate::model::Object;

/// Convert a `T` into `liquid_core::model::Value`.
///
/// # Examples
///
/// ```rust
/// let s = "foo";
/// let value = liquid_core::model::to_value(&s).unwrap();
/// assert_eq!(value, liquid_core::model::Value::scalar(s));
/// ```
pub fn to_value<T>(value: &T) -> Result<Value, crate::error::Error>
where
    T: Serialize,
{
    value.serialize(ValueSerializer).map_err(|e| e.into())
}

pub(crate) struct ValueSerializer;

impl serde::Serializer for ValueSerializer {
    type Ok = Value;
    type Error = SerError;

    type SerializeSeq = SerializeVec;
    type SerializeTuple = SerializeVec;
    type SerializeTupleStruct = SerializeVec;
    type SerializeTupleVariant = SerializeTupleVariant<Value>;
    type SerializeMap = SerializeMap<Value>;
    type SerializeStruct = SerializeMap<Value>;
    type SerializeStructVariant = SerializeStructVariant<Value>;

    #[inline]
    fn serialize_bool(self, value: bool) -> Result<Value, SerError> {
        ScalarSerializer.serialize_bool(value).map(Value::Scalar)
    }

    #[inline]
    fn serialize_i8(self, value: i8) -> Result<Value, SerError> {
        ScalarSerializer.serialize_i8(value).map(Value::Scalar)
    }

    #[inline]
    fn serialize_i16(self, value: i16) -> Result<Value, SerError> {
        ScalarSerializer.serialize_i16(value).map(Value::Scalar)
    }

    #[inline]
    fn serialize_i32(self, value: i32) -> Result<Value, SerError> {
        ScalarSerializer.serialize_i32(value).map(Value::Scalar)
    }

    fn serialize_i64(self, value: i64) -> Result<Value, SerError> {
        ScalarSerializer.serialize_i64(value).map(Value::Scalar)
    }

    #[inline]
    fn serialize_u8(self, value: u8) -> Result<Value, SerError> {
        ScalarSerializer.serialize_u8(value).map(Value::Scalar)
    }

    #[inline]
    fn serialize_u16(self, value: u16) -> Result<Value, SerError> {
        ScalarSerializer.serialize_u16(value).map(Value::Scalar)
    }

    #[inline]
    fn serialize_u32(self, value: u32) -> Result<Value, SerError> {
        ScalarSerializer.serialize_u32(value).map(Value::Scalar)
    }

    #[inline]
    fn serialize_u64(self, value: u64) -> Result<Value, SerError> {
        ScalarSerializer.serialize_u64(value).map(Value::Scalar)
    }

    #[inline]
    fn serialize_f32(self, value: f32) -> Result<Value, SerError> {
        ScalarSerializer.serialize_f32(value).map(Value::Scalar)
    }

    #[inline]
    fn serialize_f64(self, value: f64) -> Result<Value, SerError> {
        ScalarSerializer.serialize_f64(value).map(Value::Scalar)
    }

    #[inline]
    fn serialize_char(self, value: char) -> Result<Value, SerError> {
        ScalarSerializer.serialize_char(value).map(Value::Scalar)
    }

    #[inline]
    fn serialize_str(self, value: &str) -> Result<Value, SerError> {
        ScalarSerializer.serialize_str(value).map(Value::Scalar)
    }

    fn serialize_bytes(self, value: &[u8]) -> Result<Value, SerError> {
        let vec = value.iter().map(|&b| Value::scalar(i32::from(b))).collect();
        Ok(Value::Array(vec))
    }

    #[inline]
    fn serialize_unit(self) -> Result<Value, SerError> {
        Ok(Value::Nil)
    }

    #[inline]
    fn serialize_unit_struct(self, _name: &'static str) -> Result<Value, SerError> {
        self.serialize_unit()
    }

    #[inline]
    fn serialize_unit_variant(
        self,
        name: &'static str,
        variant_index: u32,
        variant: &'static str,
    ) -> Result<Value, SerError> {
        ScalarSerializer
            .serialize_unit_variant(name, variant_index, variant)
            .map(Value::Scalar)
    }

    #[inline]
    fn serialize_newtype_struct<T: ?Sized>(
        self,
        _name: &'static str,
        value: &T,
    ) -> Result<Value, SerError>
    where
        T: Serialize,
    {
        value.serialize(ValueSerializer)
    }

    fn serialize_newtype_variant<T: ?Sized>(
        self,
        _name: &'static str,
        _variant_index: u32,
        variant: &'static str,
        value: &T,
    ) -> Result<Value, SerError>
    where
        T: Serialize,
    {
        let mut values = Object::new();
        values.insert(
            KString::from_static(variant),
            value.serialize(ValueSerializer)?,
        );
        Ok(Value::Object(values))
    }

    #[inline]
    fn serialize_none(self) -> Result<Value, SerError> {
        self.serialize_unit()
    }

    #[inline]
    fn serialize_some<T: ?Sized>(self, value: &T) -> Result<Value, SerError>
    where
        T: Serialize,
    {
        value.serialize(ValueSerializer)
    }

    fn serialize_seq(self, len: Option<usize>) -> Result<Self::SerializeSeq, SerError> {
        Ok(SerializeVec {
            vec: Vec::with_capacity(len.unwrap_or(0)),
        })
    }

    fn serialize_tuple(self, len: usize) -> Result<Self::SerializeTuple, SerError> {
        self.serialize_seq(Some(len))
    }

    fn serialize_tuple_struct(
        self,
        _name: &'static str,
        len: usize,
    ) -> Result<Self::SerializeTupleStruct, SerError> {
        Ok(SerializeVec {
            vec: Vec::with_capacity(len),
        })
    }

    fn serialize_tuple_variant(
        self,
        _name: &'static str,
        _variant_index: u32,
        variant: &'static str,
        len: usize,
    ) -> Result<Self::SerializeTupleVariant, SerError> {
        Ok(SerializeTupleVariant {
            name: KString::from_static(variant),
            vec: Vec::with_capacity(len),
            other: std::marker::PhantomData,
        })
    }

    fn serialize_map(self, _len: Option<usize>) -> Result<Self::SerializeMap, SerError> {
        Ok(SerializeMap::Map {
            map: Object::new(),
            next_key: None,
            other: std::marker::PhantomData,
        })
    }

    fn serialize_struct(
        self,
        _name: &'static str,
        len: usize,
    ) -> Result<Self::SerializeStruct, SerError> {
        self.serialize_map(Some(len))
    }

    fn serialize_struct_variant(
        self,
        _name: &'static str,
        _variant_index: u32,
        variant: &'static str,
        _len: usize,
    ) -> Result<Self::SerializeStructVariant, SerError> {
        Ok(SerializeStructVariant {
            name: KString::from_static(variant),
            map: Object::new(),
            other: std::marker::PhantomData,
        })
    }
}

pub(crate) struct SerializeVec {
    vec: Vec<Value>,
}

impl serde::ser::SerializeSeq for SerializeVec {
    type Ok = Value;
    type Error = SerError;

    fn serialize_element<T: ?Sized>(&mut self, value: &T) -> Result<(), SerError>
    where
        T: Serialize,
    {
        self.vec.push(value.serialize(ValueSerializer)?);
        Ok(())
    }

    fn end(self) -> Result<Value, SerError> {
        Ok(Value::Array(self.vec))
    }
}

impl serde::ser::SerializeTuple for SerializeVec {
    type Ok = Value;
    type Error = SerError;

    fn serialize_element<T: ?Sized>(&mut self, value: &T) -> Result<(), SerError>
    where
        T: Serialize,
    {
        serde::ser::SerializeSeq::serialize_element(self, value)
    }

    fn end(self) -> Result<Value, SerError> {
        serde::ser::SerializeSeq::end(self)
    }
}

impl serde::ser::SerializeTupleStruct for SerializeVec {
    type Ok = Value;
    type Error = SerError;

    fn serialize_field<T: ?Sized>(&mut self, value: &T) -> Result<(), SerError>
    where
        T: Serialize,
    {
        serde::ser::SerializeSeq::serialize_element(self, value)
    }

    fn end(self) -> Result<Value, SerError> {
        serde::ser::SerializeSeq::end(self)
    }
}

#[cfg(test)]
mod test {
    use std::f64;

    use serde_yaml;

    #[test]
    pub fn serialize_num() {
        let actual = crate::model::Value::scalar(1f64);
        let actual = serde_yaml::to_string(&actual).unwrap();
        difference::assert_diff!(&actual, "---\n1.0", "", 0);

        let actual = crate::model::Value::scalar(-100f64);
        let actual = serde_yaml::to_string(&actual).unwrap();
        difference::assert_diff!(&actual, "---\n-100.0", "", 0);

        let actual = crate::model::Value::scalar(3.14e_10f64);
        let actual = serde_yaml::to_string(&actual).unwrap();
        difference::assert_diff!(&actual, "---\n31400000000.0", "", 0);

        let actual = crate::model::Value::scalar(f64::NAN);
        let actual = serde_yaml::to_string(&actual).unwrap();
        difference::assert_diff!(&actual, "---\n.nan", "", 0);

        let actual = crate::model::Value::scalar(f64::INFINITY);
        let actual = serde_yaml::to_string(&actual).unwrap();
        difference::assert_diff!(&actual, "---\n.inf", "", 0);
    }

    #[test]
    pub fn deserialize_num() {
        let actual: crate::model::Value = serde_yaml::from_str("---\n1").unwrap();
        assert_eq!(actual, crate::model::Value::scalar(1f64));

        let actual: crate::model::Value = serde_yaml::from_str("---\n-100").unwrap();
        assert_eq!(actual, crate::model::Value::scalar(-100f64));

        let actual: crate::model::Value = serde_yaml::from_str("---\n31399999488").unwrap();
        assert_eq!(actual, crate::model::Value::scalar(31399999488.0f64));

        // Skipping NaN and inf
    }

    #[test]
    pub fn serialize_bool() {
        let actual = crate::model::Value::scalar(true);
        let actual = serde_yaml::to_string(&actual).unwrap();
        difference::assert_diff!(&actual, "---\ntrue", "", 0);

        let actual = crate::model::Value::scalar(false);
        let actual = serde_yaml::to_string(&actual).unwrap();
        difference::assert_diff!(&actual, "---\nfalse", "", 0);
    }

    #[test]
    pub fn deserialize_bool() {
        let actual: crate::model::Value = serde_yaml::from_str("---\ntrue").unwrap();
        assert_eq!(actual, crate::model::Value::scalar(true));

        let actual: crate::model::Value = serde_yaml::from_str("---\nfalse").unwrap();
        assert_eq!(actual, crate::model::Value::scalar(false));
    }

    #[test]
    pub fn serialize_nil() {
        let actual = crate::model::Value::Nil;
        let actual = serde_yaml::to_string(&actual).unwrap();
        difference::assert_diff!(&actual, "---\n~", "", 0);
    }

    #[test]
    pub fn deserialize_nil() {
        let actual: crate::model::Value = serde_yaml::from_str("---\n~").unwrap();
        assert_eq!(actual, crate::model::Value::Nil);

        let actual: crate::model::Value = serde_yaml::from_str("---\n- ").unwrap();
        assert_eq!(
            actual,
            crate::model::Value::Array(vec![crate::model::Value::Nil])
        );
    }

    #[test]
    pub fn serialize_str() {
        let actual = crate::model::Value::scalar("Hello");
        let actual = serde_yaml::to_string(&actual).unwrap();
        difference::assert_diff!(&actual, "---\nHello", "", 0);

        let actual = crate::model::Value::scalar("10");
        let actual = serde_yaml::to_string(&actual).unwrap();
        difference::assert_diff!(&actual, "---\n\"10\"", "", 0);

        let actual = crate::model::Value::scalar("false");
        let actual = serde_yaml::to_string(&actual).unwrap();
        difference::assert_diff!(&actual, "---\n\"false\"", "", 0);
    }

    #[test]
    pub fn deserialize_str() {
        let actual: crate::model::Value = serde_yaml::from_str("---\nHello").unwrap();
        assert_eq!(actual, crate::model::Value::scalar("Hello"));

        let actual: crate::model::Value = serde_yaml::from_str("\"10\"\n").unwrap();
        assert_eq!(actual, crate::model::Value::scalar("10"));

        let actual: crate::model::Value = serde_yaml::from_str("---\n\"false\"").unwrap();
        assert_eq!(actual, crate::model::Value::scalar("false"));
    }

    #[test]
    pub fn serialize_array() {
        let actual = vec![
            crate::model::Value::scalar(1f64),
            crate::model::Value::scalar(true),
            crate::model::Value::scalar("true"),
        ];
        let actual = crate::model::Value::Array(actual);
        let actual = serde_yaml::to_string(&actual).unwrap();
        difference::assert_diff!(&actual, "---\n- 1.0\n- true\n- \"true\"", "", 0);
    }

    #[test]
    pub fn deserialize_array() {
        let actual: crate::model::Value =
            serde_yaml::from_str("---\n- 1\n- true\n- \"true\"").unwrap();
        let expected = vec![
            crate::model::Value::scalar(1f64),
            crate::model::Value::scalar(true),
            crate::model::Value::scalar("true"),
        ];
        let expected = crate::model::Value::Array(expected);
        assert_eq!(actual, expected);
    }

    #[test]
    pub fn serialize_object() {
        // Skipping due to HashMap ordering issues
    }

    #[test]
    pub fn deserialize_object() {
        let actual: crate::model::Value =
            serde_yaml::from_str("---\nNum: 1\nBool: true\nStr: \"true\"").unwrap();
        let expected: crate::model::Object = [
            ("Num".into(), crate::model::Value::scalar(1f64)),
            ("Bool".into(), crate::model::Value::scalar(true)),
            ("Str".into(), crate::model::Value::scalar("true")),
        ]
        .iter()
        .cloned()
        .collect();
        let expected = crate::model::Value::Object(expected);
        assert_eq!(actual, expected);
    }
}
<|endoftext|>


<commit_before>/// Benchmarks game nbody code
/// Taken from the  `packed_simd` crate
/// Run this benchmark with `cargo test --example body`
use core_simd::*;

use std::f64::consts::PI;
const SOLAR_MASS: f64 = 4.0 * PI * PI;
const DAYS_PER_YEAR: f64 = 365.24;

#[derive(Debug, Clone, Copy)]
pub struct Body {
    pub x: f64x4,
    pub v: f64x4,
    pub mass: f64,
}

// translation up to here
const N_BODIES: usize = 5;
const BODIES: [Body; N_BODIES] = [
    // sun:
    Body {
        x: f64x4::from_array([0., 0., 0., 0.]),
        v: f64x4::from_array([0., 0., 0., 0.]),
        mass: SOLAR_MASS,
    },
    // jupiter:
    Body {
        x: f64x4::from_array([
            4.84143144246472090e+00,
            -1.16032004402742839e+00,
            -1.03622044471123109e-01,
            0.,
        ]),
        v: f64x4::from_array([
            1.66007664274403694e-03 * DAYS_PER_YEAR,
            7.69901118419740425e-03 * DAYS_PER_YEAR,
            -6.90460016972063023e-05 * DAYS_PER_YEAR,
            0.,
        ]),
        mass: 9.54791938424326609e-04 * SOLAR_MASS,
    },
    // saturn:
    Body {
        x: f64x4::from_array([
            8.34336671824457987e+00,
            4.12479856412430479e+00,
            -4.03523417114321381e-01,
            0.,
        ]),
        v: f64x4::from_array([
            -2.76742510726862411e-03 * DAYS_PER_YEAR,
            4.99852801234917238e-03 * DAYS_PER_YEAR,
            2.30417297573763929e-05 * DAYS_PER_YEAR,
            0.,
        ]),
        mass: 2.85885980666130812e-04 * SOLAR_MASS,
    },
    // uranus:
    Body {
        x: f64x4::from_array([
            1.28943695621391310e+01,
            -1.51111514016986312e+01,
            -2.23307578892655734e-01,
            0.,
        ]),
        v: f64x4::from_array([
            2.96460137564761618e-03 * DAYS_PER_YEAR,
            2.37847173959480950e-03 * DAYS_PER_YEAR,
            -2.96589568540237556e-05 * DAYS_PER_YEAR,
            0.,
        ]),
        mass: 4.36624404335156298e-05 * SOLAR_MASS,
    },
    // neptune:
    Body {
        x: f64x4::from_array([
            1.53796971148509165e+01,
            -2.59193146099879641e+01,
            1.79258772950371181e-01,
            0.,
        ]),
        v: f64x4::from_array([
            2.68067772490389322e-03 * DAYS_PER_YEAR,
            1.62824170038242295e-03 * DAYS_PER_YEAR,
            -9.51592254519715870e-05 * DAYS_PER_YEAR,
            0.,
        ]),
        mass: 5.15138902046611451e-05 * SOLAR_MASS,
    },
];

pub fn offset_momentum(bodies: &mut [Body; N_BODIES]) {
    let (sun, rest) = bodies.split_at_mut(1);
    let sun = &mut sun[0];
    for body in rest {
        let m_ratio = body.mass / SOLAR_MASS;
        sun.v -= body.v * m_ratio;
    }
}

pub fn energy(bodies: &[Body; N_BODIES]) -> f64 {
    let mut e = 0.;
    for i in 0..N_BODIES {
        let bi = &bodies[i];
        e += bi.mass * (bi.v * bi.v).horizontal_sum() * 0.5;
        for bj in bodies.iter().take(N_BODIES).skip(i + 1) {
            let dx = bi.x - bj.x;
            e -= bi.mass * bj.mass / (dx * dx).horizontal_sum().sqrt()
        }
    }
    e
}

pub fn advance(bodies: &mut [Body; N_BODIES], dt: f64) {
    const N: usize = N_BODIES * (N_BODIES - 1) / 2;

    // compute distance between bodies:
    let mut r = [f64x4::splat(0.); N];
    {
        let mut i = 0;
        for j in 0..N_BODIES {
            for k in j + 1..N_BODIES {
                r[i] = bodies[j].x - bodies[k].x;
                i += 1;
            }
        }
    }

    let mut mag = [0.0; N];
    let mut i = 0;
    while i < N {
        let d2s = f64x2::from_array([
            (r[i] * r[i]).horizontal_sum(),
            (r[i + 1] * r[i + 1]).horizontal_sum(),
        ]);
        let dmags = f64x2::splat(dt) / (d2s * d2s.sqrt());
        // dmags.write_to_slice_unaligned(&mut mag[i..]);
        mag[i] = dmags[0];
        mag[i + 1] = dmags[1];
        i += 2;
    }

    i = 0;
    for j in 0..N_BODIES {
        for k in j + 1..N_BODIES {
            let f = r[i] * mag[i];
            bodies[j].v -= f * bodies[k].mass;
            bodies[k].v += f * bodies[j].mass;
            i += 1
        }
    }
    for body in bodies {
        body.x += dt * body.v
    }
}

// #[inline]
// pub unsafe fn write_to_slice_unaligned(slice: &mut SimdF64::<LANES>) {
//     let target_ptr = slice.get_unchecked_mut(0) as *mut f64x2;
//     *(target_ptr as *mut f64x2) = SimdF64;
// }

pub fn run(n: usize) -> (f64, f64) {
    let mut bodies = BODIES;
    offset_momentum(&mut bodies);
    let energy_before = energy(&bodies);
    for _ in 0..n {
        advance(&mut bodies, 0.01);
    }
    let energy_after = energy(&bodies);

    (energy_before, energy_after)
}

fn approx_eq_f32(a: f32, b: f32) -> bool {
    (a - b).abs() < 0.00000001
}

#[cfg(test)]
mod tests {
    #[test]
    fn test() {
        use super::*;
        const OUTPUT: [f64; 2] = [-0.169075164, -0.169087605];
        let (energy_before, energy_after) = super::run(1000);
        assert!(approx_eq_f32(energy_before as f32, OUTPUT[0] as f32));
        assert!(approx_eq_f32(energy_after as f32, OUTPUT[1] as f32));
        // }
    }
}
<commit_msg>add main to avoid CI crash<commit_after>/// Benchmarks game nbody code
/// Taken from the  `packed_simd` crate
/// Run this benchmark with `cargo test --example body`
use core_simd::*;

use std::f64::consts::PI;
const SOLAR_MASS: f64 = 4.0 * PI * PI;
const DAYS_PER_YEAR: f64 = 365.24;

#[derive(Debug, Clone, Copy)]
pub struct Body {
    pub x: f64x4,
    pub v: f64x4,
    pub mass: f64,
}

// translation up to here
const N_BODIES: usize = 5;
const BODIES: [Body; N_BODIES] = [
    // sun:
    Body {
        x: f64x4::from_array([0., 0., 0., 0.]),
        v: f64x4::from_array([0., 0., 0., 0.]),
        mass: SOLAR_MASS,
    },
    // jupiter:
    Body {
        x: f64x4::from_array([
            4.84143144246472090e+00,
            -1.16032004402742839e+00,
            -1.03622044471123109e-01,
            0.,
        ]),
        v: f64x4::from_array([
            1.66007664274403694e-03 * DAYS_PER_YEAR,
            7.69901118419740425e-03 * DAYS_PER_YEAR,
            -6.90460016972063023e-05 * DAYS_PER_YEAR,
            0.,
        ]),
        mass: 9.54791938424326609e-04 * SOLAR_MASS,
    },
    // saturn:
    Body {
        x: f64x4::from_array([
            8.34336671824457987e+00,
            4.12479856412430479e+00,
            -4.03523417114321381e-01,
            0.,
        ]),
        v: f64x4::from_array([
            -2.76742510726862411e-03 * DAYS_PER_YEAR,
            4.99852801234917238e-03 * DAYS_PER_YEAR,
            2.30417297573763929e-05 * DAYS_PER_YEAR,
            0.,
        ]),
        mass: 2.85885980666130812e-04 * SOLAR_MASS,
    },
    // uranus:
    Body {
        x: f64x4::from_array([
            1.28943695621391310e+01,
            -1.51111514016986312e+01,
            -2.23307578892655734e-01,
            0.,
        ]),
        v: f64x4::from_array([
            2.96460137564761618e-03 * DAYS_PER_YEAR,
            2.37847173959480950e-03 * DAYS_PER_YEAR,
            -2.96589568540237556e-05 * DAYS_PER_YEAR,
            0.,
        ]),
        mass: 4.36624404335156298e-05 * SOLAR_MASS,
    },
    // neptune:
    Body {
        x: f64x4::from_array([
            1.53796971148509165e+01,
            -2.59193146099879641e+01,
            1.79258772950371181e-01,
            0.,
        ]),
        v: f64x4::from_array([
            2.68067772490389322e-03 * DAYS_PER_YEAR,
            1.62824170038242295e-03 * DAYS_PER_YEAR,
            -9.51592254519715870e-05 * DAYS_PER_YEAR,
            0.,
        ]),
        mass: 5.15138902046611451e-05 * SOLAR_MASS,
    },
];

pub fn offset_momentum(bodies: &mut [Body; N_BODIES]) {
    let (sun, rest) = bodies.split_at_mut(1);
    let sun = &mut sun[0];
    for body in rest {
        let m_ratio = body.mass / SOLAR_MASS;
        sun.v -= body.v * m_ratio;
    }
}

pub fn energy(bodies: &[Body; N_BODIES]) -> f64 {
    let mut e = 0.;
    for i in 0..N_BODIES {
        let bi = &bodies[i];
        e += bi.mass * (bi.v * bi.v).horizontal_sum() * 0.5;
        for bj in bodies.iter().take(N_BODIES).skip(i + 1) {
            let dx = bi.x - bj.x;
            e -= bi.mass * bj.mass / (dx * dx).horizontal_sum().sqrt()
        }
    }
    e
}

pub fn advance(bodies: &mut [Body; N_BODIES], dt: f64) {
    const N: usize = N_BODIES * (N_BODIES - 1) / 2;

    // compute distance between bodies:
    let mut r = [f64x4::splat(0.); N];
    {
        let mut i = 0;
        for j in 0..N_BODIES {
            for k in j + 1..N_BODIES {
                r[i] = bodies[j].x - bodies[k].x;
                i += 1;
            }
        }
    }

    let mut mag = [0.0; N];
    let mut i = 0;
    while i < N {
        let d2s = f64x2::from_array([
            (r[i] * r[i]).horizontal_sum(),
            (r[i + 1] * r[i + 1]).horizontal_sum(),
        ]);
        let dmags = f64x2::splat(dt) / (d2s * d2s.sqrt());
        // dmags.write_to_slice_unaligned(&mut mag[i..]);
        mag[i] = dmags[0];
        mag[i + 1] = dmags[1];
        i += 2;
    }

    i = 0;
    for j in 0..N_BODIES {
        for k in j + 1..N_BODIES {
            let f = r[i] * mag[i];
            bodies[j].v -= f * bodies[k].mass;
            bodies[k].v += f * bodies[j].mass;
            i += 1
        }
    }
    for body in bodies {
        body.x += dt * body.v
    }
}

// #[inline]
// pub unsafe fn write_to_slice_unaligned(slice: &mut SimdF64::<LANES>) {
//     let target_ptr = slice.get_unchecked_mut(0) as *mut f64x2;
//     *(target_ptr as *mut f64x2) = SimdF64;
// }

pub fn run(n: usize) -> (f64, f64) {
    let mut bodies = BODIES;
    offset_momentum(&mut bodies);
    let energy_before = energy(&bodies);
    for _ in 0..n {
        advance(&mut bodies, 0.01);
    }
    let energy_after = energy(&bodies);

    (energy_before, energy_after)
}

fn approx_eq_f32(a: f32, b: f32) -> bool {
    (a - b).abs() < 0.00000001
}

#[cfg(test)]
mod tests {
    #[test]
    fn test() {
        use super::*;
        const OUTPUT: [f64; 2] = [-0.169075164, -0.169087605];
        let (energy_before, energy_after) = super::run(1000);
        assert!(approx_eq_f32(energy_before as f32, OUTPUT[0] as f32));
        assert!(approx_eq_f32(energy_after as f32, OUTPUT[1] as f32));
        // }
    }
}

fn main () {
    // empty main to pass CI
}
<|endoftext|>


<commit_before>use std::error::Error;
use std::io::{Cursor, Read};
use std::sync::{Arc, Mutex};

use zip::ZipArchive;
use image::{self, DynamicImage, GenericImage, ImageLuma8, ImageFormat};

use cache::{AssetLoadContext, GeneratedAsset, WebAsset};
use terrain::raster::{GlobalRaster, Raster, RasterSource};

#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum LandCoverKind {
    TreeCover,
    WaterMask,
}
impl RasterSource for LandCoverKind {
    type Type = u8;
    fn load(
        &self,
        context: &mut AssetLoadContext,
        latitude: i16,
        longitude: i16,
    ) -> Option<Raster<u8>> {
        LandCoverParams {
            latitude,
            longitude,
            kind: *self,
            raw: None,
        }.load(context)
            .ok()
    }
}

/// Coordinates are of the lower left corner of the 10x10 degree cell.
#[derive(Debug, Eq, PartialEq)]
struct RawLandCoverParams {
    pub latitude: i16,
    pub longitude: i16,
    pub kind: LandCoverKind,
}
impl WebAsset for RawLandCoverParams {
    type Type = Arc<Mutex<DynamicImage>>;

    fn url(&self) -> String {
        let (latitude, longitude) = (self.latitude + 10, self.longitude);
        assert_eq!(latitude % 10, 0);
        assert_eq!(longitude % 10, 0);
        let n_or_s = if latitude >= 0 { 'N' } else { 'S' };
        let e_or_w = if longitude >= 0 { 'E' } else { 'W' };

        match self.kind {
            LandCoverKind::TreeCover => {
                format!("https://edcintl.cr.usgs.gov/downloads/sciweb1/shared/gtc/downloads/\
                         treecover2010_v3_individual/{:02}{}_{:03}{}_treecover2010_v3.tif.zip",
                latitude.abs(),
                n_or_s,
                longitude.abs(),
                e_or_w,
            )
            }
            LandCoverKind::WaterMask => {
                format!("https://edcintl.cr.usgs.gov/downloads/sciweb1/shared/gtc/downloads/\
                         WaterMask2010_UMD_individual/Hansen_GFC2013_datamask_{:02}{}_{:03}{}\
                         .tif.zip",
                latitude.abs(),
                n_or_s,
                longitude.abs(),
                e_or_w,
            )
            }
        }
    }
    fn filename(&self) -> String {
        let (latitude, longitude) = (self.latitude + 10, self.longitude);
        assert_eq!(latitude % 10, 0);
        assert_eq!(longitude % 10, 0);
        let n_or_s = if latitude >= 0 { 'N' } else { 'S' };
        let e_or_w = if longitude >= 0 { 'E' } else { 'W' };

        match self.kind {
            LandCoverKind::TreeCover => {
                format!("treecover/raw/{:02}{}_{:03}{}_treecover2010_v3.tif.zip",
                        latitude.abs(),
                        n_or_s,
                        longitude.abs(),
                        e_or_w,
                )
            }
            LandCoverKind::WaterMask => {
                format!("watermask/raw/Hansen_GFC2013_datamask_{:02}{}_{:03}{}.tif.zip",
                        latitude.abs(),
                        n_or_s,
                        longitude.abs(),
                        e_or_w,
                )
            }
        }
    }
    fn parse(
        &self,
        context: &mut AssetLoadContext,
        data: Vec<u8>,
    ) -> Result<Self::Type, Box<::std::error::Error>> {
        let mut zip = ZipArchive::new(Cursor::new(data))?;
        assert_eq!(zip.len(), 1);

        let mut data = Vec::new();
        zip.by_index(0)?.read_to_end(&mut data)?;

        let image = image::load_from_memory_with_format(&data[..], ImageFormat::TIFF)?;
        let image = Arc::new(Mutex::new(image));

        for latitude in self.latitude..(self.latitude + 10) {
            for longitude in self.longitude..(self.longitude + 10) {
                let params = LandCoverParams {
                    latitude,
                    longitude,
                    kind: self.kind,
                    raw: Some(image.clone()),
                };
                assert_eq!(params.raw_params(), *self);
                params.load(context)?;
            }
        }

        Ok(image)
    }
}

pub struct LandCoverParams {
    pub latitude: i16,
    pub longitude: i16,
    pub kind: LandCoverKind,
    pub raw: Option<Arc<Mutex<DynamicImage>>>,
}
impl LandCoverParams {
    fn raw_params(&self) -> RawLandCoverParams {
        RawLandCoverParams {
            latitude: self.latitude - ((self.latitude % 10) + 10) % 10,
            longitude: self.longitude - ((self.longitude % 10) + 10) % 10,
            kind: self.kind,
        }
    }

    fn generate_from_raw(&self) -> Result<Raster<u8>, Box<Error>> {
        let (w, h, image) = {
            let mut image = self.raw.as_ref().unwrap().lock().unwrap();
            let (w, h) = (image.width(), image.height());
            assert_eq!(w, h);
            let image = image
                .crop(
                    (w / 10) * ((self.longitude % 10 + 10) % 10) as u32,
                    (h / 10) * (9 - ((self.latitude % 10 + 10) % 10) as u32),
                    w / 10 + 1,
                    h / 10 + 1,
                )
                .clone();
            (w, h, image)
        };
        let image = image.rotate90().flipv();
        let values = if let ImageLuma8(image) = image {
            image.into_raw().into_iter()
        } else {
            unreachable!()
        };

        let values = match self.kind {
            LandCoverKind::TreeCover => values.collect(),
            LandCoverKind::WaterMask => {
                values
                    .map(|v| if v == 1 {
                        0
                    } else if v == 0 || v == 2 {
                        255
                    } else {
                        unreachable!()
                    })
                    .collect()
            }
        };

        Ok(Raster {
            width: w as usize / 10 + 1,
            height: h as usize / 10 + 1,
            cell_size: 10.0 / (w - 1) as f64,
            xllcorner: self.latitude as f64,
            yllcorner: self.longitude as f64,
            values,
        })
    }
}
impl GeneratedAsset for LandCoverParams {
    type Type = Raster<u8>;

    fn filename(&self) -> String {
        let n_or_s = if self.latitude >= 0 { 'N' } else { 'S' };
        let e_or_w = if self.longitude >= 0 { 'E' } else { 'W' };

        let directory = match self.kind {
            LandCoverKind::TreeCover => "treecover/processed",
            LandCoverKind::WaterMask => "watermask/processed",
        };

        format!("{}/{:02}{}_{:03}{}.raster",
                directory,
                self.latitude.abs(),
                n_or_s,
                self.longitude.abs(),
                e_or_w,
        )
    }

    fn generate(&self, context: &mut AssetLoadContext) -> Result<Self::Type, Box<Error>> {
        if self.raw.is_some() {
            return self.generate_from_raw();
        }

        Self {
            latitude: self.latitude,
            longitude: self.longitude,
            kind: self.kind,
            raw: Some(self.raw_params().load(context)?),
        }.generate_from_raw()
    }
}

pub struct BlueMarble;
impl WebAsset for BlueMarble {
    type Type = GlobalRaster<u8>;

    fn url(&self) -> String {
        "https://eoimages.gsfc.nasa.gov/images/imagerecords/74000/74218/\
         world.200412.3x21600x10800.png"
            .to_owned()
    }
    fn filename(&self) -> String {
        "bluemarble/world.200412.3x21600x10800.png".to_owned()
    }
    fn parse(
        &self,
        _context: &mut AssetLoadContext,
        data: Vec<u8>,
    ) -> Result<Self::Type, Box<::std::error::Error>> {
        let image = image::load_from_memory_with_format(&data, ImageFormat::PNG)?
            .to_rgb();
        Ok(GlobalRaster {
            width: image.width() as usize,
            height: image.height() as usize,
            bands: 3,
            values: image.into_raw(),
        })
    }
}

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

    #[test]
    fn to_raw_params() {
        assert_eq!(
            LandCoverParams {
                latitude: 165,
                longitude: 31,
                kind: LandCoverKind::TreeCover,
            }.raw_params(),
            RawLandCoverParams {
                latitude: 160,
                longitude: 30,
                kind: LandCoverKind::TreeCover,
            }
        );

        assert_eq!(
            LandCoverParams {
                latitude: 20,
                longitude: 20,
                kind: LandCoverKind::TreeCover,
            }.raw_params(),
            RawLandCoverParams {
                latitude: 20,
                longitude: 20,
                kind: LandCoverKind::TreeCover,
            }
        );

        assert_eq!(
            LandCoverParams {
                latitude: -18,
                longitude: -18,
                kind: LandCoverKind::TreeCover,
            }.raw_params(),
            RawLandCoverParams {
                latitude: -20,
                longitude: -20,
                kind: LandCoverKind::TreeCover,
            }
        );

        assert_eq!(
            LandCoverParams {
                latitude: -30,
                longitude: -30,
                kind: LandCoverKind::TreeCover,
            }.raw_params(),
            RawLandCoverParams {
                latitude: -30,
                longitude: -30,
                kind: LandCoverKind::TreeCover,
            }
        );
    }
}
<commit_msg>Fix tests<commit_after>use std::error::Error;
use std::io::{Cursor, Read};
use std::sync::{Arc, Mutex};

use zip::ZipArchive;
use image::{self, DynamicImage, GenericImage, ImageLuma8, ImageFormat};

use cache::{AssetLoadContext, GeneratedAsset, WebAsset};
use terrain::raster::{GlobalRaster, Raster, RasterSource};

#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum LandCoverKind {
    TreeCover,
    WaterMask,
}
impl RasterSource for LandCoverKind {
    type Type = u8;
    fn load(
        &self,
        context: &mut AssetLoadContext,
        latitude: i16,
        longitude: i16,
    ) -> Option<Raster<u8>> {
        LandCoverParams {
            latitude,
            longitude,
            kind: *self,
            raw: None,
        }.load(context)
            .ok()
    }
}

/// Coordinates are of the lower left corner of the 10x10 degree cell.
#[derive(Debug, Eq, PartialEq)]
struct RawLandCoverParams {
    pub latitude: i16,
    pub longitude: i16,
    pub kind: LandCoverKind,
}
impl WebAsset for RawLandCoverParams {
    type Type = Arc<Mutex<DynamicImage>>;

    fn url(&self) -> String {
        let (latitude, longitude) = (self.latitude + 10, self.longitude);
        assert_eq!(latitude % 10, 0);
        assert_eq!(longitude % 10, 0);
        let n_or_s = if latitude >= 0 { 'N' } else { 'S' };
        let e_or_w = if longitude >= 0 { 'E' } else { 'W' };

        match self.kind {
            LandCoverKind::TreeCover => {
                format!("https://edcintl.cr.usgs.gov/downloads/sciweb1/shared/gtc/downloads/\
                         treecover2010_v3_individual/{:02}{}_{:03}{}_treecover2010_v3.tif.zip",
                latitude.abs(),
                n_or_s,
                longitude.abs(),
                e_or_w,
            )
            }
            LandCoverKind::WaterMask => {
                format!("https://edcintl.cr.usgs.gov/downloads/sciweb1/shared/gtc/downloads/\
                         WaterMask2010_UMD_individual/Hansen_GFC2013_datamask_{:02}{}_{:03}{}\
                         .tif.zip",
                latitude.abs(),
                n_or_s,
                longitude.abs(),
                e_or_w,
            )
            }
        }
    }
    fn filename(&self) -> String {
        let (latitude, longitude) = (self.latitude + 10, self.longitude);
        assert_eq!(latitude % 10, 0);
        assert_eq!(longitude % 10, 0);
        let n_or_s = if latitude >= 0 { 'N' } else { 'S' };
        let e_or_w = if longitude >= 0 { 'E' } else { 'W' };

        match self.kind {
            LandCoverKind::TreeCover => {
                format!("treecover/raw/{:02}{}_{:03}{}_treecover2010_v3.tif.zip",
                        latitude.abs(),
                        n_or_s,
                        longitude.abs(),
                        e_or_w,
                )
            }
            LandCoverKind::WaterMask => {
                format!("watermask/raw/Hansen_GFC2013_datamask_{:02}{}_{:03}{}.tif.zip",
                        latitude.abs(),
                        n_or_s,
                        longitude.abs(),
                        e_or_w,
                )
            }
        }
    }
    fn parse(
        &self,
        context: &mut AssetLoadContext,
        data: Vec<u8>,
    ) -> Result<Self::Type, Box<::std::error::Error>> {
        let mut zip = ZipArchive::new(Cursor::new(data))?;
        assert_eq!(zip.len(), 1);

        let mut data = Vec::new();
        zip.by_index(0)?.read_to_end(&mut data)?;

        let image = image::load_from_memory_with_format(&data[..], ImageFormat::TIFF)?;
        let image = Arc::new(Mutex::new(image));

        for latitude in self.latitude..(self.latitude + 10) {
            for longitude in self.longitude..(self.longitude + 10) {
                let params = LandCoverParams {
                    latitude,
                    longitude,
                    kind: self.kind,
                    raw: Some(image.clone()),
                };
                assert_eq!(params.raw_params(), *self);
                params.load(context)?;
            }
        }

        Ok(image)
    }
}

pub struct LandCoverParams {
    pub latitude: i16,
    pub longitude: i16,
    pub kind: LandCoverKind,
    pub raw: Option<Arc<Mutex<DynamicImage>>>,
}
impl LandCoverParams {
    fn raw_params(&self) -> RawLandCoverParams {
        RawLandCoverParams {
            latitude: self.latitude - ((self.latitude % 10) + 10) % 10,
            longitude: self.longitude - ((self.longitude % 10) + 10) % 10,
            kind: self.kind,
        }
    }

    fn generate_from_raw(&self) -> Result<Raster<u8>, Box<Error>> {
        let (w, h, image) = {
            let mut image = self.raw.as_ref().unwrap().lock().unwrap();
            let (w, h) = (image.width(), image.height());
            assert_eq!(w, h);
            let image = image
                .crop(
                    (w / 10) * ((self.longitude % 10 + 10) % 10) as u32,
                    (h / 10) * (9 - ((self.latitude % 10 + 10) % 10) as u32),
                    w / 10 + 1,
                    h / 10 + 1,
                )
                .clone();
            (w, h, image)
        };
        let image = image.rotate90().flipv();
        let values = if let ImageLuma8(image) = image {
            image.into_raw().into_iter()
        } else {
            unreachable!()
        };

        let values = match self.kind {
            LandCoverKind::TreeCover => values.collect(),
            LandCoverKind::WaterMask => {
                values
                    .map(|v| if v == 1 {
                        0
                    } else if v == 0 || v == 2 {
                        255
                    } else {
                        unreachable!()
                    })
                    .collect()
            }
        };

        Ok(Raster {
            width: w as usize / 10 + 1,
            height: h as usize / 10 + 1,
            cell_size: 10.0 / (w - 1) as f64,
            xllcorner: self.latitude as f64,
            yllcorner: self.longitude as f64,
            values,
        })
    }
}
impl GeneratedAsset for LandCoverParams {
    type Type = Raster<u8>;

    fn filename(&self) -> String {
        let n_or_s = if self.latitude >= 0 { 'N' } else { 'S' };
        let e_or_w = if self.longitude >= 0 { 'E' } else { 'W' };

        let directory = match self.kind {
            LandCoverKind::TreeCover => "treecover/processed",
            LandCoverKind::WaterMask => "watermask/processed",
        };

        format!("{}/{:02}{}_{:03}{}.raster",
                directory,
                self.latitude.abs(),
                n_or_s,
                self.longitude.abs(),
                e_or_w,
        )
    }

    fn generate(&self, context: &mut AssetLoadContext) -> Result<Self::Type, Box<Error>> {
        if self.raw.is_some() {
            return self.generate_from_raw();
        }

        Self {
            latitude: self.latitude,
            longitude: self.longitude,
            kind: self.kind,
            raw: Some(self.raw_params().load(context)?),
        }.generate_from_raw()
    }
}

pub struct BlueMarble;
impl WebAsset for BlueMarble {
    type Type = GlobalRaster<u8>;

    fn url(&self) -> String {
        "https://eoimages.gsfc.nasa.gov/images/imagerecords/74000/74218/\
         world.200412.3x21600x10800.png"
            .to_owned()
    }
    fn filename(&self) -> String {
        "bluemarble/world.200412.3x21600x10800.png".to_owned()
    }
    fn parse(
        &self,
        _context: &mut AssetLoadContext,
        data: Vec<u8>,
    ) -> Result<Self::Type, Box<::std::error::Error>> {
        let image = image::load_from_memory_with_format(&data, ImageFormat::PNG)?
            .to_rgb();
        Ok(GlobalRaster {
            width: image.width() as usize,
            height: image.height() as usize,
            bands: 3,
            values: image.into_raw(),
        })
    }
}

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

    #[test]
    fn to_raw_params() {
        assert_eq!(
            LandCoverParams {
                latitude: 165,
                longitude: 31,
                kind: LandCoverKind::TreeCover,
                raw: None,
            }.raw_params(),
            RawLandCoverParams {
                latitude: 160,
                longitude: 30,
                kind: LandCoverKind::TreeCover,
            }
        );

        assert_eq!(
            LandCoverParams {
                latitude: 20,
                longitude: 20,
                kind: LandCoverKind::TreeCover,
                raw: None,
            }.raw_params(),
            RawLandCoverParams {
                latitude: 20,
                longitude: 20,
                kind: LandCoverKind::TreeCover,
            }
        );

        assert_eq!(
            LandCoverParams {
                latitude: -18,
                longitude: -18,
                kind: LandCoverKind::TreeCover,
                raw: None,
            }.raw_params(),
            RawLandCoverParams {
                latitude: -20,
                longitude: -20,
                kind: LandCoverKind::TreeCover,
            }
        );

        assert_eq!(
            LandCoverParams {
                latitude: -30,
                longitude: -30,
                kind: LandCoverKind::TreeCover,
                raw: None,
            }.raw_params(),
            RawLandCoverParams {
                latitude: -30,
                longitude: -30,
                kind: LandCoverKind::TreeCover,
            }
        );
    }
}
<|endoftext|>