aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLibravatar cel 🌸 <cel@bunny.garden>2025-04-30 14:41:40 +0100
committerLibravatar cel 🌸 <cel@bunny.garden>2025-04-30 16:15:15 +0100
commit74e5ab6702bbbe292fcb38ccb97036ce5b376956 (patch)
tree54416ab9cb9794a46f42a56a38e0a05abf354ae4
parent1f1f267df88638229f09b875e72fd118c08a7ae7 (diff)
downloadluz-74e5ab6702bbbe292fcb38ccb97036ce5b376956.tar.gz
luz-74e5ab6702bbbe292fcb38ccb97036ce5b376956.tar.bz2
luz-74e5ab6702bbbe292fcb38ccb97036ce5b376956.zip
feat(stanza): XRD
-rw-r--r--stanza/Cargo.toml1
-rw-r--r--stanza/src/lib.rs2
-rw-r--r--stanza/src/xep_0156.rs233
3 files changed, 236 insertions, 0 deletions
diff --git a/stanza/Cargo.toml b/stanza/Cargo.toml
index 2832ed6..54577ff 100644
--- a/stanza/Cargo.toml
+++ b/stanza/Cargo.toml
@@ -20,6 +20,7 @@ xep_0084 = []
xep_0115 = []
xep_0128 = ["xep_0004"]
xep_0131 = []
+xep_0156 = ["dep:chrono"]
xep_0172 = []
xep_0199 = []
xep_0203 = ["dep:chrono"]
diff --git a/stanza/src/lib.rs b/stanza/src/lib.rs
index 5e30cd0..8f8d430 100644
--- a/stanza/src/lib.rs
+++ b/stanza/src/lib.rs
@@ -25,6 +25,8 @@ pub mod xep_0084;
pub mod xep_0115;
#[cfg(feature = "xep_0131")]
pub mod xep_0131;
+#[cfg(feature = "xep_0156")]
+pub mod xep_0156;
#[cfg(feature = "xep_0172")]
pub mod xep_0172;
#[cfg(feature = "xep_0199")]
diff --git a/stanza/src/xep_0156.rs b/stanza/src/xep_0156.rs
new file mode 100644
index 0000000..ad22f7e
--- /dev/null
+++ b/stanza/src/xep_0156.rs
@@ -0,0 +1,233 @@
+use chrono::{DateTime, Utc};
+use peanuts::{
+ element::{FromElement, IntoElement},
+ Element, XML_NS,
+};
+
+pub const XMLNS: &str = "http://docs.oasis-open.org/ns/xri/xrd-1.0";
+pub const SIGNATURE_XMLNS: &str = "http://www.w3.org/2000/09/xmldsig#";
+
+#[derive(Debug, Clone)]
+pub struct XRD {
+ pub id: Option<String>,
+ pub expires: Option<Expires>,
+ pub subject: Option<Subject>,
+ pub aliases: Vec<Alias>,
+ pub properties: Vec<Property>,
+ pub links: Vec<Link>,
+ pub signature: Vec<Signature>,
+}
+
+impl FromElement for XRD {
+ fn from_element(mut element: peanuts::Element) -> peanuts::element::DeserializeResult<Self> {
+ element.check_name("XRD")?;
+ element.check_namespace(XMLNS)?;
+
+ let id = element.attribute_opt_namespaced("id", XML_NS)?;
+
+ let expires = element.child_opt()?;
+ let subject = element.child_opt()?;
+ let aliases = element.children()?;
+ let properties = element.children()?;
+ let links = element.children()?;
+ let signature = element.children()?;
+
+ Ok(Self {
+ id,
+ expires,
+ subject,
+ aliases,
+ properties,
+ links,
+ signature,
+ })
+ }
+}
+
+impl IntoElement for XRD {
+ fn builder(&self) -> peanuts::element::ElementBuilder {
+ Element::builder("XRD", Some(XMLNS))
+ .push_attribute_opt_namespaced(XML_NS, "id", self.id.clone())
+ .push_child_opt(self.expires.clone())
+ .push_child_opt(self.subject.clone())
+ .push_children(self.aliases.clone())
+ .push_children(self.properties.clone())
+ .push_children(self.links.clone())
+ .push_children(self.signature.clone())
+ }
+}
+
+#[derive(Debug, Clone)]
+pub struct Expires(pub DateTime<Utc>);
+
+impl FromElement for Expires {
+ fn from_element(mut element: Element) -> peanuts::element::DeserializeResult<Self> {
+ element.check_name("Expires")?;
+ element.check_namespace(XMLNS)?;
+
+ Ok(Self(element.pop_value()?))
+ }
+}
+
+impl IntoElement for Expires {
+ fn builder(&self) -> peanuts::element::ElementBuilder {
+ Element::builder("Expires", Some(XMLNS))
+ .push_text(self.0.format("%C%y-%m-%dT%H:%M:%S%.3f%:z"))
+ }
+}
+
+// anyURI
+#[derive(Debug, Clone)]
+pub struct Subject(pub String);
+
+impl FromElement for Subject {
+ fn from_element(mut element: Element) -> peanuts::element::DeserializeResult<Self> {
+ element.check_name("Subject")?;
+ element.check_namespace(XMLNS)?;
+
+ Ok(Self(element.pop_value()?))
+ }
+}
+
+impl IntoElement for Subject {
+ fn builder(&self) -> peanuts::element::ElementBuilder {
+ Element::builder("Subject", Some(XMLNS)).push_text(self.0.clone())
+ }
+}
+
+// anyURI
+#[derive(Debug, Clone)]
+pub struct Alias(pub String);
+
+impl FromElement for Alias {
+ fn from_element(mut element: Element) -> peanuts::element::DeserializeResult<Self> {
+ element.check_name("Alias")?;
+ element.check_namespace(XMLNS)?;
+
+ Ok(Self(element.pop_value()?))
+ }
+}
+
+impl IntoElement for Alias {
+ fn builder(&self) -> peanuts::element::ElementBuilder {
+ Element::builder("Alias", Some(XMLNS)).push_text(self.0.clone())
+ }
+}
+
+#[derive(Debug, Clone)]
+pub struct Property {
+ pub r#type: String,
+ pub property: Option<String>,
+}
+
+impl FromElement for Property {
+ fn from_element(mut element: Element) -> peanuts::element::DeserializeResult<Self> {
+ element.check_name("Property")?;
+ element.check_name(XMLNS)?;
+
+ let r#type = element.attribute("type")?;
+
+ let property = element.pop_value_opt()?;
+
+ Ok(Self { r#type, property })
+ }
+}
+
+impl IntoElement for Property {
+ fn builder(&self) -> peanuts::element::ElementBuilder {
+ Element::builder("Property", Some(XMLNS))
+ .push_attribute("type", self.r#type.clone())
+ .push_text_opt(self.property.clone())
+ }
+}
+
+#[derive(Debug, Clone)]
+pub struct Link {
+ pub rel: Option<String>,
+ pub r#type: Option<String>,
+ pub href: Option<String>,
+ pub template: Option<String>,
+ pub titles: Vec<Title>,
+ pub properties: Vec<Property>,
+}
+
+impl FromElement for Link {
+ fn from_element(mut element: Element) -> peanuts::element::DeserializeResult<Self> {
+ element.check_name("Link")?;
+ element.check_namespace(XMLNS)?;
+
+ let rel = element.attribute_opt("rel")?;
+ let r#type = element.attribute_opt("type")?;
+ let href = element.attribute_opt("href")?;
+ let template = element.attribute_opt("template")?;
+
+ let titles = element.children()?;
+ let properties = element.children()?;
+
+ Ok(Self {
+ rel,
+ r#type,
+ href,
+ template,
+ titles,
+ properties,
+ })
+ }
+}
+
+impl IntoElement for Link {
+ fn builder(&self) -> peanuts::element::ElementBuilder {
+ Element::builder("Link", Some(XMLNS))
+ .push_attribute_opt("rel", self.rel.clone())
+ .push_attribute_opt("type", self.r#type.clone())
+ .push_attribute_opt("href", self.href.clone())
+ .push_attribute_opt("template", self.template.clone())
+ .push_children(self.titles.clone())
+ .push_children(self.properties.clone())
+ }
+}
+
+#[derive(Debug, Clone)]
+pub struct Title {
+ pub lang: Option<String>,
+ pub title: String,
+}
+
+impl FromElement for Title {
+ fn from_element(mut element: Element) -> peanuts::element::DeserializeResult<Self> {
+ element.check_name("Title")?;
+ element.check_namespace(XMLNS)?;
+
+ let lang = element.attribute_opt_namespaced("lang", XML_NS)?;
+
+ let title = element.pop_value_opt()?.unwrap_or_default();
+
+ Ok(Self { lang, title })
+ }
+}
+
+impl IntoElement for Title {
+ fn builder(&self) -> peanuts::element::ElementBuilder {
+ Element::builder("Title", Some(XMLNS))
+ .push_attribute_opt_namespaced(XML_NS, "lang", self.lang.clone())
+ .push_text(self.title.clone())
+ }
+}
+
+#[derive(Debug, Clone, Copy)]
+pub struct Signature;
+
+impl FromElement for Signature {
+ fn from_element(element: Element) -> peanuts::element::DeserializeResult<Self> {
+ element.check_name("Signature")?;
+ element.check_namespace(SIGNATURE_XMLNS)?;
+
+ Ok(Self)
+ }
+}
+
+impl IntoElement for Signature {
+ fn builder(&self) -> peanuts::element::ElementBuilder {
+ Element::builder("Signature", Some(SIGNATURE_XMLNS))
+ }
+}