iq: Make @id required, as per RFC6120 §8.1.3.
This commit is contained in:
parent
d811c10ed3
commit
63dcba03b2
1 changed files with 44 additions and 33 deletions
77
src/iq.rs
77
src/iq.rs
|
@ -63,7 +63,7 @@ pub struct Iq {
|
||||||
|
|
||||||
/// The @id attribute of this stanza, which is required in order to match a
|
/// The @id attribute of this stanza, which is required in order to match a
|
||||||
/// request with its result/error.
|
/// request with its result/error.
|
||||||
pub id: Option<String>,
|
pub id: String,
|
||||||
|
|
||||||
/// The payload content of this stanza.
|
/// The payload content of this stanza.
|
||||||
pub payload: IqType,
|
pub payload: IqType,
|
||||||
|
@ -71,41 +71,41 @@ pub struct Iq {
|
||||||
|
|
||||||
impl Iq {
|
impl Iq {
|
||||||
/// Creates an `<iq/>` stanza containing a get request.
|
/// Creates an `<iq/>` stanza containing a get request.
|
||||||
pub fn from_get(payload: impl IqGetPayload) -> Iq {
|
pub fn from_get(id: String, payload: impl IqGetPayload) -> Iq {
|
||||||
Iq {
|
Iq {
|
||||||
from: None,
|
from: None,
|
||||||
to: None,
|
to: None,
|
||||||
id: None,
|
id,
|
||||||
payload: IqType::Get(payload.into()),
|
payload: IqType::Get(payload.into()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates an `<iq/>` stanza containing a set request.
|
/// Creates an `<iq/>` stanza containing a set request.
|
||||||
pub fn from_set(payload: impl IqSetPayload) -> Iq {
|
pub fn from_set(id: String, payload: impl IqSetPayload) -> Iq {
|
||||||
Iq {
|
Iq {
|
||||||
from: None,
|
from: None,
|
||||||
to: None,
|
to: None,
|
||||||
id: None,
|
id,
|
||||||
payload: IqType::Set(payload.into()),
|
payload: IqType::Set(payload.into()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates an `<iq/>` stanza containing a result.
|
/// Creates an `<iq/>` stanza containing a result.
|
||||||
pub fn from_result(payload: Option<impl IqResultPayload>) -> Iq {
|
pub fn from_result(id: String, payload: Option<impl IqResultPayload>) -> Iq {
|
||||||
Iq {
|
Iq {
|
||||||
from: None,
|
from: None,
|
||||||
to: None,
|
to: None,
|
||||||
id: None,
|
id,
|
||||||
payload: IqType::Result(payload.map(|payload| payload.into())),
|
payload: IqType::Result(payload.map(|payload| payload.into())),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates an `<iq/>` stanza containing an error.
|
/// Creates an `<iq/>` stanza containing an error.
|
||||||
pub fn from_error(payload: StanzaError) -> Iq {
|
pub fn from_error(id: String, payload: StanzaError) -> Iq {
|
||||||
Iq {
|
Iq {
|
||||||
from: None,
|
from: None,
|
||||||
to: None,
|
to: None,
|
||||||
id: None,
|
id,
|
||||||
payload: IqType::Error(payload),
|
payload: IqType::Error(payload),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -124,7 +124,7 @@ impl Iq {
|
||||||
|
|
||||||
/// Sets the id of this stanza, in order to later match its response.
|
/// Sets the id of this stanza, in order to later match its response.
|
||||||
pub fn with_id(mut self, id: String) -> Iq {
|
pub fn with_id(mut self, id: String) -> Iq {
|
||||||
self.id = Some(id);
|
self.id = id;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -136,7 +136,7 @@ impl TryFrom<Element> for Iq {
|
||||||
check_self!(root, "iq", DEFAULT_NS);
|
check_self!(root, "iq", DEFAULT_NS);
|
||||||
let from = get_attr!(root, "from", optional);
|
let from = get_attr!(root, "from", optional);
|
||||||
let to = get_attr!(root, "to", optional);
|
let to = get_attr!(root, "to", optional);
|
||||||
let id = get_attr!(root, "id", optional);
|
let id = get_attr!(root, "id", required);
|
||||||
let type_: String = get_attr!(root, "type", required);
|
let type_: String = get_attr!(root, "type", required);
|
||||||
|
|
||||||
let mut payload = None;
|
let mut payload = None;
|
||||||
|
@ -247,19 +247,30 @@ mod tests {
|
||||||
Error::ParseError(string) => string,
|
Error::ParseError(string) => string,
|
||||||
_ => panic!(),
|
_ => panic!(),
|
||||||
};
|
};
|
||||||
|
assert_eq!(message, "Required attribute 'id' missing.");
|
||||||
|
|
||||||
|
#[cfg(not(feature = "component"))]
|
||||||
|
let elem: Element = "<iq xmlns='jabber:client' id='coucou'/>".parse().unwrap();
|
||||||
|
#[cfg(feature = "component")]
|
||||||
|
let elem: Element = "<iq xmlns='jabber:component:accept' id='coucou'/>".parse().unwrap();
|
||||||
|
let error = Iq::try_from(elem).unwrap_err();
|
||||||
|
let message = match error {
|
||||||
|
Error::ParseError(string) => string,
|
||||||
|
_ => panic!(),
|
||||||
|
};
|
||||||
assert_eq!(message, "Required attribute 'type' missing.");
|
assert_eq!(message, "Required attribute 'type' missing.");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_get() {
|
fn test_get() {
|
||||||
#[cfg(not(feature = "component"))]
|
#[cfg(not(feature = "component"))]
|
||||||
let elem: Element = "<iq xmlns='jabber:client' type='get'>
|
let elem: Element = "<iq xmlns='jabber:client' type='get' id='foo'>
|
||||||
<foo xmlns='bar'/>
|
<foo xmlns='bar'/>
|
||||||
</iq>"
|
</iq>"
|
||||||
.parse()
|
.parse()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
#[cfg(feature = "component")]
|
#[cfg(feature = "component")]
|
||||||
let elem: Element = "<iq xmlns='jabber:component:accept' type='get'>
|
let elem: Element = "<iq xmlns='jabber:component:accept' type='get' id='foo'>
|
||||||
<foo xmlns='bar'/>
|
<foo xmlns='bar'/>
|
||||||
</iq>"
|
</iq>"
|
||||||
.parse()
|
.parse()
|
||||||
|
@ -268,7 +279,7 @@ mod tests {
|
||||||
let query: Element = "<foo xmlns='bar'/>".parse().unwrap();
|
let query: Element = "<foo xmlns='bar'/>".parse().unwrap();
|
||||||
assert_eq!(iq.from, None);
|
assert_eq!(iq.from, None);
|
||||||
assert_eq!(iq.to, None);
|
assert_eq!(iq.to, None);
|
||||||
assert_eq!(iq.id, None);
|
assert_eq!(&iq.id, "foo");
|
||||||
assert!(match iq.payload {
|
assert!(match iq.payload {
|
||||||
IqType::Get(element) => element.compare_to(&query),
|
IqType::Get(element) => element.compare_to(&query),
|
||||||
_ => false,
|
_ => false,
|
||||||
|
@ -278,13 +289,13 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn test_set() {
|
fn test_set() {
|
||||||
#[cfg(not(feature = "component"))]
|
#[cfg(not(feature = "component"))]
|
||||||
let elem: Element = "<iq xmlns='jabber:client' type='set'>
|
let elem: Element = "<iq xmlns='jabber:client' type='set' id='vcard'>
|
||||||
<vCard xmlns='vcard-temp'/>
|
<vCard xmlns='vcard-temp'/>
|
||||||
</iq>"
|
</iq>"
|
||||||
.parse()
|
.parse()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
#[cfg(feature = "component")]
|
#[cfg(feature = "component")]
|
||||||
let elem: Element = "<iq xmlns='jabber:component:accept' type='set'>
|
let elem: Element = "<iq xmlns='jabber:component:accept' type='set' id='vcard'>
|
||||||
<vCard xmlns='vcard-temp'/>
|
<vCard xmlns='vcard-temp'/>
|
||||||
</iq>"
|
</iq>"
|
||||||
.parse()
|
.parse()
|
||||||
|
@ -293,7 +304,7 @@ mod tests {
|
||||||
let vcard: Element = "<vCard xmlns='vcard-temp'/>".parse().unwrap();
|
let vcard: Element = "<vCard xmlns='vcard-temp'/>".parse().unwrap();
|
||||||
assert_eq!(iq.from, None);
|
assert_eq!(iq.from, None);
|
||||||
assert_eq!(iq.to, None);
|
assert_eq!(iq.to, None);
|
||||||
assert_eq!(iq.id, None);
|
assert_eq!(&iq.id, "vcard");
|
||||||
assert!(match iq.payload {
|
assert!(match iq.payload {
|
||||||
IqType::Set(element) => element.compare_to(&vcard),
|
IqType::Set(element) => element.compare_to(&vcard),
|
||||||
_ => false,
|
_ => false,
|
||||||
|
@ -303,15 +314,15 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn test_result_empty() {
|
fn test_result_empty() {
|
||||||
#[cfg(not(feature = "component"))]
|
#[cfg(not(feature = "component"))]
|
||||||
let elem: Element = "<iq xmlns='jabber:client' type='result'/>".parse().unwrap();
|
let elem: Element = "<iq xmlns='jabber:client' type='result' id='res'/>".parse().unwrap();
|
||||||
#[cfg(feature = "component")]
|
#[cfg(feature = "component")]
|
||||||
let elem: Element = "<iq xmlns='jabber:component:accept' type='result'/>"
|
let elem: Element = "<iq xmlns='jabber:component:accept' type='result' id='res'/>"
|
||||||
.parse()
|
.parse()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let iq = Iq::try_from(elem).unwrap();
|
let iq = Iq::try_from(elem).unwrap();
|
||||||
assert_eq!(iq.from, None);
|
assert_eq!(iq.from, None);
|
||||||
assert_eq!(iq.to, None);
|
assert_eq!(iq.to, None);
|
||||||
assert_eq!(iq.id, None);
|
assert_eq!(&iq.id, "res");
|
||||||
assert!(match iq.payload {
|
assert!(match iq.payload {
|
||||||
IqType::Result(None) => true,
|
IqType::Result(None) => true,
|
||||||
_ => false,
|
_ => false,
|
||||||
|
@ -321,13 +332,13 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn test_result() {
|
fn test_result() {
|
||||||
#[cfg(not(feature = "component"))]
|
#[cfg(not(feature = "component"))]
|
||||||
let elem: Element = "<iq xmlns='jabber:client' type='result'>
|
let elem: Element = "<iq xmlns='jabber:client' type='result' id='res'>
|
||||||
<query xmlns='http://jabber.org/protocol/disco#items'/>
|
<query xmlns='http://jabber.org/protocol/disco#items'/>
|
||||||
</iq>"
|
</iq>"
|
||||||
.parse()
|
.parse()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
#[cfg(feature = "component")]
|
#[cfg(feature = "component")]
|
||||||
let elem: Element = "<iq xmlns='jabber:component:accept' type='result'>
|
let elem: Element = "<iq xmlns='jabber:component:accept' type='result' id='res'>
|
||||||
<query xmlns='http://jabber.org/protocol/disco#items'/>
|
<query xmlns='http://jabber.org/protocol/disco#items'/>
|
||||||
</iq>"
|
</iq>"
|
||||||
.parse()
|
.parse()
|
||||||
|
@ -338,7 +349,7 @@ mod tests {
|
||||||
.unwrap();
|
.unwrap();
|
||||||
assert_eq!(iq.from, None);
|
assert_eq!(iq.from, None);
|
||||||
assert_eq!(iq.to, None);
|
assert_eq!(iq.to, None);
|
||||||
assert_eq!(iq.id, None);
|
assert_eq!(&iq.id, "res");
|
||||||
assert!(match iq.payload {
|
assert!(match iq.payload {
|
||||||
IqType::Result(Some(element)) => element.compare_to(&query),
|
IqType::Result(Some(element)) => element.compare_to(&query),
|
||||||
_ => false,
|
_ => false,
|
||||||
|
@ -348,7 +359,7 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn test_error() {
|
fn test_error() {
|
||||||
#[cfg(not(feature = "component"))]
|
#[cfg(not(feature = "component"))]
|
||||||
let elem: Element = "<iq xmlns='jabber:client' type='error'>
|
let elem: Element = "<iq xmlns='jabber:client' type='error' id='err1'>
|
||||||
<ping xmlns='urn:xmpp:ping'/>
|
<ping xmlns='urn:xmpp:ping'/>
|
||||||
<error type='cancel'>
|
<error type='cancel'>
|
||||||
<service-unavailable xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>
|
<service-unavailable xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>
|
||||||
|
@ -357,7 +368,7 @@ mod tests {
|
||||||
.parse()
|
.parse()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
#[cfg(feature = "component")]
|
#[cfg(feature = "component")]
|
||||||
let elem: Element = "<iq xmlns='jabber:component:accept' type='error'>
|
let elem: Element = "<iq xmlns='jabber:component:accept' type='error' id='err1'>
|
||||||
<ping xmlns='urn:xmpp:ping'/>
|
<ping xmlns='urn:xmpp:ping'/>
|
||||||
<error type='cancel'>
|
<error type='cancel'>
|
||||||
<service-unavailable xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>
|
<service-unavailable xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>
|
||||||
|
@ -368,7 +379,7 @@ mod tests {
|
||||||
let iq = Iq::try_from(elem).unwrap();
|
let iq = Iq::try_from(elem).unwrap();
|
||||||
assert_eq!(iq.from, None);
|
assert_eq!(iq.from, None);
|
||||||
assert_eq!(iq.to, None);
|
assert_eq!(iq.to, None);
|
||||||
assert_eq!(iq.id, None);
|
assert_eq!(iq.id, "err1");
|
||||||
match iq.payload {
|
match iq.payload {
|
||||||
IqType::Error(error) => {
|
IqType::Error(error) => {
|
||||||
assert_eq!(error.type_, ErrorType::Cancel);
|
assert_eq!(error.type_, ErrorType::Cancel);
|
||||||
|
@ -387,11 +398,11 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn test_children_invalid() {
|
fn test_children_invalid() {
|
||||||
#[cfg(not(feature = "component"))]
|
#[cfg(not(feature = "component"))]
|
||||||
let elem: Element = "<iq xmlns='jabber:client' type='error'></iq>"
|
let elem: Element = "<iq xmlns='jabber:client' type='error' id='error'/>"
|
||||||
.parse()
|
.parse()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
#[cfg(feature = "component")]
|
#[cfg(feature = "component")]
|
||||||
let elem: Element = "<iq xmlns='jabber:component:accept' type='error'></iq>"
|
let elem: Element = "<iq xmlns='jabber:component:accept' type='error' id='error'/>"
|
||||||
.parse()
|
.parse()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let error = Iq::try_from(elem).unwrap_err();
|
let error = Iq::try_from(elem).unwrap_err();
|
||||||
|
@ -405,15 +416,15 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn test_serialise() {
|
fn test_serialise() {
|
||||||
#[cfg(not(feature = "component"))]
|
#[cfg(not(feature = "component"))]
|
||||||
let elem: Element = "<iq xmlns='jabber:client' type='result'/>".parse().unwrap();
|
let elem: Element = "<iq xmlns='jabber:client' type='result' id='res'/>".parse().unwrap();
|
||||||
#[cfg(feature = "component")]
|
#[cfg(feature = "component")]
|
||||||
let elem: Element = "<iq xmlns='jabber:component:accept' type='result'/>"
|
let elem: Element = "<iq xmlns='jabber:component:accept' type='result' id='res'/>"
|
||||||
.parse()
|
.parse()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let iq2 = Iq {
|
let iq2 = Iq {
|
||||||
from: None,
|
from: None,
|
||||||
to: None,
|
to: None,
|
||||||
id: None,
|
id: String::from("res"),
|
||||||
payload: IqType::Result(None),
|
payload: IqType::Result(None),
|
||||||
};
|
};
|
||||||
let elem2 = iq2.into();
|
let elem2 = iq2.into();
|
||||||
|
@ -423,9 +434,9 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn test_disco() {
|
fn test_disco() {
|
||||||
#[cfg(not(feature = "component"))]
|
#[cfg(not(feature = "component"))]
|
||||||
let elem: Element = "<iq xmlns='jabber:client' type='get'><query xmlns='http://jabber.org/protocol/disco#info'/></iq>".parse().unwrap();
|
let elem: Element = "<iq xmlns='jabber:client' type='get' id='disco'><query xmlns='http://jabber.org/protocol/disco#info'/></iq>".parse().unwrap();
|
||||||
#[cfg(feature = "component")]
|
#[cfg(feature = "component")]
|
||||||
let elem: Element = "<iq xmlns='jabber:component:accept' type='get'><query xmlns='http://jabber.org/protocol/disco#info'/></iq>".parse().unwrap();
|
let elem: Element = "<iq xmlns='jabber:component:accept' type='get' id='disco'><query xmlns='http://jabber.org/protocol/disco#info'/></iq>".parse().unwrap();
|
||||||
let iq = Iq::try_from(elem).unwrap();
|
let iq = Iq::try_from(elem).unwrap();
|
||||||
let disco_info = match iq.payload {
|
let disco_info = match iq.payload {
|
||||||
IqType::Get(payload) => DiscoInfoQuery::try_from(payload).unwrap(),
|
IqType::Get(payload) => DiscoInfoQuery::try_from(payload).unwrap(),
|
||||||
|
|
Loading…
Reference in a new issue