xmpp_parsers::jingle: add ReasonElement::other

The Jingle XEP states [1] that the `<reason/>` element may contain up
to one element which further qualifies the error condition:

> The <reason/> element MAY contain an element qualified by some other
> namespace that provides more detailed machine-readable information
> about the reason for the action.

The schema agrees:

> ```
>   <xs:complexType name='reasonElementType'>
>     <xs:sequence>
>       <xs:choice>
>         <!-- … omitted … -->
>       </xs:choice>
>       <!-- … omitted … -->
>       <xs:any namespace='##other' minOccurs='0' maxOccurs='1'/>
>     </xs:sequence>
>   </xs:complexType>
> ```

   [1]: https://xmpp.org/extensions/xep-0166.html#def-reason
This commit is contained in:
Jonas Schäfer 2024-03-30 10:03:49 +01:00
parent ed0a1cd8cf
commit b5de6cb82c
1 changed files with 19 additions and 6 deletions

View File

@ -468,6 +468,11 @@ pub struct ReasonElement {
/// A human-readable description of this reason.
pub text: Option<String>,
/// A machine-readable extension of the reason.
// using a box here to save a significant amount of bytes in the common
// (absent) case.
pub other: Option<Box<Element>>,
}
impl fmt::Display for ReasonElement {
@ -488,6 +493,7 @@ impl TryFrom<Element> for ReasonElement {
check_no_attributes!(elem, "reason");
let mut reason = None;
let mut text = None;
let mut other = None;
for child in elem.children() {
if child.is("text", ns::JINGLE) {
check_no_children!(child, "text");
@ -505,12 +511,18 @@ impl TryFrom<Element> for ReasonElement {
check_no_children!(child, "reason");
check_no_attributes!(child, "reason");
reason = Some(child.name().parse()?);
} else if other.is_none() {
other = Some(Box::new(child.clone()));
} else {
return Err(Error::ParseError("Reason contains a foreign element."));
}
}
let reason = reason.ok_or(Error::ParseError("Reason doesnt contain a valid reason."))?;
Ok(ReasonElement { reason, text })
Ok(ReasonElement {
reason,
text,
other,
})
}
}
@ -524,6 +536,7 @@ impl From<ReasonElement> for Element {
.into_iter()
.map(|text| Element::builder("text", ns::JINGLE).append(text)),
)
.append_all(reason.other.into_iter().map(|other| *other))
.build()
}
}
@ -684,9 +697,9 @@ mod tests {
assert_size!(ContentId, 12);
assert_size!(Content, 216);
assert_size!(Reason, 1);
assert_size!(ReasonElement, 16);
assert_size!(ReasonElement, 20);
assert_size!(SessionId, 12);
assert_size!(Jingle, 104);
assert_size!(Jingle, 108);
}
#[cfg(target_pointer_width = "64")]
@ -703,9 +716,9 @@ mod tests {
#[cfg(feature = "stable")]
assert_size!(Content, 440);
assert_size!(Reason, 1);
assert_size!(ReasonElement, 32);
assert_size!(ReasonElement, 40);
assert_size!(SessionId, 24);
assert_size!(Jingle, 208);
assert_size!(Jingle, 216);
}
#[test]
@ -850,7 +863,7 @@ mod tests {
Error::ParseError(string) => string,
_ => panic!(),
};
assert_eq!(message, "Reason contains a foreign element.");
assert_eq!(message, "Reason doesnt contain a valid reason.");
let elem: Element = "<jingle xmlns='urn:xmpp:jingle:1' action='session-initiate' sid='coucou'><reason><decline/></reason><reason/></jingle>".parse().unwrap();
let error = Jingle::try_from(elem).unwrap_err();