xmpp-parsers: Update XEP-0313 to its latest version

Emmanuel Gil Peyrot created

We didn’t have support for the <metadata/> element, which is a
requirement for XEP-0386: Bind 2.

Change summary

parsers/doap.xml   |  2 
parsers/src/mam.rs | 81 +++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 81 insertions(+), 2 deletions(-)

Detailed changes

parsers/doap.xml 🔗

@@ -460,7 +460,7 @@
         <xmpp:SupportedXep>
             <xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0313.html"/>
             <xmpp:status>complete</xmpp:status>
-            <xmpp:version>1.0.0</xmpp:version>
+            <xmpp:version>1.1.1</xmpp:version>
             <xmpp:since>0.1.0</xmpp:since>
         </xmpp:SupportedXep>
     </implements>

parsers/src/mam.rs 🔗

@@ -4,7 +4,13 @@
 // 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::{
+    error::{Error, FromElementError},
+    FromXml, IntoXml,
+};
+
 use crate::data_forms::DataForm;
+use crate::date::DateTime;
 use crate::forwarding::Forwarded;
 use crate::iq::{IqGetPayload, IqResultPayload, IqSetPayload};
 use crate::message::MessagePayload;
@@ -13,7 +19,6 @@ use crate::pubsub::NodeName;
 use crate::rsm::{SetQuery, SetResult};
 use crate::Element;
 use minidom::Node;
-use xso::error::{Error, FromElementError};
 
 generate_id!(
     /// An identifier matching a result message to the query requesting it.
@@ -159,6 +164,52 @@ generate_element!(
 
 impl IqResultPayload for Fin {}
 
+/// Metadata of the first message in the archive.
+#[derive(FromXml, IntoXml, Debug, Clone, PartialEq)]
+#[xml(namespace = ns::MAM, name = "start")]
+pub struct Start {
+    /// The id of the first message in the archive.
+    #[xml(attribute)]
+    pub id: String,
+
+    /// Time at which that message was sent.
+    #[xml(attribute)]
+    pub timestamp: DateTime,
+}
+
+/// Metadata of the last message in the archive.
+#[derive(FromXml, IntoXml, Debug, Clone, PartialEq)]
+#[xml(namespace = ns::MAM, name = "end")]
+pub struct End {
+    /// The id of the last message in the archive.
+    #[xml(attribute)]
+    pub id: String,
+
+    /// Time at which that message was sent.
+    #[xml(attribute)]
+    pub timestamp: DateTime,
+}
+
+/// Request an archive for its metadata.
+#[derive(FromXml, IntoXml, Debug, Clone, PartialEq)]
+#[xml(namespace = ns::MAM, name = "metadata")]
+pub struct MetadataQuery;
+
+impl IqGetPayload for MetadataQuery {}
+
+generate_element!(
+/// Response from the archive, containing the start and end metadata if it isn’t empty.
+MetadataResponse, "metadata", MAM,
+children: [
+    /// Metadata about the first message in the archive.
+    start: Option<Start> = ("start", MAM) => Start,
+
+    /// Metadata about the last message in the archive.
+    end: Option<End> = ("end", MAM) => End,
+]);
+
+impl IqResultPayload for MetadataResponse {}
+
 #[cfg(test)]
 mod tests {
     use super::*;
@@ -171,6 +222,10 @@ mod tests {
         assert_size!(Result_, 164);
         assert_size!(Complete, 1);
         assert_size!(Fin, 44);
+        assert_size!(Start, 28);
+        assert_size!(End, 28);
+        assert_size!(MetadataQuery, 0);
+        assert_size!(MetadataResponse, 56);
     }
 
     #[cfg(target_pointer_width = "64")]
@@ -181,6 +236,10 @@ mod tests {
         assert_size!(Result_, 312);
         assert_size!(Complete, 1);
         assert_size!(Fin, 88);
+        assert_size!(Start, 40);
+        assert_size!(End, 40);
+        assert_size!(MetadataQuery, 0);
+        assert_size!(MetadataResponse, 80);
     }
 
     #[test]
@@ -291,6 +350,26 @@ mod tests {
         Query::try_from(elem).unwrap();
     }
 
+    #[test]
+    fn test_metadata() {
+        let elem: Element = r"<metadata xmlns='urn:xmpp:mam:2'/>".parse().unwrap();
+        MetadataQuery::try_from(elem).unwrap();
+
+        let elem: Element = r"<metadata xmlns='urn:xmpp:mam:2'>
+  <start id='YWxwaGEg' timestamp='2008-08-22T21:09:04Z' />
+  <end id='b21lZ2Eg' timestamp='2020-04-20T14:34:21Z' />
+</metadata>"
+            .parse()
+            .unwrap();
+        let metadata = MetadataResponse::try_from(elem).unwrap();
+        let start = metadata.start.unwrap();
+        let end = metadata.end.unwrap();
+        assert_eq!(start.id, "YWxwaGEg");
+        assert_eq!(start.timestamp.0.timestamp(), 1219439344);
+        assert_eq!(end.id, "b21lZ2Eg");
+        assert_eq!(end.timestamp.0.timestamp(), 1587393261);
+    }
+
     #[test]
     fn test_invalid_child() {
         let elem: Element = "<query xmlns='urn:xmpp:mam:2'><coucou/></query>"