use std::collections::{HashMap, HashSet};
use peanuts::element::{Content, ElementBuilder, FromElement, IntoElement, NamespaceDeclaration};
use peanuts::XML_NS;
use peanuts::{element::Name, Element};
use crate::{Error, JID};
use super::starttls::{self, StartTls};
pub const XMLNS: &str = "http://etherx.jabber.org/streams";
pub const XMLNS_CLIENT: &str = "jabber:client";
// MUST be qualified by stream namespace
// #[derive(XmlSerialize, XmlDeserialize)]
// #[peanuts(xmlns = XMLNS)]
#[derive(Debug)]
pub struct Stream {
pub from: Option<JID>,
to: Option<JID>,
id: Option<String>,
version: Option<String>,
// TODO: lang enum
lang: Option<String>,
// #[peanuts(content)]
// content: Message,
}
impl FromElement for Stream {
fn from_element(mut element: Element) -> std::result::Result<Self, peanuts::DeserializeError> {
element.check_namespace(XMLNS)?;
element.check_name("stream")?;
let from = element.attribute_opt("from")?;
let to = element.attribute_opt("to")?;
let id = element.attribute_opt("id")?;
let version = element.attribute_opt("version")?;
let lang = element.attribute_opt_namespaced("lang", peanuts::XML_NS)?;
Ok(Stream {
from,
to,
id,
version,
lang,
})
}
}
impl IntoElement for Stream {
fn builder(&self) -> ElementBuilder {
Element::builder("stream", Some(XMLNS.to_string()))
.push_namespace_declaration_override(Some("stream"), XMLNS)
.push_namespace_declaration_override(None::<&str>, XMLNS_CLIENT)
.push_attribute_opt("to", self.to.clone())
.push_attribute_opt("from", self.from.clone())
.push_attribute_opt("id", self.id.clone())
.push_attribute_opt("version", self.version.clone())
.push_attribute_opt_namespaced(peanuts::XML_NS, "to", self.lang.clone())
}
}
impl<'s> Stream {
pub fn new(
from: Option<JID>,
to: Option<JID>,
id: Option<String>,
version: Option<String>,
lang: Option<String>,
) -> Self {
Self {
from,
to,
id,
version,
lang,
}
}
/// For initial stream headers, the initiating entity SHOULD include the 'xml:lang' attribute.
/// For privacy, it is better to not set `from` when sending a client stanza over an unencrypted connection.
pub fn new_client(from: Option<JID>, to: JID, id: Option<String>, lang: String) -> Self {
Self {
from,
to: Some(to),
id,
version: Some("1.0".to_string()),
lang: Some(lang),
}
}
}
#[derive(Debug)]
pub struct Features {
features: Vec<Feature>,
}
impl IntoElement for Features {
fn builder(&self) -> ElementBuilder {
Element::builder("features", Some(XMLNS)).push_children(self.features.clone())
// let mut content = Vec::new();
// for feature in &self.features {
// match feature {
// Feature::StartTls(start_tls) => {
// content.push(Content::Element(start_tls.into_element()))
// }
// Feature::Sasl => {}
// Feature::Bind => {}
// Feature::Unknown => {}
// }
// }
// Element {
// name: Name {
// namespace: Some(XMLNS.to_string()),
// local_name: "features".to_string(),
// },
// namespace_declaration_overrides: HashSet::new(),
// attributes: HashMap::new(),
// content,
// }
}
}
impl FromElement for Features {
fn from_element(
mut element: Element,
) -> std::result::Result<Features, peanuts::DeserializeError> {
element.check_namespace(XMLNS)?;
element.check_name("features")?;
let features = element.children()?;
Ok(Features { features })
}
}
#[derive(Debug, Clone)]
pub enum Feature {
StartTls(StartTls),
Sasl,
Bind,
Unknown,
}
impl IntoElement for Feature {
fn builder(&self) -> ElementBuilder {
match self {
Feature::StartTls(start_tls) => start_tls.builder(),
Feature::Sasl => todo!(),
Feature::Bind => todo!(),
Feature::Unknown => todo!(),
}
}
}
impl FromElement for Feature {
fn from_element(element: Element) -> peanuts::element::DeserializeResult<Self> {
match element.identify() {
(Some(starttls::XMLNS), "starttls") => {
Ok(Feature::StartTls(StartTls::from_element(element)?))
}
_ => Ok(Feature::Unknown),
}
}
}