// Copyright (c) 2024 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 xso::{AsXml, FromXml}; use crate::mam; use crate::ns; use minidom::Element; /// Represents the `` element, as sent by the server in SASL 2 to advertise which features /// can be enabled during the binding step. #[derive(FromXml, AsXml, Debug, Clone, PartialEq)] #[xml(namespace = ns::BIND2, name = "bind")] pub struct BindFeature { /// The features that can be enabled by the client. #[xml(extract(default, name = "inline", fields(extract(n = .., name = "feature", fields(attribute(name = "var", type_ = String))))))] pub inline_features: Vec, } /// Represents a `` element, as sent by the client inline in the `` SASL 2 /// element, to perform the binding at the same time as the authentication. #[derive(FromXml, AsXml, Debug, Clone, PartialEq)] #[xml(namespace = ns::BIND2, name = "bind")] pub struct BindQuery { /// Short text string that typically identifies the software the user is using, mostly useful /// for diagnostic purposes for users, operators and developers. This tag may be visible to /// other entities on the XMPP network. #[xml(extract(default, fields(text(type_ = String))))] pub tag: Option, /// Features that the client requests to be automatically enabled for its new session. #[xml(element(n = ..))] pub payloads: Vec, } /// Represents a `` element, which tells the client its resource is bound, alongside other /// requests. #[derive(FromXml, AsXml, Debug, Clone, PartialEq)] #[xml(namespace = ns::BIND2, name = "bound")] pub struct Bound { /// Indicates which messages got missed by this particular device, start is the oldest message /// and end is the newest, before this connection. #[xml(child(default))] pub mam_metadata: Option, /// Additional payloads which happened during the binding process. #[xml(element(n = ..))] pub payloads: Vec, } #[cfg(test)] mod tests { use super::*; #[cfg(target_pointer_width = "32")] #[test] fn test_size() { assert_size!(BindFeature, 12); assert_size!(BindQuery, 24); assert_size!(Bound, 68); } #[cfg(target_pointer_width = "64")] #[test] fn test_size() { assert_size!(BindFeature, 24); assert_size!(BindQuery, 48); assert_size!(Bound, 104); } #[test] fn test_empty() { let elem: Element = "".parse().unwrap(); let bind = BindQuery::try_from(elem).unwrap(); assert_eq!(bind.tag, None); assert_eq!(bind.payloads.len(), 0); } #[test] fn test_simple() { // Example 1 let elem: Element = "" .parse() .unwrap(); let bind = BindFeature::try_from(elem.clone()).unwrap(); assert_eq!(bind.inline_features.len(), 3); assert_eq!(bind.inline_features[0], "urn:xmpp:carbons:2"); assert_eq!(bind.inline_features[1], "urn:xmpp:csi:0"); assert_eq!(bind.inline_features[2], "urn:xmpp:sm:3"); let elem2 = bind.into(); assert_eq!(elem, elem2); // Example 2 let elem: Element = "AwesomeXMPP" .parse() .unwrap(); let bind = BindQuery::try_from(elem).unwrap(); assert_eq!(bind.tag.unwrap(), "AwesomeXMPP"); assert_eq!(bind.payloads.len(), 0); // Example 3 let elem: Element = "AwesomeXMPP".parse().unwrap(); let bind = BindQuery::try_from(elem).unwrap(); assert_eq!(bind.tag.unwrap(), "AwesomeXMPP"); assert_eq!(bind.payloads.len(), 3); // Example 4 let elem: Element = "".parse().unwrap(); let bound = Bound::try_from(elem).unwrap(); assert!(bound.mam_metadata.is_some()); assert_eq!(bound.payloads.len(), 0); } }