diff --git a/xmpp-parsers/doap.xml b/xmpp-parsers/doap.xml
index 96877c3f393695521428dd624492ef9d69d5c9c2..f578ca15160ae0b964cb35b104cd6bed9f8dd587 100644
--- a/xmpp-parsers/doap.xml
+++ b/xmpp-parsers/doap.xml
@@ -435,6 +435,14 @@
0.16.0
+
+
+
+ complete
+ 1.0.0
+ NEXT
+
+
diff --git a/xmpp-parsers/src/jingle_grouping.rs b/xmpp-parsers/src/jingle_grouping.rs
new file mode 100644
index 0000000000000000000000000000000000000000..310e09c3b053586c787c2336286d20f4b2a11d15
--- /dev/null
+++ b/xmpp-parsers/src/jingle_grouping.rs
@@ -0,0 +1,91 @@
+// Copyright (c) 2020 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::jingle::ContentId;
+
+generate_attribute!(
+ /// The semantics of the grouping.
+ Semantics, "semantics", {
+ /// Lip synchronsation.
+ Ls => "LS",
+
+ /// Bundle.
+ Bundle => "BUNDLE",
+ }
+);
+
+generate_element!(
+ #[derive(PartialEq)]
+ /// Describes a content that should be grouped with other ones.
+ Content, "content", JINGLE_GROUPING,
+ attributes: [
+ /// The name of the matching [`Content`](crate::jingle::Content).
+ name: Required = "name",
+ ]
+);
+
+impl Content {
+ /// Creates a new element.
+ pub fn new(name: &str) -> Content {
+ Content {
+ name: ContentId(name.to_string()),
+ }
+ }
+}
+
+generate_element!(
+ /// A semantic group of contents.
+ Group, "group", JINGLE_GROUPING,
+ attributes: [
+ /// Semantics of the grouping.
+ semantics: Required = "semantics",
+ ],
+ children: [
+ /// List of contents that should be grouped with each other.
+ contents: Vec = ("content", JINGLE_GROUPING) => Content
+ ]
+);
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use crate::Element;
+ use std::convert::TryFrom;
+
+ #[cfg(target_pointer_width = "32")]
+ #[test]
+ fn test_size() {
+ assert_size!(Semantics, 1);
+ assert_size!(Content, 12);
+ assert_size!(Group, 16);
+ }
+
+ #[cfg(target_pointer_width = "64")]
+ #[test]
+ fn test_size() {
+ assert_size!(Semantics, 1);
+ assert_size!(Content, 24);
+ assert_size!(Group, 32);
+ }
+
+ #[test]
+ fn parse_group() {
+ let elem: Element = "
+
+
+
+ "
+ .parse()
+ .unwrap();
+ let group = Group::try_from(elem).unwrap();
+ assert_eq!(group.semantics, Semantics::Bundle);
+ assert_eq!(group.contents.len(), 2);
+ assert_eq!(
+ group.contents,
+ &[Content::new("voice"), Content::new("webcam")]
+ );
+ }
+}
diff --git a/xmpp-parsers/src/lib.rs b/xmpp-parsers/src/lib.rs
index e65349fecb7014bae947aa663ee7f55c7639244f..4ffee2bb5d8d94e4a5099513b4f43f2ff8274cd7 100644
--- a/xmpp-parsers/src/lib.rs
+++ b/xmpp-parsers/src/lib.rs
@@ -189,6 +189,9 @@ pub mod jingle_dtls_srtp;
/// XEP-0328: JID Prep
pub mod jid_prep;
+/// XEP-0338: Jingle Grouping Framework
+pub mod jingle_grouping;
+
/// XEP-0339: Source-Specific Media Attributes in Jingle
pub mod jingle_ssma;
diff --git a/xmpp-parsers/src/ns.rs b/xmpp-parsers/src/ns.rs
index 6e4c4a4d17fb4aa0f7dc92911cdae8c2d4657c3c..345cc4ddff5499b59aa41dd20326981d71d43479 100644
--- a/xmpp-parsers/src/ns.rs
+++ b/xmpp-parsers/src/ns.rs
@@ -198,6 +198,9 @@ pub const JINGLE_DTLS: &str = "urn:xmpp:jingle:apps:dtls:0";
/// XEP-0328: JID Prep
pub const JID_PREP: &str = "urn:xmpp:jidprep:0";
+/// XEP-0338: Jingle Grouping Framework
+pub const JINGLE_GROUPING: &str = "urn:xmpp:jingle:apps:grouping:0";
+
/// XEP-0339: Source-Specific Media Attributes in Jingle
pub const JINGLE_SSMA: &str = "urn:xmpp:jingle:apps:rtp:ssma:0";