parsers: port enums over to derive macros
This commit is contained in:
parent
c028c3b91a
commit
6afd0ef52f
7 changed files with 563 additions and 428 deletions
|
|
@ -4,12 +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::{text::EmptyAsNone, AsXml, FromXml};
|
||||
|
||||
use crate::message::MessagePayload;
|
||||
use crate::ns;
|
||||
use crate::presence::PresencePayload;
|
||||
use jid::Jid;
|
||||
use minidom::Element;
|
||||
use minidom::Node;
|
||||
use std::collections::BTreeMap;
|
||||
use std::convert::TryFrom;
|
||||
use xso::error::{Error, FromElementError};
|
||||
|
|
@ -34,163 +35,195 @@ generate_attribute!(
|
|||
}
|
||||
);
|
||||
|
||||
generate_element_enum!(
|
||||
/// List of valid error conditions.
|
||||
DefinedCondition, "condition", XMPP_STANZAS, {
|
||||
/// The sender has sent a stanza containing XML that does not conform
|
||||
/// to the appropriate schema or that cannot be processed (e.g., an IQ
|
||||
/// stanza that includes an unrecognized value of the 'type' attribute,
|
||||
/// or an element that is qualified by a recognized namespace but that
|
||||
/// violates the defined syntax for the element); the associated error
|
||||
/// type SHOULD be "modify".
|
||||
BadRequest => "bad-request",
|
||||
/// List of valid error conditions.
|
||||
#[derive(FromXml, AsXml, PartialEq, Debug, Clone)]
|
||||
#[xml(namespace = ns::XMPP_STANZAS, exhaustive)]
|
||||
pub enum DefinedCondition {
|
||||
/// The sender has sent a stanza containing XML that does not conform
|
||||
/// to the appropriate schema or that cannot be processed (e.g., an IQ
|
||||
/// stanza that includes an unrecognized value of the 'type' attribute,
|
||||
/// or an element that is qualified by a recognized namespace but that
|
||||
/// violates the defined syntax for the element); the associated error
|
||||
/// type SHOULD be "modify".
|
||||
#[xml(name = "bad-request")]
|
||||
BadRequest,
|
||||
|
||||
/// Access cannot be granted because an existing resource exists with
|
||||
/// the same name or address; the associated error type SHOULD be
|
||||
/// "cancel".
|
||||
Conflict => "conflict",
|
||||
/// Access cannot be granted because an existing resource exists with
|
||||
/// the same name or address; the associated error type SHOULD be
|
||||
/// "cancel".
|
||||
#[xml(name = "conflict")]
|
||||
Conflict,
|
||||
|
||||
/// The feature represented in the XML stanza is not implemented by the
|
||||
/// intended recipient or an intermediate server and therefore the
|
||||
/// stanza cannot be processed (e.g., the entity understands the
|
||||
/// namespace but does not recognize the element name); the associated
|
||||
/// error type SHOULD be "cancel" or "modify".
|
||||
FeatureNotImplemented => "feature-not-implemented",
|
||||
/// The feature represented in the XML stanza is not implemented by the
|
||||
/// intended recipient or an intermediate server and therefore the
|
||||
/// stanza cannot be processed (e.g., the entity understands the
|
||||
/// namespace but does not recognize the element name); the associated
|
||||
/// error type SHOULD be "cancel" or "modify".
|
||||
#[xml(name = "feature-not-implemented")]
|
||||
FeatureNotImplemented,
|
||||
|
||||
/// The requesting entity does not possess the necessary permissions to
|
||||
/// perform an action that only certain authorized roles or individuals
|
||||
/// are allowed to complete (i.e., it typically relates to
|
||||
/// authorization rather than authentication); the associated error
|
||||
/// type SHOULD be "auth".
|
||||
Forbidden => "forbidden",
|
||||
/// The requesting entity does not possess the necessary permissions to
|
||||
/// perform an action that only certain authorized roles or individuals
|
||||
/// are allowed to complete (i.e., it typically relates to
|
||||
/// authorization rather than authentication); the associated error
|
||||
/// type SHOULD be "auth".
|
||||
#[xml(name = "forbidden")]
|
||||
Forbidden,
|
||||
|
||||
/// The recipient or server can no longer be contacted at this address,
|
||||
/// typically on a permanent basis (as opposed to the \<redirect/\> error
|
||||
/// condition, which is used for temporary addressing failures); the
|
||||
/// associated error type SHOULD be "cancel" and the error stanza
|
||||
/// SHOULD include a new address (if available) as the XML character
|
||||
/// data of the \<gone/\> element (which MUST be a Uniform Resource
|
||||
/// Identifier (URI) or Internationalized Resource Identifier (IRI) at
|
||||
/// which the entity can be contacted, typically an XMPP IRI as
|
||||
/// specified in [XMPP‑URI](https://www.rfc-editor.org/rfc/rfc5122)).
|
||||
Gone => "gone",
|
||||
/// The recipient or server can no longer be contacted at this address,
|
||||
/// typically on a permanent basis (as opposed to the \<redirect/\> error
|
||||
/// condition, which is used for temporary addressing failures); the
|
||||
/// associated error type SHOULD be "cancel" and the error stanza
|
||||
/// SHOULD include a new address (if available) as the XML character
|
||||
/// data of the \<gone/\> element (which MUST be a Uniform Resource
|
||||
/// Identifier (URI) or Internationalized Resource Identifier (IRI) at
|
||||
/// which the entity can be contacted, typically an XMPP IRI as
|
||||
/// specified in [XMPP‑URI](https://www.rfc-editor.org/rfc/rfc5122)).
|
||||
#[xml(name = "gone")]
|
||||
Gone {
|
||||
/// The new address of the entity for which the error was returned,
|
||||
/// if available.
|
||||
#[xml(text(codec = EmptyAsNone))]
|
||||
new_address: Option<String>,
|
||||
},
|
||||
|
||||
/// The server has experienced a misconfiguration or other internal
|
||||
/// error that prevents it from processing the stanza; the associated
|
||||
/// error type SHOULD be "cancel".
|
||||
InternalServerError => "internal-server-error",
|
||||
/// The server has experienced a misconfiguration or other internal
|
||||
/// error that prevents it from processing the stanza; the associated
|
||||
/// error type SHOULD be "cancel".
|
||||
#[xml(name = "internal-server-error")]
|
||||
InternalServerError,
|
||||
|
||||
/// The addressed JID or item requested cannot be found; the associated
|
||||
/// error type SHOULD be "cancel".
|
||||
ItemNotFound => "item-not-found",
|
||||
/// The addressed JID or item requested cannot be found; the associated
|
||||
/// error type SHOULD be "cancel".
|
||||
#[xml(name = "item-not-found")]
|
||||
ItemNotFound,
|
||||
|
||||
/// The sending entity has provided (e.g., during resource binding) or
|
||||
/// communicated (e.g., in the 'to' address of a stanza) an XMPP
|
||||
/// address or aspect thereof that violates the rules defined in
|
||||
/// [XMPP‑ADDR]; the associated error type SHOULD be "modify".
|
||||
JidMalformed => "jid-malformed",
|
||||
/// The sending entity has provided (e.g., during resource binding) or
|
||||
/// communicated (e.g., in the 'to' address of a stanza) an XMPP
|
||||
/// address or aspect thereof that violates the rules defined in
|
||||
/// [XMPP‑ADDR]; the associated error type SHOULD be "modify".
|
||||
#[xml(name = "jid-malformed")]
|
||||
JidMalformed,
|
||||
|
||||
/// The recipient or server understands the request but cannot process
|
||||
/// it because the request does not meet criteria defined by the
|
||||
/// recipient or server (e.g., a request to subscribe to information
|
||||
/// that does not simultaneously include configuration parameters
|
||||
/// needed by the recipient); the associated error type SHOULD be
|
||||
/// "modify".
|
||||
NotAcceptable => "not-acceptable",
|
||||
/// The recipient or server understands the request but cannot process
|
||||
/// it because the request does not meet criteria defined by the
|
||||
/// recipient or server (e.g., a request to subscribe to information
|
||||
/// that does not simultaneously include configuration parameters
|
||||
/// needed by the recipient); the associated error type SHOULD be
|
||||
/// "modify".
|
||||
#[xml(name = "not-acceptable")]
|
||||
NotAcceptable,
|
||||
|
||||
/// The recipient or server does not allow any entity to perform the
|
||||
/// action (e.g., sending to entities at a blacklisted domain); the
|
||||
/// associated error type SHOULD be "cancel".
|
||||
NotAllowed => "not-allowed",
|
||||
/// The recipient or server does not allow any entity to perform the
|
||||
/// action (e.g., sending to entities at a blacklisted domain); the
|
||||
/// associated error type SHOULD be "cancel".
|
||||
#[xml(name = "not-allowed")]
|
||||
NotAllowed,
|
||||
|
||||
/// The sender needs to provide credentials before being allowed to
|
||||
/// perform the action, or has provided improper credentials (the name
|
||||
/// "not-authorized", which was borrowed from the "401 Unauthorized"
|
||||
/// error of HTTP, might lead the reader to think that this condition
|
||||
/// relates to authorization, but instead it is typically used in
|
||||
/// relation to authentication); the associated error type SHOULD be
|
||||
/// "auth".
|
||||
NotAuthorized => "not-authorized",
|
||||
/// The sender needs to provide credentials before being allowed to
|
||||
/// perform the action, or has provided improper credentials (the name
|
||||
/// "not-authorized", which was borrowed from the "401 Unauthorized"
|
||||
/// error of HTTP, might lead the reader to think that this condition
|
||||
/// relates to authorization, but instead it is typically used in
|
||||
/// relation to authentication); the associated error type SHOULD be
|
||||
/// "auth".
|
||||
#[xml(name = "not-authorized")]
|
||||
NotAuthorized,
|
||||
|
||||
/// The entity has violated some local service policy (e.g., a message
|
||||
/// contains words that are prohibited by the service) and the server
|
||||
/// MAY choose to specify the policy in the \<text/\> element or in an
|
||||
/// application-specific condition element; the associated error type
|
||||
/// SHOULD be "modify" or "wait" depending on the policy being
|
||||
/// violated.
|
||||
PolicyViolation => "policy-violation",
|
||||
/// The entity has violated some local service policy (e.g., a message
|
||||
/// contains words that are prohibited by the service) and the server
|
||||
/// MAY choose to specify the policy in the \<text/\> element or in an
|
||||
/// application-specific condition element; the associated error type
|
||||
/// SHOULD be "modify" or "wait" depending on the policy being
|
||||
/// violated.
|
||||
#[xml(name = "policy-violation")]
|
||||
PolicyViolation,
|
||||
|
||||
/// The intended recipient is temporarily unavailable, undergoing
|
||||
/// maintenance, etc.; the associated error type SHOULD be "wait".
|
||||
RecipientUnavailable => "recipient-unavailable",
|
||||
/// The intended recipient is temporarily unavailable, undergoing
|
||||
/// maintenance, etc.; the associated error type SHOULD be "wait".
|
||||
#[xml(name = "recipient-unavailable")]
|
||||
RecipientUnavailable,
|
||||
|
||||
/// The recipient or server is redirecting requests for this
|
||||
/// information to another entity, typically in a temporary fashion (as
|
||||
/// opposed to the \<gone/\> error condition, which is used for permanent
|
||||
/// addressing failures); the associated error type SHOULD be "modify"
|
||||
/// and the error stanza SHOULD contain the alternate address in the
|
||||
/// XML character data of the \<redirect/\> element (which MUST be a URI
|
||||
/// or IRI with which the sender can communicate, typically an XMPP IRI
|
||||
/// as specified in [XMPP‑URI](https://xmpp.org/rfcs/rfc5122.html)).
|
||||
Redirect => "redirect",
|
||||
/// The recipient or server is redirecting requests for this
|
||||
/// information to another entity, typically in a temporary fashion (as
|
||||
/// opposed to the \<gone/\> error condition, which is used for permanent
|
||||
/// addressing failures); the associated error type SHOULD be "modify"
|
||||
/// and the error stanza SHOULD contain the alternate address in the
|
||||
/// XML character data of the \<redirect/\> element (which MUST be a URI
|
||||
/// or IRI with which the sender can communicate, typically an XMPP IRI
|
||||
/// as specified in [XMPP‑URI](https://xmpp.org/rfcs/rfc5122.html)).
|
||||
#[xml(name = "redirect")]
|
||||
Redirect {
|
||||
/// The new address of the entity for which the error was returned,
|
||||
/// if available.
|
||||
#[xml(text(codec = EmptyAsNone))]
|
||||
new_address: Option<String>,
|
||||
},
|
||||
|
||||
/// The requesting entity is not authorized to access the requested
|
||||
/// service because prior registration is necessary (examples of prior
|
||||
/// registration include members-only rooms in XMPP multi-user chat
|
||||
/// [XEP‑0045] and gateways to non-XMPP instant messaging services,
|
||||
/// which traditionally required registration in order to use the
|
||||
/// gateway [XEP‑0100]); the associated error type SHOULD be "auth".
|
||||
RegistrationRequired => "registration-required",
|
||||
/// The requesting entity is not authorized to access the requested
|
||||
/// service because prior registration is necessary (examples of prior
|
||||
/// registration include members-only rooms in XMPP multi-user chat
|
||||
/// [XEP‑0045] and gateways to non-XMPP instant messaging services,
|
||||
/// which traditionally required registration in order to use the
|
||||
/// gateway [XEP‑0100]); the associated error type SHOULD be "auth".
|
||||
#[xml(name = "registration-required")]
|
||||
RegistrationRequired,
|
||||
|
||||
/// A remote server or service specified as part or all of the JID of
|
||||
/// the intended recipient does not exist or cannot be resolved (e.g.,
|
||||
/// there is no _xmpp-server._tcp DNS SRV record, the A or AAAA
|
||||
/// fallback resolution fails, or A/AAAA lookups succeed but there is
|
||||
/// no response on the IANA-registered port 5269); the associated error
|
||||
/// type SHOULD be "cancel".
|
||||
RemoteServerNotFound => "remote-server-not-found",
|
||||
/// A remote server or service specified as part or all of the JID of
|
||||
/// the intended recipient does not exist or cannot be resolved (e.g.,
|
||||
/// there is no _xmpp-server._tcp DNS SRV record, the A or AAAA
|
||||
/// fallback resolution fails, or A/AAAA lookups succeed but there is
|
||||
/// no response on the IANA-registered port 5269); the associated error
|
||||
/// type SHOULD be "cancel".
|
||||
#[xml(name = "remote-server-not-found")]
|
||||
RemoteServerNotFound,
|
||||
|
||||
/// A remote server or service specified as part or all of the JID of
|
||||
/// the intended recipient (or needed to fulfill a request) was
|
||||
/// resolved but communications could not be established within a
|
||||
/// reasonable amount of time (e.g., an XML stream cannot be
|
||||
/// established at the resolved IP address and port, or an XML stream
|
||||
/// can be established but stream negotiation fails because of problems
|
||||
/// with TLS, SASL, Server Dialback, etc.); the associated error type
|
||||
/// SHOULD be "wait" (unless the error is of a more permanent nature,
|
||||
/// e.g., the remote server is found but it cannot be authenticated or
|
||||
/// it violates security policies).
|
||||
RemoteServerTimeout => "remote-server-timeout",
|
||||
/// A remote server or service specified as part or all of the JID of
|
||||
/// the intended recipient (or needed to fulfill a request) was
|
||||
/// resolved but communications could not be established within a
|
||||
/// reasonable amount of time (e.g., an XML stream cannot be
|
||||
/// established at the resolved IP address and port, or an XML stream
|
||||
/// can be established but stream negotiation fails because of problems
|
||||
/// with TLS, SASL, Server Dialback, etc.); the associated error type
|
||||
/// SHOULD be "wait" (unless the error is of a more permanent nature,
|
||||
/// e.g., the remote server is found but it cannot be authenticated or
|
||||
/// it violates security policies).
|
||||
#[xml(name = "remote-server-timeout")]
|
||||
RemoteServerTimeout,
|
||||
|
||||
/// The server or recipient is busy or lacks the system resources
|
||||
/// necessary to service the request; the associated error type SHOULD
|
||||
/// be "wait".
|
||||
ResourceConstraint => "resource-constraint",
|
||||
/// The server or recipient is busy or lacks the system resources
|
||||
/// necessary to service the request; the associated error type SHOULD
|
||||
/// be "wait".
|
||||
#[xml(name = "resource-constraint")]
|
||||
ResourceConstraint,
|
||||
|
||||
/// The server or recipient does not currently provide the requested
|
||||
/// service; the associated error type SHOULD be "cancel".
|
||||
ServiceUnavailable => "service-unavailable",
|
||||
/// The server or recipient does not currently provide the requested
|
||||
/// service; the associated error type SHOULD be "cancel".
|
||||
#[xml(name = "service-unavailable")]
|
||||
ServiceUnavailable,
|
||||
|
||||
/// The requesting entity is not authorized to access the requested
|
||||
/// service because a prior subscription is necessary (examples of
|
||||
/// prior subscription include authorization to receive presence
|
||||
/// information as defined in [XMPP‑IM] and opt-in data feeds for XMPP
|
||||
/// publish-subscribe as defined in [XEP‑0060]); the associated error
|
||||
/// type SHOULD be "auth".
|
||||
SubscriptionRequired => "subscription-required",
|
||||
/// The requesting entity is not authorized to access the requested
|
||||
/// service because a prior subscription is necessary (examples of
|
||||
/// prior subscription include authorization to receive presence
|
||||
/// information as defined in [XMPP‑IM] and opt-in data feeds for XMPP
|
||||
/// publish-subscribe as defined in [XEP‑0060]); the associated error
|
||||
/// type SHOULD be "auth".
|
||||
#[xml(name = "subscription-required")]
|
||||
SubscriptionRequired,
|
||||
|
||||
/// The error condition is not one of those defined by the other
|
||||
/// conditions in this list; any error type can be associated with this
|
||||
/// condition, and it SHOULD NOT be used except in conjunction with an
|
||||
/// application-specific condition.
|
||||
UndefinedCondition => "undefined-condition",
|
||||
/// The error condition is not one of those defined by the other
|
||||
/// conditions in this list; any error type can be associated with this
|
||||
/// condition, and it SHOULD NOT be used except in conjunction with an
|
||||
/// application-specific condition.
|
||||
#[xml(name = "undefined-condition")]
|
||||
UndefinedCondition,
|
||||
|
||||
/// The recipient or server understood the request but was not
|
||||
/// expecting it at this time (e.g., the request was out of order); the
|
||||
/// associated error type SHOULD be "wait" or "modify".
|
||||
UnexpectedRequest => "unexpected-request",
|
||||
}
|
||||
);
|
||||
/// The recipient or server understood the request but was not
|
||||
/// expecting it at this time (e.g., the request was out of order); the
|
||||
/// associated error type SHOULD be "wait" or "modify".
|
||||
#[xml(name = "unexpected-request")]
|
||||
UnexpectedRequest,
|
||||
}
|
||||
|
||||
type Lang = String;
|
||||
|
||||
|
|
@ -211,11 +244,6 @@ pub struct StanzaError {
|
|||
|
||||
/// A protocol-specific extension for this error.
|
||||
pub other: Option<Element>,
|
||||
|
||||
/// May include an alternate address if `defined_condition` is `Gone` or `Redirect`. It is
|
||||
/// a Uniform Resource Identifier [URI] or Internationalized Resource Identifier [IRI] at
|
||||
/// which the entity can be contacted, typically an XMPP IRI as specified in [XMPP‑URI]
|
||||
pub alternate_address: Option<String>,
|
||||
}
|
||||
|
||||
impl MessagePayload for StanzaError {}
|
||||
|
|
@ -243,7 +271,6 @@ impl StanzaError {
|
|||
map
|
||||
},
|
||||
other: None,
|
||||
alternate_address: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -263,7 +290,6 @@ impl TryFrom<Element> for StanzaError {
|
|||
defined_condition: DefinedCondition::UndefinedCondition,
|
||||
texts: BTreeMap::new(),
|
||||
other: None,
|
||||
alternate_address: None,
|
||||
};
|
||||
let mut defined_condition = None;
|
||||
|
||||
|
|
@ -286,16 +312,7 @@ impl TryFrom<Element> for StanzaError {
|
|||
}
|
||||
check_no_children!(child, "defined-condition");
|
||||
check_no_attributes!(child, "defined-condition");
|
||||
let condition = DefinedCondition::try_from(child.clone())?;
|
||||
|
||||
if condition == DefinedCondition::Gone || condition == DefinedCondition::Redirect {
|
||||
stanza_error.alternate_address = child.nodes().find_map(|node| {
|
||||
let Node::Text(text) = node else { return None };
|
||||
Some(text.to_string())
|
||||
});
|
||||
}
|
||||
|
||||
defined_condition = Some(condition);
|
||||
defined_condition = Some(DefinedCondition::try_from(child.clone())?);
|
||||
} else {
|
||||
if stanza_error.other.is_some() {
|
||||
return Err(
|
||||
|
|
@ -336,16 +353,16 @@ mod tests {
|
|||
#[test]
|
||||
fn test_size() {
|
||||
assert_size!(ErrorType, 1);
|
||||
assert_size!(DefinedCondition, 1);
|
||||
assert_size!(StanzaError, 104);
|
||||
assert_size!(DefinedCondition, 16);
|
||||
assert_size!(StanzaError, 108);
|
||||
}
|
||||
|
||||
#[cfg(target_pointer_width = "64")]
|
||||
#[test]
|
||||
fn test_size() {
|
||||
assert_size!(ErrorType, 1);
|
||||
assert_size!(DefinedCondition, 1);
|
||||
assert_size!(StanzaError, 208);
|
||||
assert_size!(DefinedCondition, 32);
|
||||
assert_size!(StanzaError, 216);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
@ -446,10 +463,11 @@ mod tests {
|
|||
.unwrap();
|
||||
let error = StanzaError::try_from(elem).unwrap();
|
||||
assert_eq!(error.type_, ErrorType::Cancel);
|
||||
assert_eq!(error.defined_condition, DefinedCondition::Gone);
|
||||
assert_eq!(
|
||||
error.alternate_address,
|
||||
Some("xmpp:room@muc.example.org?join".to_string())
|
||||
error.defined_condition,
|
||||
DefinedCondition::Gone {
|
||||
new_address: Some("xmpp:room@muc.example.org?join".to_string()),
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -465,8 +483,10 @@ mod tests {
|
|||
.unwrap();
|
||||
let error = StanzaError::try_from(elem).unwrap();
|
||||
assert_eq!(error.type_, ErrorType::Cancel);
|
||||
assert_eq!(error.defined_condition, DefinedCondition::Gone);
|
||||
assert_eq!(error.alternate_address, None);
|
||||
assert_eq!(
|
||||
error.defined_condition,
|
||||
DefinedCondition::Gone { new_address: None }
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
@ -481,10 +501,11 @@ mod tests {
|
|||
.unwrap();
|
||||
let error = StanzaError::try_from(elem).unwrap();
|
||||
assert_eq!(error.type_, ErrorType::Modify);
|
||||
assert_eq!(error.defined_condition, DefinedCondition::Redirect);
|
||||
assert_eq!(
|
||||
error.alternate_address,
|
||||
Some("xmpp:characters@conference.example.org".to_string())
|
||||
error.defined_condition,
|
||||
DefinedCondition::Redirect {
|
||||
new_address: Some("xmpp:characters@conference.example.org".to_string()),
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -500,7 +521,9 @@ mod tests {
|
|||
.unwrap();
|
||||
let error = StanzaError::try_from(elem).unwrap();
|
||||
assert_eq!(error.type_, ErrorType::Modify);
|
||||
assert_eq!(error.defined_condition, DefinedCondition::Redirect);
|
||||
assert_eq!(error.alternate_address, None);
|
||||
assert_eq!(
|
||||
error.defined_condition,
|
||||
DefinedCondition::Redirect { new_address: None }
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue