From 6bd407605b788439ac58dbb0de6b9ccd1007b9da Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Fri, 30 Dec 2022 15:16:33 +0100 Subject: [PATCH] xmpp-parsers: Add Message Reactions (XEP-0444) support --- parsers/ChangeLog | 12 ++--- parsers/src/lib.rs | 3 ++ parsers/src/ns.rs | 3 ++ parsers/src/reactions.rs | 96 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 109 insertions(+), 5 deletions(-) create mode 100644 parsers/src/reactions.rs diff --git a/parsers/ChangeLog b/parsers/ChangeLog index 57fc0068..2bfb185e 100644 --- a/parsers/ChangeLog +++ b/parsers/ChangeLog @@ -1,8 +1,10 @@ Version xxx: xxx + * New parsers/serialisers: + - Message Reactions (XEP-0444) * Improvements: - - muc::user::Item: Added with_ helpers - - Correct cargo doc warnings + - muc::user::Item: Added with_ helpers + - Correct cargo doc warnings Version 0.19.2: 2022-12-17 Maxime β€œpep” Buquet @@ -17,10 +19,10 @@ Version 0.19.2: Version 0.19.1: 2022-07-13 Emmanuel Gil Peyrot * New parsers/serialisers: - - Add In-Band Real Time Text support - - Add OMEMO support + - Add In-Band Real Time Text support + - Add OMEMO support * Improvements: - - bookmarks 2: uncomment test + - bookmarks 2: uncomment test Version 0.19.0: 2022-03-07 Emmanuel Gil Peyrot diff --git a/parsers/src/lib.rs b/parsers/src/lib.rs index db45509b..6919a9bd 100644 --- a/parsers/src/lib.rs +++ b/parsers/src/lib.rs @@ -239,3 +239,6 @@ pub mod occupant_id; /// XEP-0441: Message Archive Management Preferences pub mod mam_prefs; + +/// XEP-0444: Message Reactions +pub mod reactions; diff --git a/parsers/src/ns.rs b/parsers/src/ns.rs index 5cde63cb..e2304e59 100644 --- a/parsers/src/ns.rs +++ b/parsers/src/ns.rs @@ -272,6 +272,9 @@ pub const BOOKMARKS2_COMPAT_PEP: &str = "urn:xmpp:bookmarks:1#compat-pep"; /// XEP-0421: Anonymous unique occupant identifiers for MUCs pub const OID: &str = "urn:xmpp:occupant-id:0"; +/// XEP-0444: Message Reactions +pub const REACTIONS: &str = "urn:xmpp:reactions:0"; + /// Alias for the main namespace of the stream, that is "jabber:client" when /// the component feature isn’t enabled. #[cfg(not(feature = "component"))] diff --git a/parsers/src/reactions.rs b/parsers/src/reactions.rs new file mode 100644 index 00000000..dd241e40 --- /dev/null +++ b/parsers/src/reactions.rs @@ -0,0 +1,96 @@ +// Copyright (c) 2022 Emmanuel Gil Peyrot +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +use crate::message::MessagePayload; +use crate::util::helpers::Text; + +generate_element!( + /// Container for a set of reactions. + Reactions, "reactions", REACTIONS, + attributes: [ + /// The id of the message these reactions apply to. + id: Required = "id", + ], + children: [ + /// The list of reactions. + reactions: Vec = ("reaction", REACTIONS) => Reaction, + ] +); + +impl MessagePayload for Reactions {} + +generate_element!( + /// One emoji reaction. + Reaction, "reaction", REACTIONS, + text: ( + /// The text of this reaction. + emoji: Text + ) +); + +#[cfg(test)] +mod tests { + use super::*; + use crate::Element; + use std::convert::{TryFrom, TryInto}; + + #[cfg(target_pointer_width = "32")] + #[test] + fn test_size() { + assert_size!(Reactions, 24); + assert_size!(Reaction, 12); + } + + #[cfg(target_pointer_width = "64")] + #[test] + fn test_size() { + assert_size!(Reactions, 48); + assert_size!(Reaction, 24); + } + + #[test] + fn test_empty() { + let elem: Element = "" + .parse() + .unwrap(); + let elem2 = elem.clone(); + let reactions = Reactions::try_from(elem2).unwrap(); + assert_eq!(reactions.id, "foo"); + assert_eq!(reactions.reactions.len(), 0); + } + + #[test] + fn test_multi() { + let elem: Element = + "πŸ‘‹πŸ’" + .parse() + .unwrap(); + let elem2 = elem.clone(); + let reactions = Reactions::try_from(elem2).unwrap(); + assert_eq!(reactions.id, "foo"); + assert_eq!(reactions.reactions.len(), 2); + let [hand, turtle]: [Reaction; 2] = reactions.reactions.try_into().unwrap(); + assert_eq!(hand.emoji, "πŸ‘‹"); + assert_eq!(turtle.emoji, "🐒"); + } + + #[test] + fn test_zwj_emoji() { + let elem: Element = + "πŸ‘©πŸΎβ€β€οΈβ€πŸ‘©πŸΌ" + .parse() + .unwrap(); + let elem2 = elem.clone(); + let mut reactions = Reactions::try_from(elem2).unwrap(); + assert_eq!(reactions.id, "foo"); + assert_eq!(reactions.reactions.len(), 1); + let reaction = reactions.reactions.pop().unwrap(); + assert_eq!( + reaction.emoji, + "\u{1F469}\u{1F3FE}\u{200D}\u{2764}\u{FE0F}\u{200D}\u{1F469}\u{1F3FC}" + ); + } +}