use std::str::FromStr;
use chrono::{DateTime, Utc};
use jid::JID;
use peanuts::{
element::{FromElement, IntoElement},
DeserializeError, Element,
};
use crate::xep_0004::X;
#[cfg(feature = "xep_0172")]
use crate::xep_0172::{self, Nick};
pub const XMLNS: &str = "http://jabber.org/protocol/pubsub#event";
#[derive(Clone, Debug)]
pub enum Event {
Collection(Collection),
Configuration(Configuration),
Delete(Delete),
Items(Items),
Purge(Purge),
Subscription(Subscription),
}
impl FromElement for Event {
fn from_element(mut element: peanuts::Element) -> peanuts::element::DeserializeResult<Self> {
element.check_name("event")?;
element.check_namespace(XMLNS)?;
let child = element.pop_child_one::<Element>()?;
match child.identify() {
(Some(XMLNS), "collection") => Ok(Self::Collection(Collection::from_element(child)?)),
(Some(XMLNS), "configuration") => {
Ok(Self::Configuration(Configuration::from_element(child)?))
}
(Some(XMLNS), "delete") => Ok(Self::Delete(Delete::from_element(child)?)),
(Some(XMLNS), "items") => Ok(Self::Items(Items::from_element(child)?)),
(Some(XMLNS), "purge") => Ok(Self::Purge(Purge::from_element(child)?)),
(Some(XMLNS), "subscription") => {
Ok(Self::Subscription(Subscription::from_element(child)?))
}
_ => Err(peanuts::DeserializeError::UnexpectedElement(child)),
}
}
}
impl IntoElement for Event {
fn builder(&self) -> peanuts::element::ElementBuilder {
let builder = Element::builder("event", Some(XMLNS));
match self {
Event::Collection(collection) => builder.push_child(collection.clone()),
Event::Configuration(configuration) => builder.push_child(configuration.clone()),
Event::Delete(delete) => builder.push_child(delete.clone()),
Event::Items(items) => builder.push_child(items.clone()),
Event::Purge(purge) => builder.push_child(purge.clone()),
Event::Subscription(subscription) => builder.push_child(subscription.clone()),
}
}
}
#[derive(Clone, Debug)]
pub struct Collection {
node: Option<String>,
r#type: CollectionType,
}
impl FromElement for Collection {
fn from_element(mut element: Element) -> peanuts::element::DeserializeResult<Self> {
element.check_name("node")?;
element.check_namespace(XMLNS)?;
let node = element.attribute_opt("node")?;
let r#type = element.pop_child_one()?;
Ok(Self { node, r#type })
}
}
impl IntoElement for Collection {
fn builder(&self) -> peanuts::element::ElementBuilder {
Element::builder("collection", Some(XMLNS))
.push_attribute_opt("node", self.node.clone())
.push_child(self.r#type.clone())
}
}
#[derive(Clone, Debug)]
pub enum CollectionType {
Associate(Associate),
Disassociate(Disassociate),
}
impl FromElement for CollectionType {
fn from_element(element: Element) -> peanuts::element::DeserializeResult<Self> {
match element.identify() {
(Some(XMLNS), "associate") => {
Ok(CollectionType::Associate(Associate::from_element(element)?))
}
(Some(XMLNS), "disassociate") => Ok(CollectionType::Disassociate(
Disassociate::from_element(element)?,
)),
_ => Err(DeserializeError::UnexpectedElement(element)),
}
}
}
impl IntoElement for CollectionType {
fn builder(&self) -> peanuts::element::ElementBuilder {
match self {
CollectionType::Associate(associate) => associate.builder(),
CollectionType::Disassociate(disassociate) => disassociate.builder(),
}
}
}
#[derive(Clone, Debug)]
pub struct Associate {
node: String,
}
impl FromElement for Associate {
fn from_element(mut element: Element) -> peanuts::element::DeserializeResult<Self> {
element.check_name("associate")?;
element.check_namespace(XMLNS)?;
let node = element.attribute("node")?;
Ok(Self { node })
}
}
impl IntoElement for Associate {
fn builder(&self) -> peanuts::element::ElementBuilder {
Element::builder("associate", Some(XMLNS)).push_attribute("node", self.node.clone())
}
}
#[derive(Clone, Debug)]
pub struct Disassociate {
node: String,
}
impl FromElement for Disassociate {
fn from_element(mut element: Element) -> peanuts::element::DeserializeResult<Self> {
element.check_name("disassociate")?;
element.check_namespace(XMLNS)?;
let node = element.attribute("node")?;
Ok(Self { node })
}
}
impl IntoElement for Disassociate {
fn builder(&self) -> peanuts::element::ElementBuilder {
Element::builder("disassociate", Some(XMLNS)).push_attribute("node", self.node.clone())
}
}
#[derive(Clone, Debug)]
pub struct Configuration {
node: Option<String>,
configuration: Option<X>,
}
impl FromElement for Configuration {
fn from_element(mut element: Element) -> peanuts::element::DeserializeResult<Self> {
element.check_name("configuration")?;
element.check_namespace(XMLNS)?;
let node = element.attribute_opt("node")?;
let configuration = element.pop_child_opt()?;
Ok(Self {
node,
configuration,
})
}
}
impl IntoElement for Configuration {
fn builder(&self) -> peanuts::element::ElementBuilder {
Element::builder("configuration", Some(XMLNS))
.push_attribute_opt("node", self.node.clone())
.push_child_opt(self.configuration.clone())
}
}
#[derive(Clone, Debug)]
pub struct Delete {
node: String,
redirect: Option<Redirect>,
}
impl FromElement for Delete {
fn from_element(mut element: Element) -> peanuts::element::DeserializeResult<Self> {
element.check_name("delete")?;
element.check_namespace(XMLNS)?;
let node = element.attribute("node")?;
let redirect = element.pop_child_opt()?;
Ok(Self { node, redirect })
}
}
impl IntoElement for Delete {
fn builder(&self) -> peanuts::element::ElementBuilder {
Element::builder("delete", Some(XMLNS))
.push_attribute("node", self.node.clone())
.push_child_opt(self.redirect.clone())
}
}
#[derive(Clone, Debug)]
pub struct Items {
pub node: String,
pub items: ItemsType,
}
impl FromElement for Items {
fn from_element(mut element: Element) -> peanuts::element::DeserializeResult<Self> {
element.check_name("items")?;
element.check_namespace(XMLNS)?;
let node = element.attribute("node")?;
let items = if let Ok(items) = element.pop_children() {
ItemsType::Item(items)
} else {
ItemsType::Retract(element.pop_children()?)
};
Ok(Self { node, items })
}
}
impl IntoElement for Items {
fn builder(&self) -> peanuts::element::ElementBuilder {
let builder =
Element::builder("items", Some(XMLNS)).push_attribute("node", self.node.clone());
match &self.items {
ItemsType::Item(items) => builder.push_children(items.clone()),
ItemsType::Retract(retracts) => builder.push_children(retracts.clone()),
}
}
}
#[derive(Clone, Debug)]
pub enum ItemsType {
Item(Vec<Item>),
Retract(Vec<Retract>),
}
#[derive(Clone, Debug)]
pub struct Item {
pub id: Option<String>,
pub publisher: Option<String>,
pub item: Option<Content>,
}
impl FromElement for Item {
fn from_element(mut element: Element) -> peanuts::element::DeserializeResult<Self> {
element.check_name("item")?;
element.check_namespace(XMLNS)?;
let id = element.attribute_opt("id")?;
let publisher = element.attribute_opt("publisher")?;
let item = element.pop_child_opt()?;
Ok(Self {
id,
publisher,
item,
})
}
}
impl IntoElement for Item {
fn builder(&self) -> peanuts::element::ElementBuilder {
Element::builder("item", Some(XMLNS))
.push_attribute_opt("id", self.id.clone())
.push_attribute_opt("publisher", self.publisher.clone())
.push_child_opt(self.item.clone())
}
}
#[derive(Clone, Debug)]
pub enum Content {
#[cfg(feature = "xep_0172")]
Nick(Nick),
Unknown(Element),
}
impl FromElement for Content {
fn from_element(element: Element) -> peanuts::element::DeserializeResult<Self> {
match element.identify() {
#[cfg(feature = "xep_0172")]
(Some(xep_0172::XMLNS), "nick") => Ok(Content::Nick(Nick::from_element(element)?)),
_ => Ok(Self::Unknown(element)),
}
}
}
impl IntoElement for Content {
fn builder(&self) -> peanuts::element::ElementBuilder {
match self {
#[cfg(feature = "xep_0172")]
Content::Nick(nick) => nick.builder(),
Content::Unknown(_e) => panic!("unknown content cannot be serialized"),
}
}
}
#[derive(Clone, Debug)]
pub struct Purge {
node: String,
}
impl FromElement for Purge {
fn from_element(mut element: Element) -> peanuts::element::DeserializeResult<Self> {
element.check_name("purge")?;
element.check_namespace(XMLNS)?;
let node = element.attribute("node")?;
Ok(Self { node })
}
}
impl IntoElement for Purge {
fn builder(&self) -> peanuts::element::ElementBuilder {
Element::builder("purge", Some(XMLNS)).push_attribute("node", self.node.clone())
}
}
#[derive(Clone, Debug)]
pub struct Redirect {
uri: String,
}
impl FromElement for Redirect {
fn from_element(mut element: Element) -> peanuts::element::DeserializeResult<Self> {
element.check_name("redirect")?;
element.check_namespace(XMLNS)?;
let uri = element.attribute("uri")?;
Ok(Self { uri })
}
}
impl IntoElement for Redirect {
fn builder(&self) -> peanuts::element::ElementBuilder {
Element::builder("redirect", Some(XMLNS)).push_attribute("uri", self.uri.clone())
}
}
#[derive(Clone, Debug)]
pub struct Retract {
id: String,
}
impl FromElement for Retract {
fn from_element(mut element: Element) -> peanuts::element::DeserializeResult<Self> {
element.check_name("retract")?;
element.check_namespace(XMLNS)?;
let id = element.attribute("id")?;
Ok(Self { id })
}
}
impl IntoElement for Retract {
fn builder(&self) -> peanuts::element::ElementBuilder {
Element::builder("retract", Some(XMLNS)).push_attribute("id", self.id.clone())
}
}
#[derive(Clone, Debug)]
pub struct Subscription {
expiry: Option<DateTime<Utc>>,
jid: JID,
node: Option<String>,
subid: Option<String>,
subscription: Option<SubscriptionState>,
}
impl FromElement for Subscription {
fn from_element(mut element: Element) -> peanuts::element::DeserializeResult<Self> {
element.check_name("subscription")?;
element.check_namespace(XMLNS)?;
let expiry = element.attribute_opt("expiry")?;
let jid = element.attribute("jid")?;
let node = element.attribute_opt("node")?;
let subid = element.attribute_opt("subid")?;
let subscription = element.attribute_opt("subscription")?;
Ok(Self {
expiry,
jid,
node,
subid,
subscription,
})
}
}
impl IntoElement for Subscription {
fn builder(&self) -> peanuts::element::ElementBuilder {
Element::builder("subscription", Some(XMLNS))
.push_attribute_opt("expiry", self.expiry)
.push_attribute("jid", self.jid.clone())
.push_attribute_opt("node", self.node.clone())
.push_attribute_opt("subid", self.subid.clone())
.push_attribute_opt("subscription", self.subscription.clone())
}
}
#[derive(Clone, Debug)]
pub enum SubscriptionState {
None,
Pending,
Subscribed,
Unconfigured,
}
impl FromStr for SubscriptionState {
type Err = DeserializeError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"none" => Ok(Self::None),
"pending" => Ok(Self::Pending),
"subscribed" => Ok(Self::Subscribed),
"unconfigured" => Ok(Self::Unconfigured),
s => Err(DeserializeError::FromStr(s.to_owned())),
}
}
}
impl ToString for SubscriptionState {
fn to_string(&self) -> String {
match self {
SubscriptionState::None => "none",
SubscriptionState::Pending => "pending",
SubscriptionState::Subscribed => "subscribed",
SubscriptionState::Unconfigured => "unconfigured",
}
.to_owned()
}
}