use js_int::UInt;
use ruma_common::{presence::PresenceState, OwnedMxcUri, OwnedUserId};
use ruma_macros::{Event, EventContent};
use serde::{ser::SerializeStruct, Deserialize, Serialize};
use super::EventContent;
#[derive(Clone, Debug, Event)]
#[allow(clippy::exhaustive_structs)]
pub struct PresenceEvent {
pub content: PresenceEventContent,
pub sender: OwnedUserId,
}
impl Serialize for PresenceEvent {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
let mut state = serializer.serialize_struct("PresenceEvent", 3)?;
state.serialize_field("type", &self.content.event_type())?;
state.serialize_field("content", &self.content)?;
state.serialize_field("sender", &self.sender)?;
state.end()
}
}
#[derive(Clone, Debug, Deserialize, Serialize, EventContent)]
#[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
#[ruma_event(type = "m.presence")]
pub struct PresenceEventContent {
#[serde(skip_serializing_if = "Option::is_none")]
#[cfg_attr(
feature = "compat-empty-string-null",
serde(default, deserialize_with = "ruma_common::serde::empty_string_as_none")
)]
pub avatar_url: Option<OwnedMxcUri>,
#[serde(skip_serializing_if = "Option::is_none")]
pub currently_active: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub displayname: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub last_active_ago: Option<UInt>,
pub presence: PresenceState,
#[serde(skip_serializing_if = "Option::is_none")]
pub status_msg: Option<String>,
}
impl PresenceEventContent {
pub fn new(presence: PresenceState) -> Self {
Self {
avatar_url: None,
currently_active: None,
displayname: None,
last_active_ago: None,
presence,
status_msg: None,
}
}
}
#[cfg(test)]
mod tests {
use js_int::uint;
use ruma_common::{mxc_uri, presence::PresenceState};
use serde_json::{from_value as from_json_value, json, to_value as to_json_value};
use super::{PresenceEvent, PresenceEventContent};
#[test]
fn serialization() {
let content = PresenceEventContent {
avatar_url: Some(mxc_uri!("mxc://localhost/wefuiwegh8742w").to_owned()),
currently_active: Some(false),
displayname: None,
last_active_ago: Some(uint!(2_478_593)),
presence: PresenceState::Online,
status_msg: Some("Making cupcakes".into()),
};
let json = json!({
"avatar_url": "mxc://localhost/wefuiwegh8742w",
"currently_active": false,
"last_active_ago": 2_478_593,
"presence": "online",
"status_msg": "Making cupcakes"
});
assert_eq!(to_json_value(&content).unwrap(), json);
}
#[test]
fn deserialization() {
let json = json!({
"content": {
"avatar_url": "mxc://localhost/wefuiwegh8742w",
"currently_active": false,
"last_active_ago": 2_478_593,
"presence": "online",
"status_msg": "Making cupcakes"
},
"sender": "@example:localhost",
"type": "m.presence"
});
let ev = from_json_value::<PresenceEvent>(json).unwrap();
assert_eq!(
ev.content.avatar_url.as_deref(),
Some(mxc_uri!("mxc://localhost/wefuiwegh8742w"))
);
assert_eq!(ev.content.currently_active, Some(false));
assert_eq!(ev.content.displayname, None);
assert_eq!(ev.content.last_active_ago, Some(uint!(2_478_593)));
assert_eq!(ev.content.presence, PresenceState::Online);
assert_eq!(ev.content.status_msg.as_deref(), Some("Making cupcakes"));
assert_eq!(ev.sender, "@example:localhost");
#[cfg(feature = "compat-empty-string-null")]
{
let json = json!({
"content": {
"avatar_url": "",
"currently_active": false,
"last_active_ago": 2_478_593,
"presence": "online",
"status_msg": "Making cupcakes"
},
"sender": "@example:localhost",
"type": "m.presence"
});
let ev = from_json_value::<PresenceEvent>(json).unwrap();
assert_eq!(ev.content.avatar_url, None);
assert_eq!(ev.content.currently_active, Some(false));
assert_eq!(ev.content.displayname, None);
assert_eq!(ev.content.last_active_ago, Some(uint!(2_478_593)));
assert_eq!(ev.content.presence, PresenceState::Online);
assert_eq!(ev.content.status_msg.as_deref(), Some("Making cupcakes"));
assert_eq!(ev.sender, "@example:localhost");
}
}
}