use std::str::FromStr;
use jid::JID;
use peanuts::{
element::{FromElement, IntoElement},
DeserializeError, Element,
};
use crate::xep_0004::X;
pub const XMLNS: &str = "http://jabber.org/protocol/pubsub";
#[derive(Clone, Debug)]
pub enum Pubsub {
Create(Create, Option<Configure>),
Subscribe(Option<Subscribe>, Option<Options>),
Publish(Publish, Option<PublishOptions>),
Affiliations(Affiliations),
Default(Default),
Items(Items),
Retract(Retract),
Subscription(Subscription),
Subscriptions(Subscriptions),
Unsubscribe(Unsubscribe),
Pubsub,
}
impl FromElement for Pubsub {
fn from_element(mut element: peanuts::Element) -> peanuts::element::DeserializeResult<Self> {
element.check_name("pubsub")?;
element.check_namespace(XMLNS)?;
let first_child = element.pop_child_opt::<Element>()?;
if let Some(first_child) = first_child {
match first_child.identify() {
(Some(XMLNS), "create") => {
let create = Create::from_element(first_child)?;
let configure = element.pop_child_opt()?;
Ok(Self::Create(create, configure))
}
(Some(XMLNS), "subscribe") => {
let subscribe = Subscribe::from_element(first_child)?;
let options = element.pop_child_opt()?;
Ok(Self::Subscribe(Some(subscribe), options))
}
(Some(XMLNS), "options") => {
let options = Options::from_element(first_child)?;
Ok(Self::Subscribe(None, Some(options)))
}
(Some(XMLNS), "publish") => {
let publish = Publish::from_element(first_child)?;
let publish_options = element.pop_child_opt()?;
Ok(Self::Publish(publish, publish_options))
}
(Some(XMLNS), "affiliations") => {
Ok(Self::Affiliations(Affiliations::from_element(first_child)?))
}
(Some(XMLNS), "default") => Ok(Self::Default(Default::from_element(first_child)?)),
(Some(XMLNS), "items") => Ok(Self::Items(Items::from_element(first_child)?)),
(Some(XMLNS), "retract") => Ok(Self::Retract(Retract::from_element(first_child)?)),
(Some(XMLNS), "subscription") => {
Ok(Self::Subscription(Subscription::from_element(first_child)?))
}
(Some(XMLNS), "subscriptions") => Ok(Self::Subscriptions(
Subscriptions::from_element(first_child)?,
)),
(Some(XMLNS), "unsubscribe") => {
Ok(Self::Unsubscribe(Unsubscribe::from_element(first_child)?))
}
_ => Err(peanuts::DeserializeError::UnexpectedElement(first_child)),
}
} else {
Ok(Pubsub::Pubsub)
}
}
}
impl IntoElement for Pubsub {
fn builder(&self) -> peanuts::element::ElementBuilder {
let element = Element::builder("pubsub", Some(XMLNS));
match self {
Pubsub::Create(create, configure) => element
.push_child(create.clone())
.push_child_opt(configure.clone()),
Pubsub::Subscribe(subscribe, options) => element
.push_child_opt(subscribe.clone())
.push_child_opt(options.clone()),
Pubsub::Publish(publish, publish_options) => element
.push_child(publish.clone())
.push_child_opt(publish_options.clone()),
Pubsub::Affiliations(affiliations) => element.push_child(affiliations.clone()),
Pubsub::Default(default) => element.push_child(default.clone()),
Pubsub::Items(items) => element.push_child(items.clone()),
Pubsub::Retract(retract) => element.push_child(retract.clone()),
Pubsub::Subscription(subscription) => element.push_child(subscription.clone()),
Pubsub::Subscriptions(subscriptions) => element.push_child(subscriptions.clone()),
Pubsub::Unsubscribe(unsubscribe) => element.push_child(unsubscribe.clone()),
Pubsub::Pubsub => element,
}
}
}
#[derive(Clone, Debug)]
pub struct Affiliations {
node: Option<String>,
affiliations: Vec<Affiliation>,
}
impl FromElement for Affiliations {
fn from_element(mut element: Element) -> peanuts::element::DeserializeResult<Self> {
element.check_name("affiliations")?;
element.check_namespace(XMLNS)?;
let node = element.attribute_opt("node")?;
let affiliations = element.pop_children()?;
Ok(Self { node, affiliations })
}
}
impl IntoElement for Affiliations {
fn builder(&self) -> peanuts::element::ElementBuilder {
Element::builder("affilations", Some(XMLNS))
.push_attribute_opt("node", self.node.clone())
.push_children(self.affiliations.clone())
}
}
// empty
#[derive(Clone, Debug)]
pub struct Affiliation {
affiliation: AffiliationType,
node: String,
}
impl FromElement for Affiliation {
fn from_element(mut element: Element) -> peanuts::element::DeserializeResult<Self> {
element.check_name("affiliation")?;
element.check_namespace(XMLNS)?;
let affiliation = element.attribute("affiliation")?;
let node = element.attribute("node")?;
Ok(Self { affiliation, node })
}
}
impl IntoElement for Affiliation {
fn builder(&self) -> peanuts::element::ElementBuilder {
Element::builder("affiliation", Some(XMLNS))
.push_attribute("affiliation", self.affiliation.clone())
.push_attribute("node", self.node.clone())
}
}
#[derive(Clone, Debug)]
pub enum AffiliationType {
Member,
None,
Outcast,
Owner,
Publisher,
PublishOnly,
}
impl FromStr for AffiliationType {
type Err = DeserializeError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"member" => Ok(AffiliationType::Member),
"none" => Ok(AffiliationType::None),
"outcast" => Ok(AffiliationType::Outcast),
"owner" => Ok(AffiliationType::Owner),
"publisher" => Ok(AffiliationType::Publisher),
"publish-only" => Ok(AffiliationType::PublishOnly),
s => Err(DeserializeError::FromStr(s.to_string())),
}
}
}
impl ToString for AffiliationType {
fn to_string(&self) -> String {
match self {
AffiliationType::Member => "member",
AffiliationType::None => "none",
AffiliationType::Outcast => "outcast",
AffiliationType::Owner => "owner",
AffiliationType::Publisher => "publisher",
AffiliationType::PublishOnly => "publish-only",
}
.to_owned()
}
}
#[derive(Clone, Debug)]
pub struct Configure(Option<X>);
impl FromElement for Configure {
fn from_element(mut element: Element) -> peanuts::element::DeserializeResult<Self> {
element.check_name("configure")?;
element.check_namespace(XMLNS)?;
Ok(Configure(element.pop_child_opt()?))
}
}
impl IntoElement for Configure {
fn builder(&self) -> peanuts::element::ElementBuilder {
Element::builder("configure", Some(XMLNS)).push_child_opt(self.0.clone())
}
}
#[derive(Clone, Debug)]
pub struct Create {
node: Option<String>,
}
impl FromElement for Create {
fn from_element(mut element: Element) -> peanuts::element::DeserializeResult<Self> {
element.check_name("create")?;
element.check_namespace(XMLNS)?;
let node = element.attribute_opt("node")?;
Ok(Self { node })
}
}
impl IntoElement for Create {
fn builder(&self) -> peanuts::element::ElementBuilder {
Element::builder("create", Some(XMLNS)).push_attribute_opt("node", self.node.clone())
}
}
#[derive(Clone, Debug)]
pub struct Default {
node: Option<String>,
// default leaf
r#type: Option<DefaultType>,
default: Option<X>,
}
impl FromElement for Default {
fn from_element(mut element: Element) -> peanuts::element::DeserializeResult<Self> {
element.check_name("default")?;
element.check_namespace(XMLNS)?;
let node = element.attribute_opt("node")?;
let r#type = element.attribute_opt("type")?;
let default = element.pop_child_opt()?;
Ok(Self {
node,
r#type,
default,
})
}
}
impl IntoElement for Default {
fn builder(&self) -> peanuts::element::ElementBuilder {
Element::builder("default", Some(XMLNS))
.push_attribute_opt("node", self.node.clone())
.push_attribute_opt("type", self.r#type.clone())
.push_child_opt(self.default.clone())
}
}
#[derive(Clone, Debug)]
pub enum DefaultType {
Collection,
Leaf,
}
impl FromStr for DefaultType {
type Err = DeserializeError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"collection" => Ok(Self::Collection),
"leaf" => Ok(Self::Leaf),
s => Err(DeserializeError::FromStr(s.to_string())),
}
}
}
impl ToString for DefaultType {
fn to_string(&self) -> String {
match self {
DefaultType::Collection => "collection",
DefaultType::Leaf => "leaf",
}
.to_owned()
}
}
#[derive(Clone, Debug)]
pub struct Items {
max_items: Option<usize>,
node: String,
subid: Option<String>,
items: Vec<Item>,
}
impl FromElement for Items {
fn from_element(mut element: Element) -> peanuts::element::DeserializeResult<Self> {
element.check_name("items")?;
element.check_namespace(XMLNS)?;
let max_items = element.attribute_opt("max_items")?;
let node = element.attribute("node")?;
let subid = element.attribute_opt("subid")?;
let items = element.pop_children()?;
Ok(Self {
max_items,
node,
subid,
items,
})
}
}
impl IntoElement for Items {
fn builder(&self) -> peanuts::element::ElementBuilder {
Element::builder("items", Some(XMLNS))
.push_attribute_opt("max_items", self.max_items)
.push_attribute("node", self.node.clone())
.push_attribute_opt("subid", self.subid.clone())
.push_children(self.items.clone())
}
}
#[derive(Clone, Debug)]
pub struct Item {
id: Option<String>,
publisher: Option<String>,
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 {
Unknown,
}
impl FromElement for Content {
fn from_element(_element: Element) -> peanuts::element::DeserializeResult<Self> {
// TODO: types
return Ok(Self::Unknown);
}
}
impl IntoElement for Content {
fn builder(&self) -> peanuts::element::ElementBuilder {
panic!("unknown content cannot be serialized")
}
}
#[derive(Clone, Debug)]
pub struct Options {
jid: JID,
node: Option<String>,
subid: Option<String>,
options: Option<X>,
}
impl FromElement for Options {
fn from_element(mut element: Element) -> peanuts::element::DeserializeResult<Self> {
element.check_name("options")?;
element.check_namespace(XMLNS)?;
let jid = element.attribute("jid")?;
let node = element.attribute_opt("node")?;
let subid = element.attribute_opt("subid")?;
let options = element.pop_child_opt()?;
Ok(Self {
jid,
node,
subid,
options,
})
}
}
impl IntoElement for Options {
fn builder(&self) -> peanuts::element::ElementBuilder {
Element::builder("options", Some(XMLNS))
.push_attribute("jid", self.jid.clone())
.push_attribute_opt("node", self.node.clone())
.push_attribute_opt("subid", self.subid.clone())
.push_child_opt(self.options.clone())
}
}
#[derive(Clone, Debug)]
pub struct Publish {
node: String,
items: Vec<Item>,
}
impl FromElement for Publish {
fn from_element(mut element: Element) -> peanuts::element::DeserializeResult<Self> {
element.check_name("publish")?;
element.check_namespace(XMLNS)?;
let node = element.attribute("node")?;
let items = element.pop_children()?;
Ok(Self { node, items })
}
}
impl IntoElement for Publish {
fn builder(&self) -> peanuts::element::ElementBuilder {
Element::builder("publish", Some(XMLNS))
.push_attribute("node", self.node.clone())
.push_children(self.items.clone())
}
}
#[derive(Clone, Debug)]
pub struct PublishOptions(pub Option<X>);
impl FromElement for PublishOptions {
fn from_element(mut element: Element) -> peanuts::element::DeserializeResult<Self> {
element.check_name("publish-options")?;
element.check_namespace(XMLNS)?;
Ok(Self(element.pop_child_opt()?))
}
}
impl IntoElement for PublishOptions {
fn builder(&self) -> peanuts::element::ElementBuilder {
Element::builder("publish-options", Some(XMLNS)).push_child_opt(self.0.clone())
}
}
#[derive(Clone, Debug)]
pub struct Retract {
node: String,
notify: Option<bool>,
/// minOccurs = 1
items: Vec<Item>,
}
impl FromElement for Retract {
fn from_element(mut element: Element) -> peanuts::element::DeserializeResult<Self> {
element.check_name("retract")?;
element.check_namespace(XMLNS)?;
let node = element.attribute("node")?;
let notify = element.attribute_opt("notify")?;
let items = element.pop_children()?;
Ok(Self {
node,
notify,
items,
})
}
}
impl IntoElement for Retract {
fn builder(&self) -> peanuts::element::ElementBuilder {
Element::builder("retract", Some(XMLNS))
.push_attribute("node", self.node.clone())
.push_attribute_opt("notify", self.notify)
.push_children(self.items.clone())
}
}
#[derive(Clone, Debug)]
pub struct SubscribeOptions {
// <required/> element
required: bool,
}
impl FromElement for SubscribeOptions {
fn from_element(mut element: Element) -> peanuts::element::DeserializeResult<Self> {
element.check_name("subscribe-options")?;
element.check_namespace(XMLNS)?;
let required;
if let Some(_) = element.pop_child_opt::<Required>()? {
required = true;
} else {
required = false;
}
Ok(Self { required })
}
}
impl IntoElement for SubscribeOptions {
fn builder(&self) -> peanuts::element::ElementBuilder {
let builder = Element::builder("subscribe-options", Some(XMLNS));
if self.required {
builder.push_child(Required)
} else {
builder
}
}
}
pub struct Required;
impl FromElement for Required {
fn from_element(element: Element) -> peanuts::element::DeserializeResult<Self> {
element.check_name("required")?;
element.check_namespace(XMLNS)?;
Ok(Self)
}
}
impl IntoElement for Required {
fn builder(&self) -> peanuts::element::ElementBuilder {
Element::builder("required", Some(XMLNS))
}
}
#[derive(Clone, Debug)]
pub struct Subscribe {
jid: JID,
node: Option<String>,
}
impl FromElement for Subscribe {
fn from_element(mut element: Element) -> peanuts::element::DeserializeResult<Self> {
element.check_name("subscribe")?;
element.check_namespace(XMLNS)?;
let jid = element.attribute("jid")?;
let node = element.attribute_opt("node")?;
Ok(Self { jid, node })
}
}
impl IntoElement for Subscribe {
fn builder(&self) -> peanuts::element::ElementBuilder {
Element::builder("subscribe", Some(XMLNS))
.push_attribute("jid", self.jid.clone())
.push_attribute_opt("node", self.node.clone())
}
}
#[derive(Clone, Debug)]
pub struct Subscriptions {
node: Option<String>,
subscriptions: Vec<Subscription>,
}
impl FromElement for Subscriptions {
fn from_element(mut element: Element) -> peanuts::element::DeserializeResult<Self> {
element.check_name("subscriptions")?;
element.check_namespace(XMLNS)?;
let node = element.attribute_opt("node")?;
let subscriptions = element.pop_children()?;
Ok(Self {
node,
subscriptions,
})
}
}
impl IntoElement for Subscriptions {
fn builder(&self) -> peanuts::element::ElementBuilder {
Element::builder("subscriptions", Some(XMLNS))
.push_attribute_opt("node", self.node.clone())
.push_children(self.subscriptions.clone())
}
}
#[derive(Clone, Debug)]
pub struct Subscription {
jid: JID,
node: Option<String>,
subid: Option<String>,
subscription: Option<SubscriptionState>,
subscribe_options: Option<SubscribeOptions>,
}
impl FromElement for Subscription {
fn from_element(mut element: Element) -> peanuts::element::DeserializeResult<Self> {
element.check_name("subscription")?;
element.check_namespace(XMLNS)?;
let jid = element.attribute("jid")?;
let node = element.attribute_opt("node")?;
let subid = element.attribute_opt("subid")?;
let subscription = element.attribute_opt("subscription")?;
let subscribe_options = element.pop_child_opt()?;
Ok(Self {
jid,
node,
subid,
subscription,
subscribe_options,
})
}
}
impl IntoElement for Subscription {
fn builder(&self) -> peanuts::element::ElementBuilder {
Element::builder("subscription", Some(XMLNS))
.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())
.push_child_opt(self.subscribe_options.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_string()
}
}
#[derive(Clone, Debug)]
pub struct Unsubscribe {
jid: JID,
node: Option<String>,
subid: Option<String>,
}
impl FromElement for Unsubscribe {
fn from_element(mut element: Element) -> peanuts::element::DeserializeResult<Self> {
element.check_name("unsubscribe")?;
element.check_namespace(XMLNS)?;
let jid = element.attribute("jid")?;
let node = element.attribute_opt("node")?;
let subid = element.attribute_opt("subid")?;
Ok(Self { jid, node, subid })
}
}
impl IntoElement for Unsubscribe {
fn builder(&self) -> peanuts::element::ElementBuilder {
Element::builder("unsubscribe", Some(XMLNS))
.push_attribute("jid", self.jid.clone())
.push_attribute_opt("node", self.node.clone())
.push_attribute_opt("subid", self.subid.clone())
}
}
// TODO: enforce 'empty'