use nanoid::nanoid;
use quick_xml::{
events::{BytesStart, Event},
name::QName,
Reader, Writer,
};
use crate::{JabberClient, JabberError, JID};
use super::{Element, IntoElement};
use crate::Result;
#[derive(Debug)]
pub struct IQ {
to: Option<JID>,
from: Option<JID>,
id: String,
r#type: IQType,
lang: Option<String>,
child: Element<'static>,
}
#[derive(Debug)]
enum IQType {
Get,
Set,
Result,
Error,
}
impl IQ {
pub async fn set<'j, R: IntoElement<'static>>(
client: &mut JabberClient<'j>,
to: Option<JID>,
from: Option<JID>,
element: R,
) -> Result<Element<'static>> {
let id = nanoid!();
let iq = IQ {
to,
from,
id: id.clone(),
r#type: IQType::Set,
lang: None,
child: Element::from(element),
};
println!("{:?}", iq);
let iq = Element::from(iq);
println!("{:?}", iq);
iq.write(&mut client.writer).await?;
let result = Element::read(&mut client.reader).await?;
let iq = IQ::try_from(result)?;
if iq.id == id {
return Ok(iq.child);
}
Err(JabberError::IDMismatch)
}
}
impl<'e> IntoElement<'e> for IQ {
fn event(&self) -> quick_xml::events::Event<'e> {
let mut start = BytesStart::new("iq");
if let Some(to) = &self.to {
start.push_attribute(("to", to.to_string().as_str()));
}
if let Some(from) = &self.from {
start.push_attribute(("from", from.to_string().as_str()));
}
start.push_attribute(("id", self.id.as_str()));
match self.r#type {
IQType::Get => start.push_attribute(("type", "get")),
IQType::Set => start.push_attribute(("type", "set")),
IQType::Result => start.push_attribute(("type", "result")),
IQType::Error => start.push_attribute(("type", "error")),
}
if let Some(lang) = &self.lang {
start.push_attribute(("from", lang.to_string().as_str()));
}
quick_xml::events::Event::Start(start)
}
fn children(&self) -> Option<Vec<Element<'e>>> {
Some(vec![self.child.clone()])
}
}
impl TryFrom<Element<'static>> for IQ {
type Error = JabberError;
fn try_from(element: Element<'static>) -> std::result::Result<Self, Self::Error> {
if let Event::Start(start) = &element.event {
if start.name() == QName(b"iq") {
let mut to: Option<JID> = None;
let mut from: Option<JID> = None;
let mut id = None;
let mut r#type = None;
let mut lang = None;
start
.attributes()
.into_iter()
.try_for_each(|attribute| -> Result<()> {
if let Ok(attribute) = attribute {
let buf: Vec<u8> = Vec::new();
let reader = Reader::from_reader(buf);
match attribute.key {
QName(b"to") => {
to = Some(
attribute
.decode_and_unescape_value(&reader)
.or(Err(JabberError::Utf8Decode))?
.into_owned()
.try_into()?,
)
}
QName(b"from") => {
from = Some(
attribute
.decode_and_unescape_value(&reader)
.or(Err(JabberError::Utf8Decode))?
.into_owned()
.try_into()?,
)
}
QName(b"id") => {
id = Some(
attribute
.decode_and_unescape_value(&reader)
.or(Err(JabberError::Utf8Decode))?
.into_owned(),
)
}
QName(b"type") => {
let value = attribute
.decode_and_unescape_value(&reader)
.or(Err(JabberError::Utf8Decode))?;
match value.as_ref() {
"get" => r#type = Some(IQType::Get),
"set" => r#type = Some(IQType::Set),
"result" => r#type = Some(IQType::Result),
"error" => r#type = Some(IQType::Error),
_ => return Err(JabberError::ParseError),
}
}
QName(b"lang") => {
lang = Some(
attribute
.decode_and_unescape_value(&reader)
.or(Err(JabberError::Utf8Decode))?
.into_owned(),
)
}
_ => return Err(JabberError::UnknownAttribute),
}
}
Ok(())
})?;
let iq = IQ {
to,
from,
id: id.ok_or(JabberError::NoID)?,
r#type: r#type.ok_or(JabberError::NoType)?,
lang,
child: element.child()?.to_owned(),
};
return Ok(iq);
}
}
Err(JabberError::ParseError)
}
}