use crate::types::events::ConnectFailureReason;
use std::borrow::Cow;
use wacore_binary::NodeRef;
#[derive(Debug, Clone, Default)]
pub struct ConnectFailureStanza<'a> {
pub reason: Option<ConnectFailureReason>,
pub location: Option<Cow<'a, str>>,
pub code: Option<i32>,
pub expire: Option<u64>,
pub message: Option<Cow<'a, str>>,
pub url: Option<Cow<'a, str>>,
pub logout_message_header: Option<Cow<'a, str>>,
pub logout_message_subtext: Option<Cow<'a, str>>,
pub logout_message_locale: Option<Cow<'a, str>>,
}
impl<'a> ConnectFailureStanza<'a> {
pub fn parse(node: &'a NodeRef<'_>) -> Self {
let mut attrs = node.attrs();
Self {
reason: attrs
.optional_u64("reason")
.and_then(|r| i32::try_from(r).ok())
.map(ConnectFailureReason::from),
location: attrs.optional_string("location"),
code: attrs
.optional_u64("code")
.and_then(|c| i32::try_from(c).ok()),
expire: attrs.optional_u64("expire"),
message: attrs.optional_string("message"),
url: attrs.optional_string("url"),
logout_message_header: attrs.optional_string("logout_message_header"),
logout_message_subtext: attrs.optional_string("logout_message_subtext"),
logout_message_locale: attrs.optional_string("logout_message_locale"),
}
}
pub fn logout_message(&self) -> Option<crate::types::events::LogoutMessage> {
if self.logout_message_header.is_none() && self.logout_message_subtext.is_none() {
return None;
}
Some(
crate::types::events::LogoutMessage::builder()
.maybe_header(self.logout_message_header.as_deref().map(str::to_owned))
.maybe_subtext(self.logout_message_subtext.as_deref().map(str::to_owned))
.maybe_locale(self.logout_message_locale.as_deref().map(str::to_owned))
.build(),
)
}
}
#[cfg(test)]
mod tests {
use super::*;
use wacore_binary::builder::NodeBuilder;
#[test]
fn parses_every_attribute_wa_web_reads() {
let node = NodeBuilder::new("failure")
.attr("reason", "402")
.attr("location", "frc")
.attr("code", "101")
.attr("expire", "21600")
.attr("message", "banned")
.attr("url", "https://example.invalid/appeal")
.attr("logout_message_header", "Conta desconectada")
.attr("logout_message_subtext", "Verifique o app no celular")
.attr("logout_message_locale", "pt_BR")
.build();
let node_ref = node.as_node_ref();
let parsed = ConnectFailureStanza::parse(&node_ref);
assert_eq!(parsed.reason, Some(ConnectFailureReason::TempBanned));
assert_eq!(parsed.location.as_deref(), Some("frc"));
assert_eq!(parsed.code, Some(101));
assert_eq!(parsed.expire, Some(21_600));
assert_eq!(parsed.message.as_deref(), Some("banned"));
assert_eq!(
parsed.url.as_deref(),
Some("https://example.invalid/appeal")
);
let msg = parsed.logout_message().expect("header/subtext present");
assert_eq!(msg.header.as_deref(), Some("Conta desconectada"));
assert_eq!(msg.subtext.as_deref(), Some("Verifique o app no celular"));
assert_eq!(msg.locale.as_deref(), Some("pt_BR"));
}
#[test]
fn absent_attributes_stay_absent() {
let node = NodeBuilder::new("failure").attr("reason", "401").build();
let node_ref = node.as_node_ref();
let parsed = ConnectFailureStanza::parse(&node_ref);
assert_eq!(parsed.reason, Some(ConnectFailureReason::LoggedOut));
assert_eq!(parsed.code, None);
assert_eq!(parsed.expire, None);
assert!(parsed.logout_message().is_none());
}
#[test]
fn missing_reason_is_none_not_zero() {
let node = NodeBuilder::new("failure").attr("location", "rva").build();
let node_ref = node.as_node_ref();
assert_eq!(ConnectFailureStanza::parse(&node_ref).reason, None);
}
#[test]
fn out_of_range_reason_is_preserved_as_unknown() {
let node = NodeBuilder::new("failure").attr("reason", "600").build();
let node_ref = node.as_node_ref();
assert_eq!(
ConnectFailureStanza::parse(&node_ref).reason,
Some(ConnectFailureReason::Unknown(600))
);
}
#[test]
fn locale_alone_is_not_a_logout_message() {
let node = NodeBuilder::new("failure")
.attr("reason", "403")
.attr("logout_message_locale", "en")
.build();
let node_ref = node.as_node_ref();
assert!(
ConnectFailureStanza::parse(&node_ref)
.logout_message()
.is_none()
);
}
}