use xso::{AsXml, FromXml};
use crate::data_forms::DataForm;
use crate::date::DateTime;
use crate::iq::{IqGetPayload, IqResultPayload, IqSetPayload};
use crate::ns;
generate_attribute!(
Action, "action", {
Add => "add",
Remove => "remove",
Modify => "modify",
}, Default = Add
);
generate_attribute!(
Transport, "transport", {
Tcp => "tcp",
Udp => "udp",
}
);
generate_attribute!(
Type, "type", {
Stun => "stun",
Turn => "turn",
}
);
#[derive(FromXml, AsXml, Debug, PartialEq, Clone)]
#[xml(namespace = ns::EXT_DISCO, name = "service")]
pub struct Service {
#[xml(attribute(default))]
action: Action,
#[xml(attribute(default))]
expires: Option<DateTime>,
#[xml(attribute)]
host: String,
#[xml(attribute(default))]
name: Option<String>,
#[xml(attribute(default))]
password: Option<String>,
#[xml(attribute(default))]
port: Option<u16>,
#[xml(attribute(default))]
restricted: bool,
#[xml(attribute(default))]
transport: Option<Transport>,
#[xml(attribute = "type")]
type_: Type,
#[xml(attribute(default))]
username: Option<String>,
#[xml(child(n = ..))]
ext_info: Vec<DataForm>,
}
impl IqGetPayload for Service {}
#[derive(FromXml, AsXml, PartialEq, Debug, Clone)]
#[xml(namespace = ns::EXT_DISCO, name = "services")]
pub struct ServicesQuery {
#[xml(attribute(default, name = "type"))]
pub type_: Option<Type>,
}
impl IqGetPayload for ServicesQuery {}
#[derive(FromXml, AsXml, PartialEq, Debug, Clone)]
#[xml(namespace = ns::EXT_DISCO, name = "services")]
pub struct ServicesResult {
#[xml(attribute(name = "type", default))]
pub type_: Option<Type>,
#[xml(child(n = ..))]
pub services: Vec<Service>,
}
impl IqResultPayload for ServicesResult {}
impl IqSetPayload for ServicesResult {}
#[derive(FromXml, AsXml, PartialEq, Debug, Clone)]
#[xml(namespace = ns::EXT_DISCO, name = "credentials")]
pub struct Credentials {
#[xml(child(n = ..))]
pub services: Vec<Service>,
}
impl IqGetPayload for Credentials {}
impl IqResultPayload for Credentials {}
#[cfg(test)]
mod tests {
use super::*;
use crate::ns;
use minidom::Element;
#[cfg(target_pointer_width = "32")]
#[test]
fn test_size() {
assert_size!(Action, 1);
assert_size!(Transport, 1);
assert_size!(Type, 1);
assert_size!(Service, 84);
assert_size!(ServicesQuery, 1);
assert_size!(ServicesResult, 16);
assert_size!(Credentials, 12);
}
#[cfg(target_pointer_width = "64")]
#[test]
fn test_size() {
assert_size!(Action, 1);
assert_size!(Transport, 1);
assert_size!(Type, 1);
assert_size!(Service, 144);
assert_size!(ServicesQuery, 1);
assert_size!(ServicesResult, 32);
assert_size!(Credentials, 24);
}
#[test]
fn test_simple() {
let elem: Element = "<service xmlns='urn:xmpp:extdisco:2' host='stun.shakespeare.lit' port='9998' transport='udp' type='stun'/>".parse().unwrap();
let service = Service::try_from(elem).unwrap();
assert_eq!(service.action, Action::Add);
assert!(service.expires.is_none());
assert_eq!(service.host, "stun.shakespeare.lit");
assert!(service.name.is_none());
assert!(service.password.is_none());
assert_eq!(service.port.unwrap(), 9998);
assert_eq!(service.restricted, false);
assert_eq!(service.transport.unwrap(), Transport::Udp);
assert_eq!(service.type_, Type::Stun);
assert!(service.username.is_none());
assert!(service.ext_info.is_empty());
}
#[test]
fn test_service_query() {
let query = ServicesQuery { type_: None };
let elem = Element::from(query);
assert!(elem.is("services", ns::EXT_DISCO));
assert_eq!(elem.attrs().into_iter().next(), None);
assert_eq!(elem.nodes().next(), None);
}
#[test]
fn test_service_result() {
let elem: Element = "<services xmlns='urn:xmpp:extdisco:2' type='stun'><service host='stun.shakespeare.lit' port='9998' transport='udp' type='stun'/></services>".parse().unwrap();
let services = ServicesResult::try_from(elem).unwrap();
assert_eq!(services.type_, Some(Type::Stun));
assert_eq!(services.services.len(), 1);
}
}