Implement XEP-0463: MUC Affiliations Versioning

This commit is contained in:
Link Mauve 2026-05-06 20:01:44 +02:00
commit d0b28a94b0
6 changed files with 91 additions and 2 deletions

69
parsers/src/muc/mav.rs Normal file
View file

@ -0,0 +1,69 @@
// Copyright (c) 2026 Link Mauve <linkmauve@linkmauve.fr>
//
// 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 xso::{AsXml, FromXml};
use crate::ns;
generate_id!(
/// Unique and opaque string, indicating the last affiliation version sent by the server that
/// the client has seen, and cached.
Version
);
/// A `<mav/>` element sent by the client on join, if it wants to keep track of the affiliations in
/// the room.
#[derive(FromXml, AsXml, Debug, Clone, PartialEq)]
#[xml(namespace = ns::MAV, name = "mav")]
pub struct AffiliationsVersioning {
/// Unique and opaque string, indicating the last affiliation version sent by the server that
/// the client has seen, and cached. Sending the mav element without a since attribute is a
/// called bootstrap request, which asks the server for a full response.
#[xml(attribute(default))]
since: Option<Version>,
/// The latest version the server has.
#[xml(attribute(default))]
until: Option<Version>,
}
#[cfg(test)]
mod tests {
use super::*;
use minidom::Element;
#[cfg(target_pointer_width = "32")]
#[test]
fn test_size() {
assert_size!(Version, 12);
assert_size!(AffiliationsVersioning, 24);
}
#[cfg(target_pointer_width = "64")]
#[test]
fn test_size() {
assert_size!(Version, 24);
assert_size!(AffiliationsVersioning, 48);
}
#[test]
fn test_simple() {
let elem: Element = "<mav xmlns='urn:xmpp:muc:affiliations:1'/>"
.parse()
.unwrap();
let ver = AffiliationsVersioning::try_from(elem).unwrap();
assert!(ver.since.is_none());
assert!(ver.until.is_none());
let elem: Element =
"<mav xmlns='urn:xmpp:muc:affiliations:1' since='9pacabr2q1' until='ruz41312vw'/>"
.parse()
.unwrap();
let ver = AffiliationsVersioning::try_from(elem).unwrap();
assert_eq!(ver.since.unwrap().0, "9pacabr2q1");
assert_eq!(ver.until.unwrap().0, "ruz41312vw");
}
}

View file

@ -11,5 +11,8 @@ pub mod muc;
/// The `http://jabber.org/protocol/muc#user` protocol.
pub mod user;
/// The `urn:xmpp:muc:affiliations:1` protocol.
pub mod mav;
pub use self::muc::Muc;
pub use self::user::MucUser;

View file

@ -8,6 +8,7 @@
use xso::{AsXml, FromXml};
use crate::message::MessagePayload;
use crate::muc::mav::AffiliationsVersioning;
use crate::ns;
use crate::presence::PresencePayload;
@ -302,6 +303,10 @@ pub struct MucUser {
/// A mediated invite rejection
#[xml(child(default))]
pub decline: Option<Decline>,
/// From XEP-0463: MUC Affiliations Versioning
#[xml(child(default))]
pub affiliations: Option<AffiliationsVersioning>,
}
impl MucUser {
@ -347,7 +352,7 @@ mod tests {
assert_size!(Item, 84);
assert_size!(Invite, 44);
assert_size!(Decline, 44);
assert_size!(MucUser, 112);
assert_size!(MucUser, 136);
}
#[cfg(target_pointer_width = "64")]
@ -362,7 +367,7 @@ mod tests {
assert_size!(Item, 168);
assert_size!(Invite, 88);
assert_size!(Decline, 88);
assert_size!(MucUser, 224);
assert_size!(MucUser, 272);
}
#[test]