luz/src/stanza/stream.rs

161 lines
4.7 KiB
Rust
Raw Normal View History

2024-11-23 22:39:44 +00:00
use std::collections::{HashMap, HashSet};
2024-11-23 22:39:44 +00:00
use peanuts::element::{Content, FromElement, IntoElement, NamespaceDeclaration};
use peanuts::XML_NS;
use peanuts::{element::Name, Element};
2024-11-23 22:39:44 +00:00
use crate::{Error, JID};
pub const XMLNS: &str = "http://etherx.jabber.org/streams";
pub const XMLNS_CLIENT: &str = "jabber:client";
// MUST be qualified by stream namespace
2024-11-23 22:39:44 +00:00
// #[derive(XmlSerialize, XmlDeserialize)]
// #[peanuts(xmlns = XMLNS)]
pub struct Stream {
pub from: Option<JID>,
to: Option<JID>,
id: Option<String>,
version: Option<String>,
// TODO: lang enum
2024-11-23 22:39:44 +00:00
lang: Option<String>,
// #[peanuts(content)]
// content: Message,
}
impl FromElement for Stream {
fn from_element(element: Element) -> peanuts::Result<Self> {
let Name {
namespace,
local_name,
} = element.name;
if namespace.as_deref() == Some(XMLNS) && &local_name == "stream" {
let (mut from, mut to, mut id, mut version, mut lang) = (None, None, None, None, None);
for (name, value) in element.attributes {
match (name.namespace.as_deref(), name.local_name.as_str()) {
(None, "from") => from = Some(value.try_into()?),
(None, "to") => to = Some(value.try_into()?),
(None, "id") => id = Some(value),
(None, "version") => version = Some(value),
(Some(XML_NS), "lang") => lang = Some(value),
_ => return Err(peanuts::Error::UnexpectedAttribute(name)),
}
}
return Ok(Stream {
from,
to,
id,
version,
lang,
});
} else {
return Err(peanuts::Error::IncorrectName(Name {
namespace,
local_name,
}));
}
}
}
impl IntoElement for Stream {
fn into_element(&self) -> Element {
let mut namespace_declarations = HashSet::new();
namespace_declarations.insert(NamespaceDeclaration {
prefix: Some("stream".to_string()),
namespace: XMLNS.to_string(),
});
namespace_declarations.insert(NamespaceDeclaration {
prefix: None,
// TODO: don't default to client
namespace: XMLNS_CLIENT.to_string(),
});
let mut attributes = HashMap::new();
self.from.as_ref().map(|from| {
attributes.insert(
Name {
namespace: None,
local_name: "from".to_string(),
},
from.to_string(),
);
});
self.to.as_ref().map(|to| {
attributes.insert(
Name {
namespace: None,
local_name: "to".to_string(),
},
to.to_string(),
);
});
self.id.as_ref().map(|id| {
attributes.insert(
Name {
namespace: None,
local_name: "version".to_string(),
},
id.clone(),
);
});
self.version.as_ref().map(|version| {
attributes.insert(
Name {
namespace: None,
local_name: "version".to_string(),
},
version.clone(),
);
});
self.lang.as_ref().map(|lang| {
attributes.insert(
Name {
namespace: Some(XML_NS.to_string()),
local_name: "lang".to_string(),
},
lang.to_string(),
);
});
Element {
name: Name {
namespace: Some(XMLNS.to_string()),
local_name: "stream".to_string(),
},
namespace_declarations,
attributes,
content: Vec::new(),
}
}
2023-06-19 19:23:54 +01:00
}
2024-11-23 22:39:44 +00:00
impl<'s> Stream {
pub fn new(
2024-11-23 22:39:44 +00:00
from: Option<JID>,
to: Option<JID>,
id: Option<String>,
version: Option<String>,
lang: Option<String>,
) -> Self {
2023-07-11 21:28:42 +01:00
Self {
from,
to,
2023-07-11 21:28:42 +01:00
id,
version,
2023-07-11 21:28:42 +01:00
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.
2024-11-23 22:39:44 +00:00
pub fn new_client(from: Option<JID>, to: JID, id: Option<String>, lang: String) -> Self {
Self {
from,
to: Some(to),
id,
2024-11-23 22:39:44 +00:00
version: Some("1.0".to_string()),
lang: Some(lang),
2023-07-11 21:28:42 +01:00
}
}
}